diff options
-rw-r--r-- | ranger/core/fm.py | 3 | ||||
-rw-r--r-- | ranger/ext/img_display.py | 164 | ||||
-rw-r--r-- | ranger/gui/widgets/pager.py | 18 |
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: |