Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Image preview not working with Wezterm's implementation of iTerm2 graphics protocol #3646

Open
5 of 10 tasks
unrealapex opened this issue Feb 25, 2024 · 8 comments
Open
5 of 10 tasks

Comments

@unrealapex
Copy link

  • I have read through the manual page (man fzf)
  • I have the latest version of fzf
  • I have searched through the existing issues

Info

  • OS
    • Linux
    • Mac OS X
    • Windows
    • Etc.
  • Shell
    • bash
    • zsh
    • fish

Problem / Steps to reproduce

Is it possible to get Wezterm's implementation of iTerm2's image protocol working in FZF preview? iTerm2 image support integration is available, however, it seems that it does not extend to Wezterm's implementation:

 fzf --preview 'wezterm imgcat {}'

See wez/wezterm#5049

@junegunn
Copy link
Owner

What is the difference between wezterm imgcat and imgcat? Is there any reason you have to use the former?

@unrealapex
Copy link
Author

I am not sure regarding the technical reasons why they are different, but I believe Wezterm's implementation is done natively in Rust and also renders images with better performance than iTerm2's imgcat script. You may wish to look at its help page. As for using the former, Wezterm's imgcat comes out of the box...

@junegunn
Copy link
Owner

So here's what I've found.

The output of imgcat ends with ^G as documented in the official specification (See "Protocol" section of https://iterm2.com/documentation-images.html). However, the output of wezterm imgcat ends with <ESC>\.

$ imgcat fzf.png | tr -d '\n' | tail -c 1 | ruby -ne 'p $_'
"\a"

$ wezterm imgcat fzf.png | tr -d '\n' | tail -c 2 | ruby -ne 'p $_'
"\e\\"

Is this intentional? Anyway, for some reason, iTerm2 also allows this case and prints the image.

It is trivial for fzf to change its protocol parser to support this alternative sequence.

diff --git a/src/terminal.go b/src/terminal.go
index d267aec..2069571 100644
--- a/src/terminal.go
+++ b/src/terminal.go
@@ -67,7 +67,7 @@ func init() {
 	// * https://sw.kovidgoyal.net/kitty/graphics-protocol
 	// * https://en.wikipedia.org/wiki/Sixel
 	// * https://iterm2.com/documentation-images.html
-	passThroughRegex = regexp.MustCompile(`\x1bPtmux;\x1b\x1b.*?[^\x1b]\x1b\\|\x1b(_G|P[0-9;]*q).*?\x1b\\\r?|\x1b]1337;.*?\a`)
+	passThroughRegex = regexp.MustCompile(`\x1bPtmux;\x1b\x1b.*?[^\x1b]\x1b\\|\x1b(_G|P[0-9;]*q).*?\x1b\\\r?|\x1b]1337;.*?(\a|\x1b\\)`)
 }
 
 type jumpMode int

@unrealapex
Copy link
Author

unrealapex commented Feb 29, 2024

I'm going to assume this alternate escape sequence was unintentional. @wez may have a different answer.

@wez
Copy link

wez commented Feb 29, 2024

OSC sequences are defined as being terminated by the ST sequence, which is ESC \. They can also be terminated by ^G. Since the iterm2 protocol is using OSC sequences, parsers must support both of these forms of termination.

@junegunn
Copy link
Owner

junegunn commented Mar 2, 2024

Thanks for the comment. I will update the regex.

But,

# Dump the output to a file
wezterm imgcat fzf.png > /tmp/image

# With the updated regex, fzf can render the image in the preview window
fzf --preview 'cat /tmp/image'

# However, this doesn't work. 'wezterm imgcat' process doesn't finish and just hang.
fzf --preview 'wezterm imgcat fzf.png'

@wez
Copy link

wez commented Mar 2, 2024

hmm, wezterm also tries to query terminal properties via the XTVERSION escape sequence, and then the screen dimensions via a variety of probing escape sequences. Perhaps those also need to be allowed through fzf's processing layer and the responses allowed to be read?

wezterm imgcat assumes that every terminal will be able to respond to the \u001b[c sequence, and uses that to avoid waiting forever to receive a response. I think in this case fzf may be preventing that from happening.

Here's a snippet from an ascii-cast showing the probe escapes that are emitted prior to emitting the iterm2 image data OSC sequence:

[9.833448,"o","\u001b[>q\u001b[c"]
[9.837395,"o","\u001b[>q\u001b[c"]
[9.841322,"o","\u001b[18t\u001b[16t\u001b[c"]

@junegunn
Copy link
Owner

junegunn commented Mar 4, 2024

Thanks for the pointers.

I've discovered that the hanging has something to do with setpgid. This is a minimal code to reproduce the problem.

package main

import (
	"os/exec"
	"syscall"
)

func main() {
	cmd := exec.Command("/bin/sh", "-c", "wezterm imgcat ../images/fzf.png")
	cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
	cmd.Start()
	cmd.Wait()
}

This program hangs, and not setting setpgid fixes the issue. However, fzf sets the flag so that it can easily kill the preview command and its child processes at once.

if setpgid {
cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
}

Not setting this in fzf, unfortunately, doesn't make wezterm imgcat work as a preview command. fzf reads the response sequences directly from /dev/tty as the user input and pastes them on its prompt.

image

So there doesn't seem to be an easy way to fix it right now, and I recommend using one of the alternatives for now, such as chafa.

# You need to pass the size of the preview window
fzf --preview 'chafa -f iterm -s ${FZF_PREVIEW_COLUMNS}x${FZF_PREVIEW_LINES} {}'

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants