diff options
-rw-r--r-- | ranger/config/rc.conf | 5 | ||||
-rw-r--r-- | ranger/container/settings.py | 3 | ||||
-rw-r--r-- | ranger/core/fm.py | 6 | ||||
-rw-r--r-- | ranger/ext/img_display.py | 60 | ||||
-rw-r--r-- | ranger/gui/ui.py | 3 |
5 files changed, 75 insertions, 2 deletions
diff --git a/ranger/config/rc.conf b/ranger/config/rc.conf index 6867ce4a..98a059d4 100644 --- a/ranger/config/rc.conf +++ b/ranger/config/rc.conf @@ -106,6 +106,11 @@ set preview_images false # while slower, this allows remote previews, # for example during an ssh session. # Tmux is unsupported. +# +# * ueberzug: +# Preview images in full color with the external command "ueberzug". +# Images are shown by using a child window. +# Only for users who run X11 in GNU/Linux. set preview_images_method w3m # Delay in seconds before displaying an image with the w3m method. diff --git a/ranger/container/settings.py b/ranger/container/settings.py index 22562c10..478f6124 100644 --- a/ranger/container/settings.py +++ b/ranger/container/settings.py @@ -107,7 +107,8 @@ ALLOWED_VALUES = { 'line_numbers': ['false', 'absolute', 'relative'], 'one_indexed': [False, True], 'preview_images_method': ['w3m', 'iterm2', 'terminology', - 'urxvt', 'urxvt-full', 'kitty'], + 'urxvt', 'urxvt-full', 'kitty', + 'ueberzug'], 'vcs_backend_bzr': ['disabled', 'local', 'enabled'], 'vcs_backend_git': ['enabled', 'disabled', 'local'], 'vcs_backend_hg': ['disabled', 'local', 'enabled'], diff --git a/ranger/core/fm.py b/ranger/core/fm.py index 61b3cb11..43001e8b 100644 --- a/ranger/core/fm.py +++ b/ranger/core/fm.py @@ -25,7 +25,7 @@ from ranger.core.runner import Runner from ranger.ext.img_display import (W3MImageDisplayer, ITerm2ImageDisplayer, TerminologyImageDisplayer, URXVTImageDisplayer, URXVTImageFSDisplayer, - KittyImageDisplayer, + KittyImageDisplayer, UeberzugImageDisplayer, ImageDisplayer) from ranger.core.metadata import MetadataManager from ranger.ext.rifle import Rifle @@ -102,6 +102,8 @@ class FM(Actions, # pylint: disable=too-many-instance-attributes self.rifle.reload_config() def set_image_displayer(): + if self.image_displayer: + self.image_displayer.quit() self.image_displayer = self._get_image_displayer() set_image_displayer() self.settings.signal_bind('setopt.preview_images_method', set_image_displayer, @@ -238,6 +240,8 @@ class FM(Actions, # pylint: disable=too-many-instance-attributes return URXVTImageFSDisplayer() elif self.settings.preview_images_method == "kitty": return KittyImageDisplayer() + elif self.settings.preview_images_method == "ueberzug": + return UeberzugImageDisplayer() return ImageDisplayer() def _get_thisfile(self): diff --git a/ranger/ext/img_display.py b/ranger/ext/img_display.py index 3f5b5471..9c84ce5e 100644 --- a/ranger/ext/img_display.py +++ b/ranger/ext/img_display.py @@ -20,6 +20,8 @@ import os import struct import sys import warnings +import json +import threading from subprocess import Popen, PIPE import termios @@ -254,6 +256,9 @@ class ITerm2ImageDisplayer(ImageDisplayer, FileManagerAware): self.fm.ui.win.redrawwin() self.fm.ui.win.refresh() + def quit(self): + self.clear(0, 0, 0, 0) + def _generate_iterm2_input(self, path, max_cols, max_rows): """Prepare the image content of path for image display in iTerm2""" image_width, image_height = self._get_image_dimensions(path) @@ -379,6 +384,9 @@ class TerminologyImageDisplayer(ImageDisplayer, FileManagerAware): self.fm.ui.win.redrawwin() self.fm.ui.win.refresh() + def quit(self): + self.clear(0, 0, 0, 0) + class URXVTImageDisplayer(ImageDisplayer, FileManagerAware): """Implementation of ImageDisplayer working by setting the urxvt @@ -667,3 +675,55 @@ class KittyImageDisplayer(ImageDisplayer): # os.remove(self.temp_paths[k]) # except (OSError, IOError): # continue + + +class UeberzugImageDisplayer(ImageDisplayer): + """Implementation of ImageDisplayer using ueberzug. + Ueberzug can display images in a Xorg session. + Does not work over ssh. + """ + IMAGE_ID = 'preview' + is_initialized = False + + def __init__(self): + self.process = None + + def initialize(self): + """start ueberzug""" + if (self.is_initialized and self.process.poll() is None + and not self.process.stdin.closed): + return + + self.process = Popen(['ueberzug', 'layer', '--silent'], + stdin=PIPE, universal_newlines=True) + self.is_initialized = True + + def _execute(self, **kwargs): + self.initialize() + self.process.stdin.write(json.dumps(kwargs) + '\n') + self.process.stdin.flush() + + def draw(self, path, start_x, start_y, width, height): + self._execute( + action='add', + identifier=self.IMAGE_ID, + x=start_x, + y=start_y, + max_width=width, + max_height=height, + path=path + ) + + def clear(self, start_x, start_y, width, height): + if self.process and not self.process.stdin.closed: + self._execute(action='remove', identifier=self.IMAGE_ID) + + def quit(self): + if self.is_initialized and self.process.poll() is None: + timer_kill = threading.Timer(1, self.process.kill, []) + try: + self.process.terminate() + timer_kill.start() + self.process.communicate() + finally: + timer_kill.cancel() diff --git a/ranger/gui/ui.py b/ranger/gui/ui.py index 441e9032..9eab4c87 100644 --- a/ranger/gui/ui.py +++ b/ranger/gui/ui.py @@ -136,6 +136,9 @@ class UI( # pylint: disable=too-many-instance-attributes,too-many-public-method self.vcsthread.pause() self.vcsthread.paused.wait() + if self.fm.image_displayer: + self.fm.image_displayer.quit() + self.win.keypad(0) curses.nocbreak() curses.echo() |