mirror of
https://github.com/gizak/termui.git
synced 2025-04-26 13:48:54 +08:00
added kitty drawer
This commit is contained in:
parent
c3b389694f
commit
3de3e057f6
@ -13,3 +13,7 @@ ReGis
|
||||
reimplement 23imgdisplay:
|
||||
|
||||
check for X11 and not Alacritty (https://github.com/jwilm/alacritty/issues/1021)
|
||||
|
||||
---
|
||||
|
||||
if i remember correctly xterm has a size limit for sixel images - pixel width???
|
||||
|
@ -9,7 +9,8 @@ import (
|
||||
"github.com/gizak/termui/v3/widgets"
|
||||
)
|
||||
|
||||
// the file name should start with aaa - because the init() functions are executed in alphabetic file orde
|
||||
// the file name should appear at the top when alphabetically sorted (start with "aaa")
|
||||
// because the init() functions are executed in alphabetic file order
|
||||
func init() {
|
||||
scanTerminal()
|
||||
var drawFallback func(*widgets.Image, *Buffer) (error)
|
||||
|
@ -22,7 +22,7 @@ func init() {
|
||||
widgets.Drawer{
|
||||
Remote: true,
|
||||
IsEscapeString: true,
|
||||
Available: func() bool {return isIterm2},
|
||||
Available: func() bool {return isIterm2 || isMacTerm},
|
||||
Draw: drawITerm2,
|
||||
},
|
||||
)
|
||||
@ -38,9 +38,8 @@ func drawITerm2(wdgt *widgets.Image, buf *Buffer) (err error) {
|
||||
|
||||
imageDimensions := wdgt.GetVisibleArea()
|
||||
|
||||
// iTerm2
|
||||
// https://www.iterm2.com/documentation-images.html
|
||||
if isIterm2 {
|
||||
if isIterm2 || isMacTerm {
|
||||
buf := new(bytes.Buffer)
|
||||
if err = png.Encode(buf, img); err != nil {
|
||||
goto skipIterm2
|
103
widgets/exp/drawer_kitty.go
Normal file
103
widgets/exp/drawer_kitty.go
Normal file
@ -0,0 +1,103 @@
|
||||
// <Copyright> 2019 Simon Robin Lehn. All rights reserved.
|
||||
// Use of this source code is governed by a MIT license that can
|
||||
// be found in the LICENSE file.
|
||||
|
||||
package exp
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"errors"
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"image/png"
|
||||
|
||||
. "github.com/gizak/termui/v3"
|
||||
"github.com/gizak/termui/v3/widgets"
|
||||
)
|
||||
|
||||
const (
|
||||
kittyLimit = 4096
|
||||
)
|
||||
|
||||
var (
|
||||
// for numbering of ids
|
||||
kittyImageCount int
|
||||
)
|
||||
|
||||
func init() {
|
||||
widgets.RegisterDrawer(
|
||||
"kitty",
|
||||
widgets.Drawer{
|
||||
Remote: true,
|
||||
IsEscapeString: true,
|
||||
Available: func() bool {return isKitty},
|
||||
Draw: drawKitty,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
func drawKitty(wdgt *widgets.Image, buf *Buffer) (err error) {
|
||||
if !isKitty {
|
||||
return errors.New("method not supported for this terminal type")
|
||||
}
|
||||
|
||||
wdgt.Block.Draw(buf)
|
||||
|
||||
// TODO: FIX THIS
|
||||
termWidth, termHeight := getTermSizeInChars(true)
|
||||
var _ = termWidth
|
||||
/*
|
||||
if termWidth == 0 || termHeight == 0 {
|
||||
return errors.New("could not query terminal dimensions")
|
||||
}
|
||||
*/
|
||||
|
||||
img, changed, err := resizeImage(wdgt, buf)
|
||||
if !changed || err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var imgHeight int
|
||||
imageDimensions := wdgt.GetVisibleArea()
|
||||
if wdgt.Inner.Max.Y < termHeight {
|
||||
imgHeight = wdgt.Inner.Dy()
|
||||
} else {
|
||||
imgHeight = termHeight-1
|
||||
}
|
||||
imgHeight = wdgt.Inner.Dy() // TODO: REMOVE THIS CRUTCH
|
||||
|
||||
// https://sw.kovidgoyal.net/kitty/graphics-protocol.html#remote-client
|
||||
// https://sw.kovidgoyal.net/kitty/graphics-protocol.html#png-data
|
||||
// https://sw.kovidgoyal.net/kitty/graphics-protocol.html#controlling-displayed-image-layout
|
||||
// \033_Gt=d,f=100,X=,Y=,c=,r=,m=1;...\a
|
||||
bytBuf := new(bytes.Buffer)
|
||||
if err = png.Encode(bytBuf, img); err != nil {
|
||||
return err
|
||||
}
|
||||
imgBase64 := base64.StdEncoding.EncodeToString(bytBuf.Bytes())
|
||||
lenImgB64 := len([]byte(imgBase64))
|
||||
// a=T action
|
||||
// t=d payload is (base64 encoded) data itself not a file location
|
||||
// f=100 format: 100 = PNG payload
|
||||
// o=z data compression
|
||||
// X=...,Y=,,, Upper left image corner in cell coordinates (starting with 1, 1)
|
||||
// c=...,r=... image size in cell columns and rows
|
||||
// w=...,h=... width & height (in pixels) of the image area to display // TODO: Use this to let Kitty handle cropping!
|
||||
// z=0 z-index vertical stacking order of the image
|
||||
// m=[01] 0 last escape code chunk - 1 for all except the last
|
||||
var kittyString string
|
||||
var zIndex = 2 // draw over text
|
||||
settings := fmt.Sprintf("a=T,t=d,f=100,X=%d,Y=%d,c=%d,r=%d,z=%d,", imageDimensions.Min.X, imageDimensions.Min.Y, wdgt.Inner.Dx(), imgHeight, zIndex)
|
||||
i := 0
|
||||
for ; i < (lenImgB64-1)/kittyLimit; i++ {
|
||||
kittyString += wrap(fmt.Sprintf("\033_G%sm=1;%s\033\\", settings, string([]byte(imgBase64)[i*kittyLimit:(i+1)*kittyLimit])))
|
||||
settings = ""
|
||||
}
|
||||
kittyString += wrap(fmt.Sprintf("\033_G%sm=0;%s\033\\", settings, string([]byte(imgBase64)[i*kittyLimit:lenImgB64])))
|
||||
|
||||
wdgt.Block.ANSIString = fmt.Sprintf("\033[%d;%dH%s", imageDimensions.Min.Y, imageDimensions.Min.X, kittyString)
|
||||
return nil
|
||||
}
|
||||
|
||||
// TODO:
|
||||
//
|
@ -48,8 +48,8 @@ func drawUrxvt(wdgt *widgets.Image, buf *Buffer) (err error) {
|
||||
return errors.New("could not query terminal dimensions")
|
||||
}
|
||||
|
||||
widthPercentage = 100*wdgt.Inner.Dx()/termWidth
|
||||
heightPercentage = 100*wdgt.Inner.Dy()/termHeight
|
||||
widthPercentage = (100*wdgt.Inner.Dx())/termWidth
|
||||
heightPercentage = (100*wdgt.Inner.Dy())/termHeight
|
||||
maxX := wdgt.Inner.Max.X
|
||||
maxY := wdgt.Inner.Max.Y
|
||||
if termWidth < maxX {
|
||||
@ -79,12 +79,12 @@ func drawUrxvt(wdgt *widgets.Image, buf *Buffer) (err error) {
|
||||
|
||||
// defer os.RemoveAll(dir) // clean up
|
||||
|
||||
filename := filepath.Join(tempdir, fmt.Sprintf("%x", md5.Sum(bytBuf.Bytes())) + ".png")
|
||||
filename := filepath.Join(tempdir, fmt.Sprintf("urxvt-%x", md5.Sum(bytBuf.Bytes())) + ".png")
|
||||
if err := ioutil.WriteFile(filename, bytBuf.Bytes(), 0644); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// "\033]20;%s;%dx%d+%d+%d:op=keep-aspect\a" op=keep-aspect prevents stretching
|
||||
wdgt.Block.ANSIString = wrap(fmt.Sprintf("\033]20;%s;%dx%d+%d+%d\a", filename, widthPercentage, heightPercentage, CenterPosXPercentage, CenterPosYPercentage))
|
||||
// "op=keep-aspect" maintains the image aspect ratio when scaling
|
||||
wdgt.Block.ANSIString = wrap(fmt.Sprintf("\033]20;%s;%dx%d+%d+%d:op=keep-aspect\a", filename, widthPercentage, heightPercentage, CenterPosXPercentage, CenterPosYPercentage))
|
||||
return nil
|
||||
}
|
@ -66,6 +66,11 @@ if isTmux {charBoxWidthInPixels, charBoxHeightInPixels = 10, 19} // mlterm set
|
||||
if isXterm {
|
||||
scrollExtraRows = 2
|
||||
}
|
||||
/*
|
||||
if isKitty {
|
||||
scrollExtraRows = 1
|
||||
}
|
||||
*/
|
||||
// subtract 1 pixel for small deviations from char box size (float64)
|
||||
imgCroppedHeight = int(float64(int(termHeightInRows) - wdgt.Inner.Min.Y - scrollExtraRows) * charBoxHeightInPixels) - 1
|
||||
needsCropY = true
|
||||
|
@ -18,7 +18,7 @@ func scanTerminal() {
|
||||
if os.Getenv("TERM_PROGRAM") == "iTerm.app" { isIterm2 = true } else { isIterm2 = false } // https://superuser.com/a/683971
|
||||
if os.Getenv("TERM_PROGRAM") == "MacTerm" { isMacTerm = true } else { isMacTerm = false } // https://github.com/kmgrant/macterm/issues/3#issuecomment-458387953
|
||||
if strings.HasPrefix(os.Getenv("TERM"), "rxvt-unicode") { isUrxvt = true } else { isUrxvt = false }
|
||||
if os.Getenv("TERM") == "xterm-kitty" { isKitty = true } else { isKitty = false }
|
||||
if os.Getenv("TERM") == "xterm-kitty" ||len(os.Getenv("KITTY_WINDOW_ID")) > 0 { isKitty = true } else { isKitty = false }
|
||||
if len(os.Getenv("MLTERM")) > 0 { isMlterm = true } else { isMlterm = false }
|
||||
if len(os.Getenv("MINTTY_SHORTCUT")) > 0 { isMintty = true } else { isMintty = false }
|
||||
if len(os.Getenv("ALACRITTY_LOG")) > 0 { isAlacritty = true } else { isAlacritty = false }
|
||||
|
@ -41,7 +41,10 @@ func RegisterDrawer(nameNew string, drawerNew Drawer) {
|
||||
}
|
||||
|
||||
func GetDrawers() map[string]Drawer {
|
||||
return atomicDrawers.Load().(map[string]Drawer)
|
||||
if drawers, ok := atomicDrawers.Load().(map[string]Drawer); ok {
|
||||
return drawers
|
||||
}
|
||||
return map[string]Drawer{}
|
||||
}
|
||||
|
||||
func init() {
|
||||
@ -75,7 +78,7 @@ func NewImage(img image.Image) *Image {
|
||||
}
|
||||
|
||||
func (self *Image) Draw(buf *Buffer) {
|
||||
drawers := atomicDrawers.Load().(map[string]Drawer)
|
||||
drawers := GetDrawers()
|
||||
|
||||
// fall back - draw with box characters atomicDrawers.Load().(map[string]Drawer)]["blocks"]
|
||||
// possible enhancement: make use of further box characters like chafa:
|
||||
|
Loading…
x
Reference in New Issue
Block a user