diff --git a/widgets/exp/HACKING.md b/widgets/exp/HACKING.md index 968fb8e..fb9d17f 100644 --- a/widgets/exp/HACKING.md +++ b/widgets/exp/HACKING.md @@ -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??? diff --git a/widgets/exp/aaa_init.go b/widgets/exp/aaa_init.go index 2cb7236..07b43ab 100644 --- a/widgets/exp/aaa_init.go +++ b/widgets/exp/aaa_init.go @@ -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) diff --git a/widgets/exp/iterm2.go b/widgets/exp/drawer_iterm2.go similarity index 94% rename from widgets/exp/iterm2.go rename to widgets/exp/drawer_iterm2.go index c707126..fd4e19a 100644 --- a/widgets/exp/iterm2.go +++ b/widgets/exp/drawer_iterm2.go @@ -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 diff --git a/widgets/exp/drawer_kitty.go b/widgets/exp/drawer_kitty.go new file mode 100644 index 0000000..8a5eaff --- /dev/null +++ b/widgets/exp/drawer_kitty.go @@ -0,0 +1,103 @@ +// 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: +// \ No newline at end of file diff --git a/widgets/exp/sixel.go b/widgets/exp/drawer_sixel.go similarity index 100% rename from widgets/exp/sixel.go rename to widgets/exp/drawer_sixel.go diff --git a/widgets/exp/urxvt.go b/widgets/exp/drawer_urxvt.go similarity index 82% rename from widgets/exp/urxvt.go rename to widgets/exp/drawer_urxvt.go index 0495b50..fa90b41 100644 --- a/widgets/exp/urxvt.go +++ b/widgets/exp/drawer_urxvt.go @@ -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 } diff --git a/widgets/exp/image.go b/widgets/exp/image.go index f8bf171..ff71d23 100644 --- a/widgets/exp/image.go +++ b/widgets/exp/image.go @@ -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 diff --git a/widgets/exp/terminal_scan.go b/widgets/exp/terminal_scan.go index 7bbd220..38e9cb0 100644 --- a/widgets/exp/terminal_scan.go +++ b/widgets/exp/terminal_scan.go @@ -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 } diff --git a/widgets/image.go b/widgets/image.go index 70eb59f..279f7d0 100644 --- a/widgets/image.go +++ b/widgets/image.go @@ -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: