summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--ranger/core/fm.py3
-rw-r--r--ranger/ext/img_display.py164
-rw-r--r--ranger/gui/widgets/pager.py18
3 files changed, 101 insertions, 84 deletions
diff --git a/ranger/core/fm.py b/ranger/core/fm.py
index 918d1de2..b0d34359 100644
--- a/ranger/core/fm.py
+++ b/ranger/core/fm.py
@@ -19,6 +19,7 @@ from ranger.container.tags import Tags
 from ranger.gui.ui import UI
 from ranger.container.bookmarks import Bookmarks
 from ranger.core.runner import Runner
+from ranger.ext.img_display import ImageDisplayer
 from ranger.ext.rifle import Rifle
 from ranger.container.directory import Directory
 from ranger.ext.signals import SignalDispatcher
@@ -47,6 +48,7 @@ class FM(Actions, SignalDispatcher):
         self.start_paths = paths
         self.directories = dict()
         self.log = deque(maxlen=20)
+        self.image_displayer = ImageDisplayer()
         self.bookmarks = bookmarks
         self.current_tab = 1
         self.tabs = {}
@@ -329,6 +331,7 @@ class FM(Actions, SignalDispatcher):
             raise SystemExit
 
         finally:
+            self.image_displayer.quit()
             if ranger.arg.choosedir and self.thisdir and self.thisdir.path:
                 # XXX: UnicodeEncodeError: 'utf-8' codec can't encode character
                 # '\udcf6' in position 42: surrogates not allowed
diff --git a/ranger/ext/img_display.py b/ranger/ext/img_display.py
index b6f59f4f..3d710837 100644
--- a/ranger/ext/img_display.py
+++ b/ranger/ext/img_display.py
@@ -10,7 +10,12 @@ framebuffer) or in a Xorg session.
 w3m need to be installed for this to work.
 """
 
-import termios, fcntl, struct, sys, os
+import fcntl
+import os
+import select
+import struct
+import sys
+import termios
 from subprocess import Popen, PIPE
 
 W3MIMGDISPLAY_PATH = '/usr/lib/w3m/w3mimgdisplay'
@@ -19,6 +24,91 @@ W3MIMGDISPLAY_OPTIONS = []
 class ImgDisplayUnsupportedException(Exception):
     pass
 
+
+class ImageDisplayer(object):
+    is_initialized = False
+
+    def initialize(self):
+        """start w3mimgdisplay"""
+        self.is_initialized = True
+        self.binary_path = os.environ.get("W3MIMGDISPLAY_PATH", None)
+        if not self.binary_path:
+            self.binary_path = W3MIMGDISPLAY_PATH
+        self.process = Popen([self.binary_path] + W3MIMGDISPLAY_OPTIONS,
+                stdin=PIPE, stdout=PIPE, universal_newlines=True)
+
+    def draw(self, path, start_x, start_y, width, height):
+        """Draw an image at the given coordinates."""
+        if not self.is_initialized or self.process.poll() is not None:
+            self.initialize()
+        self.process.stdin.write(self._generate_w3m_input(path, start_x,
+            start_y, width, height))
+        self.process.stdin.flush()
+        self.process.stdout.readline()
+
+    def clear(self, start_x, start_y, width, height):
+        """Clear a part of terminal display."""
+        if not self.is_initialized or self.process.poll() is not None:
+            self.initialize()
+
+        fontw, fonth = _get_font_dimensions()
+
+        cmd = "6;{x};{y};{w};{h}\n4;\n3;\n".format(
+                x = start_x * fontw,
+                y = start_y * fonth,
+                w = (width + 1) * fontw,
+                h = height * fonth)
+
+        self.process.stdin.write(cmd)
+        self.process.stdin.flush()
+        self.process.stdout.readline()
+
+    def _generate_w3m_input(self, path, start_x, start_y, max_width, max_height):
+        """Prepare the input string for w3mimgpreview
+
+        start_x, start_y, max_height and max_width specify the drawing area.
+        They are expressed in number of characters.
+        """
+        fontw, fonth = _get_font_dimensions()
+        if fontw == 0 or fonth == 0:
+            raise ImgDisplayUnsupportedException()
+
+        max_width_pixels = max_width * fontw
+        max_height_pixels = max_height * fonth
+
+        # get image size
+        cmd = "5;{}\n".format(path)
+
+        self.process.stdin.write(cmd)
+        self.process.stdin.flush()
+        output = self.process.stdout.readline().split()
+
+        if len(output) != 2:
+            raise Exception('Failed to execute w3mimgdisplay', output)
+
+        width = int(output[0])
+        height = int(output[1])
+
+        # get the maximum image size preserving ratio
+        if width > max_width_pixels:
+            height = (height * max_width_pixels) // width
+            width = max_width_pixels
+        if height > max_height_pixels:
+            width = (width * max_height_pixels) // height
+            height = max_height_pixels
+
+        return "0;1;{x};{y};{w};{h};;;;;{filename}\n4;\n3;\n".format(
+                x = start_x * fontw,
+                y = start_y * fonth,
+                w = width,
+                h = height,
+                filename = path)
+
+    def quit(self):
+        if self.is_initialized:
+            self.process.kill()
+
+
 def _get_font_dimensions():
     # Get the height and width of a character displayed in the terminal in
     # pixels.
@@ -28,75 +118,3 @@ def _get_font_dimensions():
     rows, cols, xpixels, ypixels = struct.unpack("HHHH", x)
 
     return (xpixels // cols), (ypixels // rows)
-
-
-def _w3mimgdisplay(commands):
-    """Invoke w3mimgdisplay and send commands on its standard input."""
-    path = os.environ.get("W3MIMGDISPLAY_PATH", None)
-    if not path:
-        path = W3MIMGDISPLAY_PATH
-    process = Popen([path] + W3MIMGDISPLAY_OPTIONS, stdin=PIPE,
-            stdout=PIPE, universal_newlines=True)
-
-    # wait for the external program to finish
-    output, _ = process.communicate(input=commands)
-
-    return output
-
-def generate_w3m_input(path, start_x, start_y, max_width, max_height):
-    """Prepare the input string for w3mimgpreview
-
-    start_x, start_y, max_height and max_width specify the drawing area.
-    They are expressed in number of characters.
-    """
-    fontw, fonth = _get_font_dimensions()
-    if fontw == 0 or fonth == 0:
-        raise ImgDisplayUnsupportedException()
-
-    max_width_pixels = max_width * fontw
-    max_height_pixels = max_height * fonth
-
-    # get image size
-    cmd = "5;{}".format(path)
-    output = _w3mimgdisplay(cmd).split()
-
-    if len(output) != 2:
-        raise Exception('Failed to execute w3mimgdisplay')
-
-    width = int(output[0])
-    height = int(output[1])
-
-    # get the maximum image size preserving ratio
-    if width > max_width_pixels:
-        height = (height * max_width_pixels) // width
-        width = max_width_pixels
-    if height > max_height_pixels:
-        width = (width * max_height_pixels) // height
-        height = max_height_pixels
-
-    return "0;1;{x};{y};{w};{h};;;;;{filename}\n4;\n3;".format(
-            x = start_x * fontw,
-            y = start_y * fonth,
-            w = width,
-            h = height,
-            filename = path)
-
-def draw(path, start_x, start_y, max_width, max_height):
-    """Draw an image file in the terminal.
-
-    start_x, start_y, max_height and max_width specify the drawing area.
-    They are expressed in number of characters.
-    """
-    _w3mimgdisplay(generate_w3m_input(path, start_x, start_y, max_width, max_height))
-
-def clear(start_x, start_y, width, height):
-    """Clear a part of terminal display."""
-    fontw, fonth = _get_font_dimensions()
-
-    cmd = "6;{x};{y};{w};{h}\n4;\n3;".format(
-            x = start_x * fontw,
-            y = start_y * fonth,
-            w = (width + 1) * fontw,
-            h = height * fonth)
-
-    _w3mimgdisplay(cmd)
diff --git a/ranger/gui/widgets/pager.py b/ranger/gui/widgets/pager.py
index 4a98e083..0b9071d7 100644
--- a/ranger/gui/widgets/pager.py
+++ b/ranger/gui/widgets/pager.py
@@ -8,7 +8,7 @@ from . import Widget
 from ranger.core.loader import CommandLoader
 from ranger.gui import ansi
 from ranger.ext.direction import Direction
-import ranger.ext.img_display as img_display
+from ranger.ext.img_display import ImgDisplayUnsupportedException
 
 # TODO: Scrolling in embedded pager
 class Pager(Widget):
@@ -40,7 +40,7 @@ class Pager(Widget):
 
     def clear_image(self, force=False):
         if (force or self.need_clear_image) and self.image_drawn:
-            img_display.clear(self.x, self.y, self.wid, self.hei)
+            self.fm.image_displayer.clear(self.x, self.y, self.wid, self.hei)
             self.need_clear_image = False
             self.image_drawn = False
 
@@ -90,18 +90,14 @@ class Pager(Widget):
             self.source = None
             self.need_redraw_image = False
             try:
-                cmd = CommandLoader([img_display.W3MIMGDISPLAY_PATH] +
-                            img_display.W3MIMGDISPLAY_OPTIONS,
-                        input=img_display.generate_w3m_input(self.image,
-                            self.x, self.y, self.wid, self.hei),
-                        descr="loading preview image",
-                        silent=True, kill_on_pause=True)
-                self.fm.loader.add(cmd)
-                self.image_drawn = True
-            except img_display.ImgDisplayUnsupportedException:
+                self.fm.image_displayer.draw(self.image, self.x, self.y,
+                        self.wid, self.hei)
+            except ImgDisplayUnsupportedException:
                 self.fm.settings.preview_images = False
             except Exception as e:
                 self.fm.notify(e, bad=True)
+            else:
+                self.image_drawn = True
 
     def _draw_line(self, i, line):
         if self.markup is None: