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 using ueberzug #134

Closed
aidam38 opened this issue Jan 29, 2019 · 36 comments
Closed

Image preview using ueberzug #134

aidam38 opened this issue Jan 29, 2019 · 36 comments
Labels

Comments

@aidam38
Copy link

aidam38 commented Jan 29, 2019

Did anybody try image previews using ueberzug (https://github.com/seebye/ueberzug)? It seems like a very well written tool, but I don't how much Go and Python integrate together. I tried to just bruteforce it into my preview script but it didn't work (showed blank screen or error messages from ueberzug).

@TeddyDD
Copy link
Contributor

TeddyDD commented Jan 29, 2019

I couldn't get ueberzug to work at all, not sure if it works with Kitty tho.

@seebye
Copy link

seebye commented Jan 30, 2019

@TeddyDD It works with kitty.
You should just need to install it via pip (python3.5+).
The following example should show an image if it's executed in bash.

source "`ueberzug library`"

{
    ImageLayer::add [identifier]="example0" [x]="0" [y]="0" [path]="/some/path/some_image0.jpg"
    read
} | ImageLayer

@aidam38

but I don't how much Go and Python integrate together.

It runs as a seperate process and takes commands via stdin, so it can be used with any language.

I tried to just bruteforce it into my preview script but it didn't work

That won't work as ueberzug needs to keep running.

@aidam38
Copy link
Author

aidam38 commented Jan 30, 2019

@seebye
So, if you really got it working, please elaborate. I'm not so experienced in hacking lf.

@seebye
Copy link

seebye commented Jan 30, 2019

@aidam38

So, if you really got it working, please elaborate.

I don't use lf. I just tried to answer your post.
Making changes to the preview script won't work as ueberzug needs to run as long as it's used (= as long as the image should be visible).
So you need to make changes to lf in order to use it.

@gokcehan
Copy link
Owner

@seebye Thanks for the explanation. It looks like an interesting project indeed. I'm glad to see someone working on supporting tmux for image displays.

At some point, I have tried to see if w3mimgdisplay work in a preview script without any support on the lf side. It has been some time but I think I remember two main issues. First, it wasn't possible to clear the screen afterwards when you switch to another file since there is currently no mechanism to clear a preview in lf. Second, images were only shown once since previews are currently cached in lf. Tmux and window movements were another issue but I think those are not specific to lf but to w3mimgdisplay in general. I guess maybe ueberzug solve these issues by being a persistent process during the display, though I have no clue how these programs actually work.

On the lf side, I'm still hesitant add support for image display programs. I find terminal images cool and fun but I don't have a strong motivation to work on issues regarding this. I personally rarely work with images on my linux machine and when I do feh or sxiv are easy and functional enough for me on my tiling window manager. So I'm in favor of keeping this as a non-feature.

I will mark this issue as a question just in case people want to share some hacks and ideas.

@EmmChriss
Copy link

I understand your hesitation @gokcehan, it would take some work to get it working like in ranger, but I'd really like if it was possible to make it work on the user side.
Actually that would be possible with a couple trivial changes:

  1. Supply the preview script with x, y, width, and height of the preview portion of the screen
  2. Make it possible to signal from the preview script to lf, that the preview is not textual, and shouldn't be cached (for ex. through the exit code)

@gokcehan
Copy link
Owner

gokcehan commented Feb 6, 2019

@EmmChriss Height is already passed to preview scripts. We can also pass width to the script. Though x and y is a little tricky since I think it is not possible to determine the position according to the terminal window when using tmux. Also I think one needs to determine the font size and convert these values to pixel sizes to use in w3mimgdisplay protocol. We can also disable caching in preview scripts using exit codes like ranger. That may not be a problem I think. But as far as I understand these changes may not be enough to run ueberzug and w3mimgdisplay will have those issues that ueberzug is trying to solve.

@EmmChriss
Copy link

Favoring ueberzug, and ease of implementation, terminal cell positions should be enough.
Also, while ueberzug does not yet claim to support tmux panes, it mostly does work how it's intended: using relative positions, so getting the positions in tmux shouldn't be a problem.
Supporting w3m shouldn't be the goal, ueberzug uses the same hack, but does it better.

@seebye
Copy link

seebye commented Feb 6, 2019

Supporting w3m shouldn't be the goal, ueberzug uses the same hack, but does it better.

It does not. W3M draws on the same window (/ the terminal window) which leads to race conditions.
Ueberzug creates it's own window and handles exposure -, resize -, ... events.

But as far as I understand these changes may not be enough to run ueberzug

That's true.
I think the easiest way to implement ueberzug without implementing it within lf is to use process attachments
(= ueberzug's process dies together with lf's process).
However it's not implemented yet, also I'm still designing it (https://github.com/seebye/ueberzug/issues/28).

Even if process attachments are implemented there's still an issue left:
As ueberzugs images are persistents it's also required to remove these images on e.g. spawning a shell or other programs.

@EmmChriss
Copy link

Now that you mention it, removing images on app execution is really tricky.
It could be solved by setting the lf shell to a custom script that removes the image while the shell is running. Otherwise it seems possible with a script, that periodically checks if lf is alive, and processes img change and remove events through a pipe, but that would take some time to get working.
I realise now, that what I'm describing is a poor man's process attachment.

@seebye
Copy link

seebye commented Feb 6, 2019

It could be solved by setting the lf shell to a custom script that removes the image while the shell is running.

I don't know how lf implements launching a program / shell,
but I think the easiest way would be the simply create a wrapper script.
I think the best workaround would be a hook such as.

#!/usr/bin/env sh
remove_image_command
target_program=$(...)
exec "$target_program" "$@"

Edit: I think changing set shell sh to such a script should likely work.

E.g.

#!/usr/bin/env sh
remove_image_command
exec bash

We can also disable caching in preview scripts using exit codes like ranger.

I think it's easier to add an option to disable the cache,
as images would stay on switching from an image to e.g. a text files.
(As images are persistent)

(Also I just want to remember that this implementation isn't possible till process attachment is implemented.)

@Naheel-Azawy
Copy link

Hi, just to give some hope check this VIDEO out!
It did work! But too buggy and I only showed the successful cases. Also, the code is dirty with a lot of hardcoding and zero optimization.
I'm busy now and I might work on this later but meanwhile you can check lfrc.sh and lfpv.sh from my dotfiles. I also would be so glad if someone get to work properly before me!

@Naheel-Azawy
Copy link

Hello again!
Here's a quick update. I've written THIS script and it seems to be doing the job. Here's an example of how it works where 0 is the id in this example:

$ lfimgpv --listen 0 &
$ lfimgpv --add 0 $HOME/1.png
$ lfimgpv --add 0 $HOME/2.png
$ lfimgpv --clear 0
$ lfimgpv --end 0

In a practical scenario, --add will be called from the previewer script.
--listen should be called once lf starts, --clear if not on an image file, and --end when lf exits.

The problems are that I'm not sure how to listen to these events from lf except adding.
Another issue is that the preview script is only called once. If I pass on one file, go somewhere else and back to it, it does not call the previewer again.
One last problem is how can I get the id of the lf session in the previewer script?

Would it be possible to handle all of that only from configurations scripts or the source must be modified?

@EmmChriss
Copy link

Hey, nice script.

In lf you can execute arbitrary shell code on initialization by adding

${{
  # your code here
}}

Or you could use the -command argument.
The exit hook is trickier. The only option I could think of is starting a background process that periodically checks if lf is alive. That would work though.
For the caching, you could remap the <up>, <down>, j and k keys to also call the preview script with an additional argument, that enables the image preview.

@Naheel-Azawy
Copy link

Thanks, cool!
I already did such a thing for up and down but this is so dirty.
I think there should be listeners provided in the config script.
Another issue is that lf caches the previews which means the script will only be called once. So I had to comment that part in the source to make it work. I prefer if some exit code gets defined to let lf know that it doesn't need to be cached. Should I do it myself and make a pull request? I think I haven't touched go since it first appeared xD.
The VIDEO if anyone is interested.
And the files that can be helpful: rc, pv, imgpv
I hope to see more attention on this issue from developers!

@gokcehan
Copy link
Owner

@Naheel-Azawy Sorry for late reply. I wanted to try it for myself before I make a comment here. I have now tried this with your imgpv script and I should say this is very encouraging. Using fifo to wrap ueberzug is a really nice strategy. I think we can make some changes on lf side to make this work.

As you also mention, currently we have the following issues:

  1. Previews are cached and so images are only displayed once. We should add a mechanism to disable caching in previews. As far as I know, Go does not provide exit codes but only gives errors in processes for platform independence. I think it is fine to only distinguish success and error cases so that any non-zero code should disable caching. This is probably better also for cases other than image previews.

  2. Id of the current client is only passed to the preview script after a shell command is run after initialization. This behavior is accidental as environmental variables are set during shell commands. We have these variables such as id and some others with the same value during the lifetime of the lf process so we should set these during the initialization instead.

  3. We don't have a mechanism to run a command on exit, but starting and exiting ueberzug daemon can be accomplished with a wrapper script around lf. I think most people already use a lfcd function so these can simply be added there as follows:

...
lfimgpv --listen $id &
lf -last-dir-path="$tmp" "$@"
lfimgpv --end $id
...
  1. Changing from an image to a non-image file does not clear the image on the screen. A workaround for this is to clear the image for all files at the start of the preview script though this does not work when switching from an image file to a directory as the preview script is not called then. I will check the code to see if we can add a simple mechanism to run a preview clear script to solve this issue.

Overall, 1 and 2 seems easy and I may have some time in the weekend to push these changes. I think we can leave 3 as it is for now. 4 is somewhat complicated and it may require some thinking before doing some changes.

@Naheel-Azawy
Copy link

Naheel-Azawy commented Feb 23, 2019

@gokcehan Seems very promising!
1 and 2 are pretty straight forward as you said. For 3, I actually always use lfcd and I never thought of that for some reason! But wait, how can we get the id? For 4 I did try this workaround but then realized the directories thing. I think the only workaround for this so far is as @EmmChriss mentioned plus the key but this is too dirty. Maybe just call some command whenever passing over any item for both directories or files before calling the previewer and let the user define this command in the rc. In this case, clear the image.

One more thing that I forgot! Is there a way to get the rations form lf -remote? Because now I'm assuming 1:2:3 regardless.

And btw, since you're here, I have to say thank you! I was using ranger, nnn, fff, and even nautilus at the same time and all were terrible in a way or another. You saved my environment!

@gokcehan
Copy link
Owner

For 3, I actually always use lfcd and I never thought of that for some reason! But wait, how can we get the id?

@Naheel-Azawy Ahh sorry, my bad. I forgot we don't have id in the wrapper script. It is still possible with a temporary file using -command flag with something like:

...
fid="$(mktemp)"
/tmp/lf -command '$printf $id > '"$fid"'; lfimgpv --listen $id &' -last-dir-path="$tmp" "$@"
lfimgpv --end "$(cat $fid)"
rm $fid
...

This is not as pretty but it should work.

One more thing that I forgot! Is there a way to get the rations form lf -remote? Because now I'm assuming 1:2:3 regardless.

I forgot to mention this in the previous post. So let's call this our 5th issue. And I also realize we have another issue with resizing.

  1. It is currently not easy to determine the position of the preview window. Maybe we can pass the coordinates as variables (i.e. $x and $y). Height is currently passed as a positional parameter (i.e. $2) but if we make this change, it would make more sense to pass it as a variable along with width (i.e. $h and $w). It seems that ueberzug does not require pixels so these are in terms of character coordinates.

  2. Previews currently do not react to resize events. For image previews we need a mechanism to refresh the image coordinates otherwise images can be shown in the wrong place possibly over other terminals or ui elements. I think there may also be some unresolved issues in either lfimgpv or ueberzug bash interface regarding this.

In the meantime, I have been looking at caching and it seems that this is not as straightforward as I imagined. There are a lot of places in the code that we try to load the file from the cache. Also it is even more complicated because we also check the modification time at the same time to see if the file has been changed or not. For example, when we disable cache for a file, period option periodically runs the preview script on the file. Many other commands and options also have similar behaviors.

To be honest, I started to feel a little overwhelmed by these issues. Even if we fix some of these, it still requires a lot of configuration for casual users and we won't be able to provide an out-of-the-box experience like ranger. External programs will probably be snappier than this approach. There will probably be glitches and we are probably going to introduce some bugs in the code which will effect users not interested in image previews. And I think this still won't work on ssh connections. It seems to me like too much trouble for very little gain.

@Naheel-Azawy
Copy link

  1. It is currently not easy to determine the position of the preview window. Maybe we can pass the coordinates as variables (i.e. $x and $y). Height is currently passed as a positional parameter (i.e. $2) but if we make this change, it would make more sense to pass it as a variable along with width (i.e. $h and $w). It seems that ueberzug does not require pixels so these are in terms of character coordinates.

This would be cool but I think it would be much easier if there's simply a way to get the values of the options somehow from lf -remote. This means that all what will be needed is simply to get the ratios in lfimgpv and calculate x and y based on it.

  1. Previews currently do not react to resize events. For image previews we need a mechanism to refresh the image coordinates otherwise images can be shown in the wrong place possibly over other terminals or ui elements. I think there may also be some unresolved issues in either lfimgpv or ueberzug bash interface regarding this.

I don't find this as a very big deal at the moment. Perhaps call the previewer script with some flags whenever on image and resized? I think we can leave this for later anyway.

In the meantime, I have been looking at caching and it seems that this is not as straightforward as I imagined. There are a lot of places in the code that we try to load the file from the cache. Also it is even more complicated because we also check the modification time at the same time to see if the file has been changed or not. For example, when we disable cache for a file, period option periodically runs the preview script on the file. Many other commands and options also have similar behaviors.

Not sure but what I did just for testing is that I removed everything in loadReg leaving it to be something like this:

func (nav *nav) loadReg(ui *ui, path string) *reg {
	go nav.preview()
	r := &reg{loading: true, path: path}
	return r
}

Is this somehow so wrong? Wouldn't it be some thing like if !ok || magic { ...?

To be honest, I started to feel a little overwhelmed by these issues. Even if we fix some of these, it still requires a lot of configuration for casual users and we won't be able to provide an out-of-the-box experience like ranger. External programs will probably be snappier than this approach. There will probably be glitches and we are probably going to introduce some bugs in the code which will effect users not interested in image previews. And I think this still won't work on ssh connections. It seems to me like too much trouble for very little gain.

I was amazed when I first found this issue when you clearly stated that image previews are not planned to be features in the readme! I kind of agree with you. And btw, my idea was that once this finishes is to add some binding to toggle image previews as I don't always need them. But sometimes I do need them. LF is my main file browser on my main machine so surely I need this.
I think that what should be done here is not implementing image previews in lf like ranger or anything else. It should be only preparing a friendly environment for users to be able to implement it with only few scripts and configs. I expect no more than a little section in the tips talking about this and how to implement it with ueberzug.

@gokcehan
Copy link
Owner

@Naheel-Azawy For caching, we don't want to disable it altogether. We only want to do this selectively for erroneous previews. As you said, it is something along the line if !ok || magic { ... and I did kind of manage to do this with some additions to a few places. The problem is that the rest of the code assumes things to be cached so there are a lot of redundant preview triggers here and there. For example, grep loadFile *.go | wc -l returns 67 in the current codebase. Some of these can be checked and eliminated. For example up command triggers loadFile even if the selection is at the first file which in turn executes a preview run. Others can be more difficult to get rid of, not only because it is difficult to patch the code, but also because it is not straightforward to decide what should be the behavior we want. For example our period option periodically tries to check the modification dates of files and directories and updates the cache if necessary. If we skip the cache for a file and that file is the currently selected one, currently we need to periodically trigger preview executions on that file which in turn causes flickers on the screen.

I'm not saying these things are impossible to fix, just that we need to spend some time to review the code to do this right. I'm currently working on builtin copy/move operations which I think is more prior to this issue. Maybe after that I can return to this issue again.

In the meantime, if someone wants to tackle this, feel free to do so. It may take me a while to respond here or review the PR but I should eventually be able to do so. For caching, ideally you should have come up with a solution to period option. The other critical issue is preview cleaning (i.e. issue 4) which I think can be added as an option like cleaner to take the name of a script file much like previewer. The rest of the issues may not be as critical and can be added in time.

In the long term, I feel like it is wiser to keep the "image previews" entry in non-features section in the readme as we would not want to create false expectations for new users. I agree that we should have this in our wiki with scripts and configs. Maybe it would be better to have a separate page for image previews which may also include ascii/unicode based solutions.

gokcehan added a commit that referenced this issue Mar 17, 2019
@doronbehar
Copy link
Contributor

Hey guys, since this is pretty much the single issue that tracks any kind of image preview hacks, I'd like to share with you mine. It's rather simple, it doesn't involve ueberzug in anyway so I hope this is not too much off-topic but I'd like to hear your comments never the less.

There's this kind of popular image viewer, called sxiv. It doesn't include tons of features and I don't like much it's development style but it's pretty good. One of it's features is the key-handler:

This feature allows you to put an executable in $XDG_CONFIG_HOME/sxiv/exec/ called key-handler. It's invoked when you hit Ctrl-x and press another key. The other key is passed as the only argument to the executable and the file paths of the marked images are passed via stdin to the executable. Here's an example from sxiv's repo.

I use this feature along with Lf's -remote interface to select images using sxiv. When I enter a directory with images I'd like to preview or move/copy with Lf, I launch sxiv on one of them as the default image viewer on my system, mark the images I want and when I hit Ctrl-x twice my key-handler script tells the running lf process to select these images. It's not as smooth as in Ranger but I think it's the most sensible way to integrate a real image viewer with a terminal file manager.

Unfortunately, and this is why I said I don't like sxiv's development style, one of the most wanted features from sxiv is this: You want to run sxiv this-image.png, and be able to view the rest of the images in sxiv's thumbnail mode and with n p. The developer of sxiv rejected this idea several times already and people have been writing shell wrappers for sxiv to overcome that. I didn't like that and I've implemented the requested behaviour in my fork of sxiv. Naturally, only this way sxiv can fit to the workflow explained above.

Here's my key-handler shell script:

#!/bin/sh

case "$1" in
  "C-x")
    while read file; do
      lf -remote "send select '$file'"
      lf -remote "send toggle"
    done
    ;;
esac

I haven't actually given attention to what ueberzug has to offer and what scripts the nice people here have wrote in the attempt to actually make it useful for lf so I'm not saying my hack is the best. But I hope you'll like it ☺️.

@ghyatzo
Copy link

ghyatzo commented Oct 6, 2019

Hello,

Regarding the preview of images i might have found a solution using the kitty terminal.
Kitty has a built in image previewer (icat) which works quite well.

The only issue i have right now is having some coordinates to tell kitty how to resize the bigger pictures.
Any progress on passing $x, $y, $h and $w to the preview script?

Also, minor bug that i still have to figure out.
clearing out the preview pane.

@gokcehan
Copy link
Owner

gokcehan commented Oct 7, 2019

@ghyatzo There is an ongoing PR #208 though it has some difficult issues that need to be addressed before merge.

@elhenro
Copy link

elhenro commented Nov 25, 2019

@ghyatzo i got this working with kitty and icat using this pv.sh.
issues:

  1. it is not triggered on directories, so the image is not cleared from the terminal window if moved back to a dir after previewing an image.
  2. the image is not cleared if lf is restarted and a black rectangle is left

pv.sh:

#!/bin/sh
kitty +kitten icat --clear  --transfer-mode file --silent
icat="kitty +kitten icat --place 30x30@40x0 --transfer-mode file --silent"
case "$1" in
    *.jpg) $icat "$1";;
    *.png) $icat "$1";;
    *.gif) $icat "$1";;
    *.tar*) tar tf "$1";;
    *.zip) unzip -l "$1";;
    *.rar) unrar l "$1";;
    *.7z) 7z l "$1";;
    *.pdf) pdftotext "$1" -;;
    *) highlight -O ansi "$1" || cat "$1";;```

@ghyatzo
Copy link

ghyatzo commented Nov 25, 2019

@elhenro yeah, i've encountered the same issues while playing with it. Great catch on the place function, but still. A bit wonky since it is an absolute position and size, anchored to the top left corner.

Anyway, other than not firing on directories, i get that the images are not reloaded after i previewed all the images in a folder i'm stuck with the last image until i preview another kind of file (and the clear function gets called again ).
Might be interesting to go to the kitty repo and check there the behaviour of icat and clear.

@elhenro
Copy link

elhenro commented Nov 26, 2019

@ghyatzo yeah, it is still wonky. I tried using the --align option as well, but no success in combining this with --place yet. You are also right about the images not reloading after all images of a folder were previewed, i did not notice before. i havent found any documentation on kitty's icat, except for this

@ghyatzo
Copy link

ghyatzo commented Nov 26, 2019

Probably using tput cols and tput lines and fixing the ratios of the panes in lf you can manage to put the image in the right relative position even with a resize.
I'll try to work with it this evening if i can spare some time.

@elhenro
Copy link

elhenro commented Jul 23, 2020

finally got image (and pdf, epub etc) previews working with lf in kitty terminal with ueberzug using cirala's lfimg
with BrodieRobertson's preview script

@NullSense
Copy link

NullSense commented Nov 23, 2020

Has anyone tried https://github.com/posva/catimg https://github.com/hzeller/timg https://github.com/atanunq/viu these on wayland? As normal image previewers don't really work. Viu seemed to work best for me.

I am not sure how to pass proper sizing to such a program from lf to size it properly.

@xlucn
Copy link

xlucn commented Dec 30, 2020

I confirm the ueberzug image preview is working perfectly with #531. What I did is similar to https://github.com/cirala/lfimg, but the issues mentioned in that repo does not exist anymore!

If anyone is interested, see my lf files: https://github.com/OliverLew/dotfiles/tree/master/lf.

lf-ueberzug-preview

@slavistan
Copy link

slavistan commented Dec 31, 2020

Can confirm aswell. I've written up a little recipe on how to set up lf from scratch for image and pdf previews, video thumbnails and basic shell integration to have the choice whether to change directories upon exiting lf. All required files are included.

https://github.com/slavistan/howto-lf-image-previews

@Monirzadeh
Copy link

Can confirm aswell. I've written up a little recipe on how to set up lf from scratch for image and pdf previews, video thumbnails and basic shell integration to have the choice whether to change directories upon exiting lf. All required files are included.

https://github.com/slavistan/howto-lf-image-previews

i follow steps but when i try to run lf-run get this error unknown option: cleaner

@xlucn
Copy link

xlucn commented Jan 1, 2021

@Monirzadeh You have to use the latest git version and build lf yourself

@gokcehan
Copy link
Owner

For anyone following this thread, image patch is now merged and a new release r19 is tagged with the patch. Example preview scripts can be found in Previews wiki page. Thanks to contributors for their effort and patience.

@Naheel-Azawy
Copy link

@gokcehan Finally! This took a while but certainly worth it. Have a nice day!

@alexacallmebaka
Copy link

Note for any LF newbies coming from Google like myself, this is super easy to install and does the job: https://github.com/slavistan/lf-gadgets/tree/master/lf-ueberzug.

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

No branches or pull requests