1
0
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:
Simon Lehn 2019-04-21 19:47:06 +02:00
parent c3b389694f
commit 3de3e057f6
9 changed files with 127 additions and 12 deletions

View File

@ -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???

View File

@ -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)

View File

@ -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
View 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:
//

View File

@ -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
}

View File

@ -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

View File

@ -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 }

View File

@ -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: