summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--ranger/config/rc.conf5
-rw-r--r--ranger/container/settings.py3
-rw-r--r--ranger/core/fm.py6
-rw-r--r--ranger/ext/img_display.py60
-rw-r--r--ranger/gui/ui.py3
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()