From b512e309a0a09ee88c6866535ed23ec80f7aaef9 Mon Sep 17 00:00:00 2001 From: mark-dawn Date: Wed, 14 Feb 2018 01:16:38 +0100 Subject: Kitty image display alpha, merge-ready Added more comments to the code reverted changes to default values in rc.conf addes explanations in rc.config about the new options Couple of small stylistic corrections after running make test Updated the man pages --- doc/ranger.1 | 17 +++++++++++++++++ doc/ranger.pod | 15 +++++++++++++++ 2 files changed, 32 insertions(+) (limited to 'doc') diff --git a/doc/ranger.1 b/doc/ranger.1 index 1290cb58..f8f0bd8c 100644 --- a/doc/ranger.1 +++ b/doc/ranger.1 @@ -324,6 +324,23 @@ The same as urxvt but utilizing not only the preview pane but the whole terminal window. .PP To enable this feature, set the option \f(CW\*(C`preview_images_method\*(C'\fR to urxvt-full. +.PP +\fIkitty\fR +.IX Subsection "kitty" +.PP +This only works on Kitty. +It requires \s-1PIL\s0 or pillow at the moment to work. A nasty bug that can +corrupt the terminal window when scrolling quicly many images, that can +be solved by with the \f(CW\*(C`redraw_window\*(C'\fR command. +.PP +To enable this feature, set the option \f(CW\*(C`preview_images_method\*(C'\fR to kitty +.PP +\fIkitty-network\fR +.IX Subsection "kitty-network" +.PP +The same as kitty, but uses a streaming method to allow previews remotely, +for example in an ssh session. However the system is slighly more computationally taxing +and the quality of the preview is reduced for bandwidth considerations. .SS "\s-1SELECTION\s0" .IX Subsection "SELECTION" The \fIselection\fR is defined as \*(L"All marked files \s-1IF THERE ARE ANY,\s0 otherwise diff --git a/doc/ranger.pod b/doc/ranger.pod index 4cac8ef9..79a61a98 100644 --- a/doc/ranger.pod +++ b/doc/ranger.pod @@ -236,6 +236,21 @@ window. To enable this feature, set the option C to urxvt-full. +=head3 kitty + +This only works on Kitty. +It requires PIL or pillow at the moment to work. A nasty bug that can +corrupt the terminal window when scrolling quicly many images, that can +be solved by with the C command. + +To enable this feature, set the option C to kitty + +=head3 kitty-network + +The same as kitty, but uses a streaming method to allow previews remotely, +for example in an ssh session. However the system is slighly more computationally taxing +and the quality of the preview is reduced for bandwidth considerations. + =head2 SELECTION The I is defined as "All marked files IF THERE ARE ANY, otherwise -- cgit 1.4.1-2-gfad0 From c7528ea081cd621829dd23c4e47d67d8d2da6a9b Mon Sep 17 00:00:00 2001 From: mark-dawn Date: Fri, 16 Feb 2018 03:13:29 +0100 Subject: Bugfixes & Improvements Fixed support for terminology terminal emulator Fixed the annoying bug affecting fast scrolling (tnkx@kovidgoyal) Refactored some code repeated for the whole module Eliminated some dependencies --- doc/ranger.1 | 6 ++ doc/ranger.pod | 5 ++ ranger/config/rc.conf | 4 + ranger/core/fm.py | 2 +- ranger/ext/img_display.py | 219 ++++++++++++++++++++++------------------------ 5 files changed, 120 insertions(+), 116 deletions(-) (limited to 'doc') diff --git a/doc/ranger.1 b/doc/ranger.1 index f8f0bd8c..a0fc79a1 100644 --- a/doc/ranger.1 +++ b/doc/ranger.1 @@ -307,6 +307,12 @@ This feature relies on the dimensions of the terminal's font. By default, a width of 8 and height of 11 are used. To use other values, set the options \&\f(CW\*(C`iterm2_font_width\*(C'\fR and \f(CW\*(C`iterm2_font_height\*(C'\fR to the desired values. .PP +\fIterminology\fR +.IX Subsection "terminology" +.PP +This only works in terminology. It can render vectors graphics, but works only locally. +To enable this feature, set the option \f(CW\*(C`preview_images_method\*(C'\fR to terminology. +.PP \fIurxvt\fR .IX Subsection "urxvt" .PP diff --git a/doc/ranger.pod b/doc/ranger.pod index 79a61a98..fbe20012 100644 --- a/doc/ranger.pod +++ b/doc/ranger.pod @@ -220,6 +220,11 @@ This feature relies on the dimensions of the terminal's font. By default, a width of 8 and height of 11 are used. To use other values, set the options C and C to the desired values. +=head3 terminology + +This only works in terminology. It can render vectors graphics, but works only locally. +To enable this feature, set the option C to terminology. + =head3 urxvt This only works in urxvt compiled with pixbuf support. Does not work over ssh. diff --git a/ranger/config/rc.conf b/ranger/config/rc.conf index b8bc00d8..cf27cbb5 100644 --- a/ranger/config/rc.conf +++ b/ranger/config/rc.conf @@ -86,6 +86,10 @@ set preview_images false # width of 8 and height of 11 are used. To use other values, set the options # iterm2_font_width and iterm2_font_height to the desired values. # +# * terminology: +# Previews images in full color in the terminology terminal emulator. +# Supports a wide variety of formats, even vector graphics like svg +# # * urxvt: # Preview images in full color using urxvt image backgrounds. This # requires using urxvt compiled with pixbuf support. diff --git a/ranger/core/fm.py b/ranger/core/fm.py index 26feb948..226b1461 100644 --- a/ranger/core/fm.py +++ b/ranger/core/fm.py @@ -225,7 +225,7 @@ class FM(Actions, # pylint: disable=too-many-instance-attributes for line in entry.splitlines(): yield line - def _get_image_displayer(self): + def _get_image_displayer(self): # pylint: disable=too-many-return-statements if self.settings.preview_images_method == "w3m": return W3MImageDisplayer() elif self.settings.preview_images_method == "iterm2": diff --git a/ranger/ext/img_display.py b/ranger/ext/img_display.py index 122e3a15..09d3429c 100644 --- a/ranger/ext/img_display.py +++ b/ranger/ext/img_display.py @@ -22,9 +22,7 @@ import sys from subprocess import Popen, PIPE import termios -import select from contextlib import contextmanager -import tty import codecs from tempfile import NamedTemporaryFile @@ -40,6 +38,29 @@ W3MIMGDISPLAY_PATHS = [ '/usr/local/libexec/w3m/w3mimgdisplay', ] +# Helper functions shared between the previewers (make them static methods of the base class?) + + +@contextmanager +def temporarly_moved_cursor(to_y, to_x): + """Common boilerplate code to move the cursor to a drawing area. Use it as: + with temporarly_moved_cursor(dest_y, dest_x): + your_func_here()""" + curses.putp(curses.tigetstr("sc")) + move_cur(to_y, to_x) + yield + curses.putp(curses.tigetstr("rc")) + sys.stdout.flush() + + +# this is excised since Terminology needs to move the cursor multiple times +def move_cur(to_y, to_x): + tparm = curses.tparm(curses.tigetstr("cup"), to_y, to_x) + if sys.version_info[0] < 3: + sys.stdout.write(tparm) + else: + sys.stdout.buffer.write(tparm) + class ImageDisplayError(Exception): pass @@ -215,15 +236,8 @@ class ITerm2ImageDisplayer(ImageDisplayer, FileManagerAware): """ def draw(self, path, start_x, start_y, width, height): - curses.putp(curses.tigetstr("sc")) - tparm = curses.tparm(curses.tigetstr("cup"), start_y, start_x) - if sys.version_info[0] < 3: - sys.stdout.write(tparm) - else: - sys.stdout.buffer.write(tparm) # pylint: disable=no-member - sys.stdout.write(self._generate_iterm2_input(path, width, height)) - curses.putp(curses.tigetstr("rc")) - sys.stdout.flush() + with temporarly_moved_cursor(start_y, start_x): + sys.stdout.write(self._generate_iterm2_input(path, width, height)) def clear(self, start_x, start_y, width, height): self.fm.ui.win.redrawwin() @@ -332,44 +346,23 @@ class TerminologyImageDisplayer(ImageDisplayer, FileManagerAware): self.close_protocol = "\000" def draw(self, path, start_x, start_y, width, height): - # Save cursor - curses.putp(curses.tigetstr("sc")) - - y = start_y - # Move to drawing zone - self._move_to(start_x, y) - - # Write intent - sys.stdout.write("%s}ic#%d;%d;%s%s" % ( - self.display_protocol, - width, height, - path, - self.close_protocol)) - - # Write Replacement commands ('#') - for _ in range(0, height): - sys.stdout.write("%s}ib%s%s%s}ie%s" % ( - self.display_protocol, - self.close_protocol, - "#" * width, + with temporarly_moved_cursor(start_y, start_x): + # Write intent + sys.stdout.write("%s}ic#%d;%d;%s%s" % ( self.display_protocol, + width, height, + path, self.close_protocol)) - y = y + 1 - self._move_to(start_x, y) - - # Restore cursor - curses.putp(curses.tigetstr("rc")) - - sys.stdout.flush() - @staticmethod - def _move_to(x, y): - # curses.move(y, x) - tparm = curses.tparm(curses.tigetstr("cup"), y, x) - if sys.version_info[0] < 3: - sys.stdout.write(tparm) - else: - sys.stdout.buffer.write(tparm) # pylint: disable=no-member + # Write Replacement commands ('#') + for y in range(0, height): + move_cur(start_y + y, start_x) + sys.stdout.write("%s}ib%s%s%s}ie%s\n" % ( # needs a newline to work + self.display_protocol, + self.close_protocol, + "#" * width, + self.display_protocol, + self.close_protocol)) def clear(self, start_x, start_y, width, height): self.fm.ui.win.redrawwin() @@ -485,20 +478,49 @@ class KittyImageDisplayer(ImageDisplayer): https://github.com/kovidgoyal/kitty/blob/master/graphics-protocol.asciidoc""" protocol_start = b'\033_G' protocol_end = b'\033\\' + def __init__(self, stream=False, resize_height=720): self.image_id = 0 self.temp_paths = [] # parameter deciding if we're going to send the picture data # in the command body, or save it to a temporary file - # the former being default since it is network aware self.stream = stream self.max_height = resize_height + if "screen" in os.environ['TERM']: # TODO: probably need to modify the preamble pass - # TODO: implement check if protocol terminal supports kitty protocol - # Poissibbly automatically check if transfer via file is possible, - # and if negative switch to streaming mode? + + # we need to find out the encoding for a path string, ascii won't cut it + try: + self.fsenc = sys.getfilesystemencoding() # returns None if standard utf-8 is used + # throws LookupError if can't find the codec, TypeError if fsenc is None + codecs.lookup(self.fsenc) + except (LookupError, TypeError): + self.fsenc = 'utf-8' + + # automatic check if we share the filesystem using a dummy file + # TODO: this doesn't work somehow, the response from kitty appears on + # the tty, and until a newline is inserted the data won't be sent + # to the stdin we have. This works just fine in draw. Something tells me that since this is + # called early the tubes are not set up correctly yet, but I have no idea how to fix it + # + # with NamedTemporaryFile() as tmpf: + # tmpf.write(bytearray([0xFA]*3)) + # tmpf.flush() + # for cmd in self._format_cmd_str({'i': 1, 'f': 24,'t': 'f', 's': 1, 'v': 1, 'S': 3}, + # payload=base64.standard_b64encode(tmpf.name.encode(self.fsenc))): + # sys.stdout.buffer.write(cmd) + # resp = [b''] + # sys.stdout.flush() + # for _ in range(5): + # while resp[-1] != b'\\': + # resp.append(sys.stdin.buffer.read(1)) + # if b''.join(resp[-4:-2]) == b'OK': + # self.stream = False + # else: + # self.stream = True + self.backend = None try: # pillow is the default since we are not going @@ -511,17 +533,21 @@ class KittyImageDisplayer(ImageDisplayer): # TODO: implement a wrapper class for Imagemagick process to # replicate the functionality we use from im - def draw(self, path, start_x, start_y, width, height): + def draw(self, path, start_x, start_y, width, height): # pylint: disable=too-many-locals + self.image_id += 1 # dictionary to store the command arguments for kitty # a is the display command, with T going for immediate output - cmds = {'a': 'T'} + # i is the id entifier for the image + cmds = {'a': 'T', 'i': self.image_id} # sys.stderr.write('{}-{}@{}x{}\t'.format(start_x, start_y, width, height)) + assert self.backend is not None # sanity check if we actually have a backend image = self.backend.open(path) aspect = image.width / image.height - # first let's reduce the size of the image if we intend to stream it + # first let's reduce the size of the image if image.height > self.max_height: image = image.resize((int(self.max_height * aspect), self.max_height), self.filter) + # since kitty streches the image to fill the view box # we need to resize the box to not get distortion mismatch_ratio = aspect / (width * 0.5 / height) @@ -538,7 +564,7 @@ class KittyImageDisplayer(ImageDisplayer): # encode the whole image as base64 # TODO: implement z compression # to possibly increase resolution in sent image - if image.mode != 'RGB' or image.mode != 'RGBA': + if image.mode != 'RGB' and image.mode != 'RGBA': image = image.convert('RGB') # t: transmissium medium, 'd' for embedded # f: size of a pixel fragment (8bytes per color) @@ -551,13 +577,6 @@ class KittyImageDisplayer(ImageDisplayer): bytearray().join(map(bytes, image.getdata()))) else: # put the image in a temporary png file - # we need to find out the encoding for a path string, ascii won't cut it - try: - fsenc = sys.getfilesystemencoding() # returns None if standard utf-8 is used - # throws LookupError if can't find the codec, TypeError if fsenc is None - codecs.lookup(fsenc) - except (LookupError, TypeError): - fsenc = 'utf-8' # t: transmissium medium, 't' for temporary file (kitty will delete it for us) # f: size of a pixel fragment (100 just mean that the file is png encoded, # the only format except raw RGB(A) bitmap that kitty understand) @@ -566,33 +585,33 @@ class KittyImageDisplayer(ImageDisplayer): with NamedTemporaryFile(prefix='rgr_thumb_', suffix='.png', delete=False) as tmpf: image.save(tmpf, format='png', compress_level=0) self.temp_paths.append(tmpf.name) - payload = base64.standard_b64encode(os.path.abspath(tmpf.name).encode(fsenc)) + payload = base64.standard_b64encode(tmpf.name.encode(self.fsenc)) + + with temporarly_moved_cursor(start_y, start_x): + for cmd_str in self._format_cmd_str(cmds, payload=payload): + sys.stdout.buffer.write(cmd_str) + # catch kitty answer before the escape codes corrupt the console + resp = [b''] + while resp[-1] != b'\\': + resp.append(sys.stdin.buffer.read(1)) + if b''.join(resp[-4:-2]) == b'OK': + return + else: + raise ImageDisplayError - self.image_id += 1 - # image handle we'll use with kitty - cmds['i'] = self.image_id - # save current cursor position - curses.putp(curses.tigetstr("sc")) - # we then can move the cursor to our desired spot - # we can't call window.move(y, x) since we don't have the curses win instance - sys.stdout.buffer.write(curses.tparm(curses.tigetstr("cup"), start_y, start_x)) - - # finally send the command - for cmd_str in self._format_cmd_str(cmds, payload=payload): + def clear(self, start_x, start_y, width, height): + # let's assume that every time ranger call this + # it actually wants just to remove the previous image + # TODO: implement this using the actual x, y, since the protocol supports it + cmds = {'a': 'd', 'i': self.image_id} + for cmd_str in self._format_cmd_str(cmds): sys.stdout.buffer.write(cmd_str) sys.stdout.flush() - # to catch the incoming response (which breaks ranger) - # a simple readline doesn't work, but this seems fine - # except when scrolling real fast. If kitty will implemnt a key to suppress - # responses this will be omitted - with self.non_blocking_read() as f_descr: - if select.select([f_descr], [], [], 2 if self.stream else 0.1)[0]: - sys.stdin.buffer.read() # TODO: check if all is well - # Restore cursor - curses.putp(curses.tigetstr("rc")) - sys.stdout.flush() + # kitty doesn't seem to reply on deletes, checking like we do in draw() + # will slows down scrolling with timeouts from select + self.image_id -= 1 - def _format_cmd_str(self, cmd, payload=None, max_l=1024): + def _format_cmd_str(self, cmd, payload=None, max_l=2048): central_blk = ','.join(["{}={}".format(k, v) for k, v in cmd.items()]).encode('ascii') if payload is not None: # we add the m key to signal a multiframe communication @@ -608,36 +627,6 @@ class KittyImageDisplayer(ImageDisplayer): else: yield self.protocol_start + central_blk + b';' + self.protocol_end - @staticmethod - @contextmanager - def non_blocking_read(src=sys.stdin): - # not entirely sure what's going on here - # but sure it looks like tty black magic - f_handle = src.fileno() - if src.isatty(): - old = termios.tcgetattr(f_handle) - tty.setraw(f_handle) - oldfl = fcntl.fcntl(f_handle, fcntl.F_GETFL) - # this seems the juicy part were we actally set the non_blockingness - fcntl.fcntl(f_handle, fcntl.F_SETFL, oldfl | os.O_NONBLOCK) - yield f_handle - # after the with block is done we are resetting back to the old state - if src.isatty(): - termios.tcsetattr(f_handle, termios.TCSADRAIN, old) - fcntl.fcntl(f_handle, fcntl.F_SETFL, oldfl) - - def clear(self, start_x, start_y, width, height): - # let's assume that every time ranger call this - # it actually wants just to remove the previous image - # TODO: implement this using the actual x, y, since the protocol supports it - cmds = {'a': 'd', 'i': self.image_id} - for cmd_str in self._format_cmd_str(cmds): - sys.stdout.buffer.write(cmd_str) - sys.stdout.flush() - # kitty doesn't seem to reply on deletes, checking like we do in draw() - # will slows down scrolling with timeouts from select - self.image_id -= 1 - def quit(self): # clear all remaining images, then check if all files went through or are orphaned while self.image_id >= 1: @@ -645,5 +634,5 @@ class KittyImageDisplayer(ImageDisplayer): while self.temp_paths: try: os.remove(self.temp_paths.pop()) - except FileNotFoundError: + except IOError: continue -- cgit 1.4.1-2-gfad0 From 3c428cad7e752c38a282ba1c83190eb41fc4ff3a Mon Sep 17 00:00:00 2001 From: mark-dawn Date: Sun, 18 Feb 2018 15:06:20 +0100 Subject: Grammar Fixes Fixed grammar horrors in ranger.pod and ranger.conf Improved resize handling --- doc/ranger.pod | 12 ++++-------- ranger/config/rc.conf | 16 +++++++--------- ranger/container/settings.py | 2 +- ranger/core/fm.py | 2 +- ranger/ext/img_display.py | 43 ++++++++++++++++++++++++++----------------- 5 files changed, 39 insertions(+), 36 deletions(-) (limited to 'doc') diff --git a/doc/ranger.pod b/doc/ranger.pod index fbe20012..a45ab2e4 100644 --- a/doc/ranger.pod +++ b/doc/ranger.pod @@ -222,7 +222,7 @@ C and C to the desired values. =head3 terminology -This only works in terminology. It can render vectors graphics, but works only locally. +This only works in terminology. It can render vector graphics, but works only locally. To enable this feature, set the option C to terminology. =head3 urxvt @@ -243,18 +243,14 @@ To enable this feature, set the option C to urxvt-full. =head3 kitty -This only works on Kitty. -It requires PIL or pillow at the moment to work. A nasty bug that can -corrupt the terminal window when scrolling quicly many images, that can -be solved by with the C command. +This only works on Kitty. It requires PIL (or pillow) to work. -To enable this feature, set the option C to kitty +To enable this feature, set the option C to kitty. =head3 kitty-network The same as kitty, but uses a streaming method to allow previews remotely, -for example in an ssh session. However the system is slighly more computationally taxing -and the quality of the preview is reduced for bandwidth considerations. +for example in an ssh session. However the system is more computationally taxing. =head2 SELECTION diff --git a/ranger/config/rc.conf b/ranger/config/rc.conf index cf27cbb5..7adbc239 100644 --- a/ranger/config/rc.conf +++ b/ranger/config/rc.conf @@ -88,7 +88,7 @@ set preview_images false # # * terminology: # Previews images in full color in the terminology terminal emulator. -# Supports a wide variety of formats, even vector graphics like svg +# Supports a wide variety of formats, even vector graphics like svg. # # * urxvt: # Preview images in full color using urxvt image backgrounds. This @@ -99,17 +99,15 @@ set preview_images false # whole terminal window. # # * kitty: -# Preview images in full color using kitty image protocol -# (https://github.com/kovidgoyal/kitty/blob/master/graphics-protocol.asciidoc), +# Preview images in full color using kitty image protocol. # Requires python PIL or pillow library. -# In experimental stage: tmux support is untested, and a scrolling too fast a folder with many images may glitch ranger; -# Future improvements to kitty will ameliorate this issue, for now call the command 'redraw_window' to get rid of the garbage. +# Tmux support is untested. # # * kitty-network -# Similar to base kitty, bit instead of local storage it uses kitty's protocol special feature to -# stream the whole image over standard input. More error prone, and more intensive since it scales down images, -# producing also worse quality previews. -# However it makes possible to see previews froma ranger instance over the network, +# Similar to base kitty, but instead of local storage it streams the whole image +# over standard input. More error prone, +# and more intensive since it base64 encodes entire images. +# However it makes possible to see previews from ranger over the network, # so it makes sense to enable this on remote machines. # Note that has been untested over an actual network. set preview_images_method w3m diff --git a/ranger/container/settings.py b/ranger/container/settings.py index dcadf8bf..b360cf20 100644 --- a/ranger/container/settings.py +++ b/ranger/container/settings.py @@ -100,7 +100,7 @@ ALLOWED_VALUES = { 'confirm_on_delete': ['multiple', 'always', 'never'], 'line_numbers': ['false', 'absolute', 'relative'], 'one_indexed': [False, True], - 'preview_images_method': ['w3m', 'iterm2', 'urxvt', + 'preview_images_method': ['w3m', 'iterm2', 'terminology', 'urxvt', 'urxvt-full', 'kitty', 'kitty-network'], 'vcs_backend_bzr': ['disabled', 'local', 'enabled'], 'vcs_backend_git': ['enabled', 'disabled', 'local'], diff --git a/ranger/core/fm.py b/ranger/core/fm.py index 226b1461..5f4e141d 100644 --- a/ranger/core/fm.py +++ b/ranger/core/fm.py @@ -239,7 +239,7 @@ class FM(Actions, # pylint: disable=too-many-instance-attributes elif self.settings.preview_images_method == "kitty": return KittyImageDisplayer() elif self.settings.preview_images_method == "kitty-network": - return KittyImageDisplayer(stream=True, resize_height=480) + return KittyImageDisplayer(stream=True) return ImageDisplayer() def _get_thisfile(self): diff --git a/ranger/ext/img_display.py b/ranger/ext/img_display.py index 09d3429c..f444e0f9 100644 --- a/ranger/ext/img_display.py +++ b/ranger/ext/img_display.py @@ -11,6 +11,7 @@ implementations, which are currently w3m, iTerm2 and urxvt. from __future__ import (absolute_import, division, print_function) +import math import base64 import curses import errno @@ -42,9 +43,9 @@ W3MIMGDISPLAY_PATHS = [ @contextmanager -def temporarly_moved_cursor(to_y, to_x): +def temporarily_moved_cursor(to_y, to_x): """Common boilerplate code to move the cursor to a drawing area. Use it as: - with temporarly_moved_cursor(dest_y, dest_x): + with temporarily_moved_cursor(dest_y, dest_x): your_func_here()""" curses.putp(curses.tigetstr("sc")) move_cur(to_y, to_x) @@ -236,7 +237,7 @@ class ITerm2ImageDisplayer(ImageDisplayer, FileManagerAware): """ def draw(self, path, start_x, start_y, width, height): - with temporarly_moved_cursor(start_y, start_x): + with temporarily_moved_cursor(start_y, start_x): sys.stdout.write(self._generate_iterm2_input(path, width, height)) def clear(self, start_x, start_y, width, height): @@ -346,7 +347,7 @@ class TerminologyImageDisplayer(ImageDisplayer, FileManagerAware): self.close_protocol = "\000" def draw(self, path, start_x, start_y, width, height): - with temporarly_moved_cursor(start_y, start_x): + with temporarily_moved_cursor(start_y, start_x): # Write intent sys.stdout.write("%s}ic#%d;%d;%s%s" % ( self.display_protocol, @@ -479,13 +480,12 @@ class KittyImageDisplayer(ImageDisplayer): protocol_start = b'\033_G' protocol_end = b'\033\\' - def __init__(self, stream=False, resize_height=720): + def __init__(self, stream=False): self.image_id = 0 self.temp_paths = [] # parameter deciding if we're going to send the picture data # in the command body, or save it to a temporary file self.stream = stream - self.max_height = resize_height if "screen" in os.environ['TERM']: # TODO: probably need to modify the preamble @@ -527,7 +527,7 @@ class KittyImageDisplayer(ImageDisplayer): # to spawn other processes, so it _should_ be faster import PIL.Image self.backend = PIL.Image - self.filter = PIL.Image.BILINEAR + self.filter = PIL.Image.LANCZOS except ImportError: sys.stderr.write("PIL not Found") # TODO: implement a wrapper class for Imagemagick process to @@ -543,24 +543,23 @@ class KittyImageDisplayer(ImageDisplayer): assert self.backend is not None # sanity check if we actually have a backend image = self.backend.open(path) - aspect = image.width / image.height - # first let's reduce the size of the image - if image.height > self.max_height: - image = image.resize((int(self.max_height * aspect), self.max_height), self.filter) - # since kitty streches the image to fill the view box # we need to resize the box to not get distortion - mismatch_ratio = aspect / (width * 0.5 / height) + mismatch_ratio = (image.width / image.height) / (width * 0.5 / height) if mismatch_ratio > 1.05: new_h = height / mismatch_ratio - start_y += int((height - new_h) / 2) + start_y += int((height - new_h) // 2) height = int(new_h) elif mismatch_ratio < 0.95: new_w = width * mismatch_ratio - start_x += int((width - new_w) / 2) + start_x += int((width - new_w) // 2) width = int(new_w) + # resize image to a smaller size. Ideally this should be + # image = self._resize_max_area(image, (480*960), self.filter) + if self.stream: + image = self._resize_max_area(image, (480 * 960), self.filter) # encode the whole image as base64 # TODO: implement z compression # to possibly increase resolution in sent image @@ -581,13 +580,14 @@ class KittyImageDisplayer(ImageDisplayer): # f: size of a pixel fragment (100 just mean that the file is png encoded, # the only format except raw RGB(A) bitmap that kitty understand) # c, r: size in cells of the viewbox - cmds.update({'t': 't', 'f': 100, 'c': width, 'r': height}) + cmds.update({'t': 't', 'f': 100, + 'c': width, 'r': height}) with NamedTemporaryFile(prefix='rgr_thumb_', suffix='.png', delete=False) as tmpf: image.save(tmpf, format='png', compress_level=0) self.temp_paths.append(tmpf.name) payload = base64.standard_b64encode(tmpf.name.encode(self.fsenc)) - with temporarly_moved_cursor(start_y, start_x): + with temporarily_moved_cursor(start_y, start_x): for cmd_str in self._format_cmd_str(cmds, payload=payload): sys.stdout.buffer.write(cmd_str) # catch kitty answer before the escape codes corrupt the console @@ -627,6 +627,15 @@ class KittyImageDisplayer(ImageDisplayer): else: yield self.protocol_start + central_blk + b';' + self.protocol_end + @staticmethod + def _resize_max_area(image, max_area, img_filter): + aspect = image.width / image.height + area = image.width * image.height + if area > max_area: + image = image.resize((int(math.sqrt(area * aspect)), + int(math.sqrt(area / aspect))), img_filter) + return image + def quit(self): # clear all remaining images, then check if all files went through or are orphaned while self.image_id >= 1: -- cgit 1.4.1-2-gfad0 From 5bde5d533557968deda1e37db49d9d8650925786 Mon Sep 17 00:00:00 2001 From: mark-dawn Date: Tue, 20 Feb 2018 02:46:42 +0100 Subject: Automatic network detection, python2 and Exception fixes Created late_init method to handle task dependent on late facilities Enabled automatic network detection (required stdin to be properly init) Fixed proper handling of import Errors (required ranger Exception handling) Fixed handling of binary stdio differnces between py2/3 Unified said handling aross the module --- doc/ranger.pod | 7 +-- ranger/config/rc.conf | 12 ++--- ranger/container/settings.py | 4 +- ranger/core/fm.py | 2 - ranger/ext/img_display.py | 110 ++++++++++++++++++++++++------------------- 5 files changed, 70 insertions(+), 65 deletions(-) (limited to 'doc') diff --git a/doc/ranger.pod b/doc/ranger.pod index a45ab2e4..887f704d 100644 --- a/doc/ranger.pod +++ b/doc/ranger.pod @@ -244,14 +244,11 @@ To enable this feature, set the option C to urxvt-full. =head3 kitty This only works on Kitty. It requires PIL (or pillow) to work. +It is able to automatically work over network, +by switching to a slower transfer method. To enable this feature, set the option C to kitty. -=head3 kitty-network - -The same as kitty, but uses a streaming method to allow previews remotely, -for example in an ssh session. However the system is more computationally taxing. - =head2 SELECTION The I is defined as "All marked files IF THERE ARE ANY, otherwise diff --git a/ranger/config/rc.conf b/ranger/config/rc.conf index 7adbc239..305143c1 100644 --- a/ranger/config/rc.conf +++ b/ranger/config/rc.conf @@ -101,15 +101,11 @@ set preview_images false # * kitty: # Preview images in full color using kitty image protocol. # Requires python PIL or pillow library. +# If ranger does not share the local filesystem with kitty +# the transfer method is switched to encoding the whole image in +# the protocol that, while slower, allows remote previews, +# for example during an ssh session. # Tmux support is untested. -# -# * kitty-network -# Similar to base kitty, but instead of local storage it streams the whole image -# over standard input. More error prone, -# and more intensive since it base64 encodes entire images. -# However it makes possible to see previews from ranger over the network, -# so it makes sense to enable this on remote machines. -# Note that has been untested over an actual network. set preview_images_method w3m # Default iTerm2 font size (see: preview_images_method: iterm2) diff --git a/ranger/container/settings.py b/ranger/container/settings.py index b360cf20..b9695301 100644 --- a/ranger/container/settings.py +++ b/ranger/container/settings.py @@ -100,8 +100,8 @@ ALLOWED_VALUES = { 'confirm_on_delete': ['multiple', 'always', 'never'], 'line_numbers': ['false', 'absolute', 'relative'], 'one_indexed': [False, True], - 'preview_images_method': ['w3m', 'iterm2', 'terminology', 'urxvt', - 'urxvt-full', 'kitty', 'kitty-network'], + 'preview_images_method': ['w3m', 'iterm2', 'terminology', + 'urxvt', 'urxvt-full', 'kitty'], '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 5f4e141d..f3a19f0a 100644 --- a/ranger/core/fm.py +++ b/ranger/core/fm.py @@ -238,8 +238,6 @@ 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 == "kitty-network": - return KittyImageDisplayer(stream=True) return ImageDisplayer() def _get_thisfile(self): diff --git a/ranger/ext/img_display.py b/ranger/ext/img_display.py index f444e0f9..387d61a3 100644 --- a/ranger/ext/img_display.py +++ b/ranger/ext/img_display.py @@ -57,10 +57,9 @@ def temporarily_moved_cursor(to_y, to_x): # this is excised since Terminology needs to move the cursor multiple times def move_cur(to_y, to_x): tparm = curses.tparm(curses.tigetstr("cup"), to_y, to_x) - if sys.version_info[0] < 3: - sys.stdout.write(tparm) - else: - sys.stdout.buffer.write(tparm) + # on python2 stdout is already in binary mode, in python3 is accessed via buffer + bin_stdout = getattr(sys.stdout, 'buffer', sys.stdout) + bin_stdout.write(tparm) class ImageDisplayError(Exception): @@ -479,60 +478,72 @@ class KittyImageDisplayer(ImageDisplayer): https://github.com/kovidgoyal/kitty/blob/master/graphics-protocol.asciidoc""" protocol_start = b'\033_G' protocol_end = b'\033\\' + # we are going to use stdio in binary mode a lot, so due to py2 -> py3 + # differnces is worth to do this: + stdbout = getattr(sys.stdout, 'buffer', sys.stdout) + stdbin = getattr(sys.stdin, 'buffer', sys.stdin) + # counter for image ids on kitty's end + image_id = 0 + # we need to find out the encoding for a path string, ascii won't cut it + try: + fsenc = sys.getfilesystemencoding() # returns None if standard utf-8 is used + # throws LookupError if can't find the codec, TypeError if fsenc is None + codecs.lookup(fsenc) + except (LookupError, TypeError): + fsenc = 'utf-8' - def __init__(self, stream=False): - self.image_id = 0 + def __init__(self): self.temp_paths = [] - # parameter deciding if we're going to send the picture data - # in the command body, or save it to a temporary file - self.stream = stream if "screen" in os.environ['TERM']: # TODO: probably need to modify the preamble pass - # we need to find out the encoding for a path string, ascii won't cut it - try: - self.fsenc = sys.getfilesystemencoding() # returns None if standard utf-8 is used - # throws LookupError if can't find the codec, TypeError if fsenc is None - codecs.lookup(self.fsenc) - except (LookupError, TypeError): - self.fsenc = 'utf-8' + # the rest of the initializations that require reading stdio or raising exceptions + # are delayed to the first draw call, since curses + # and ranger exception handler are not online at __init__() time + self.needs_late_init = True + # to init in _late_init() + self.backend = None + self.stream = None + def _late_init(self): # automatic check if we share the filesystem using a dummy file - # TODO: this doesn't work somehow, the response from kitty appears on - # the tty, and until a newline is inserted the data won't be sent - # to the stdin we have. This works just fine in draw. Something tells me that since this is - # called early the tubes are not set up correctly yet, but I have no idea how to fix it - # - # with NamedTemporaryFile() as tmpf: - # tmpf.write(bytearray([0xFA]*3)) - # tmpf.flush() - # for cmd in self._format_cmd_str({'i': 1, 'f': 24,'t': 'f', 's': 1, 'v': 1, 'S': 3}, - # payload=base64.standard_b64encode(tmpf.name.encode(self.fsenc))): - # sys.stdout.buffer.write(cmd) - # resp = [b''] - # sys.stdout.flush() - # for _ in range(5): - # while resp[-1] != b'\\': - # resp.append(sys.stdin.buffer.read(1)) - # if b''.join(resp[-4:-2]) == b'OK': - # self.stream = False - # else: - # self.stream = True + with NamedTemporaryFile() as tmpf: + tmpf.write(bytearray([0xFF] * 3)) + tmpf.flush() + for cmd in self._format_cmd_str( + {'a': 'q', 'i': 1, 'f': 24, 't': 'f', 's': 1, 'v': 1, 'S': 3}, + payload=base64.standard_b64encode(tmpf.name.encode(self.fsenc))): + self.stdbout.write(cmd) + sys.stdout.flush() + resp = [b''] + while resp[-1] != b'\\': + resp.append(self.stdbin.read(1)) + # set the transfer method based on the response + resp = str(b''.join(resp)) + if resp.find('OK') != -1: + self.stream = False + elif resp.find('EBADF') != -1: + self.stream = True + else: + raise ImgDisplayUnsupportedException( + 'kitty replied an unexpected response: {}' + .format(resp)) - self.backend = None + # get the image manipulation backend try: # pillow is the default since we are not going # to spawn other processes, so it _should_ be faster import PIL.Image self.backend = PIL.Image - self.filter = PIL.Image.LANCZOS except ImportError: - sys.stderr.write("PIL not Found") + raise ImageDisplayError("Images previews in kitty require PIL (pillow)") # TODO: implement a wrapper class for Imagemagick process to # replicate the functionality we use from im + self.needs_late_init = False + def draw(self, path, start_x, start_y, width, height): # pylint: disable=too-many-locals self.image_id += 1 # dictionary to store the command arguments for kitty @@ -541,7 +552,10 @@ class KittyImageDisplayer(ImageDisplayer): cmds = {'a': 'T', 'i': self.image_id} # sys.stderr.write('{}-{}@{}x{}\t'.format(start_x, start_y, width, height)) - assert self.backend is not None # sanity check if we actually have a backend + # finish initialization if it is the first call + if self.needs_late_init: + self._late_init() + image = self.backend.open(path) # since kitty streches the image to fill the view box # we need to resize the box to not get distortion @@ -556,10 +570,10 @@ class KittyImageDisplayer(ImageDisplayer): width = int(new_w) # resize image to a smaller size. Ideally this should be - # image = self._resize_max_area(image, (480*960), self.filter) + # image = self._resize_max_area(image, (), self.backend.LANCZOS) if self.stream: - image = self._resize_max_area(image, (480 * 960), self.filter) + image = self._resize_max_area(image, (480 * 960), self.backend.LANCZOS) # encode the whole image as base64 # TODO: implement z compression # to possibly increase resolution in sent image @@ -589,15 +603,15 @@ class KittyImageDisplayer(ImageDisplayer): with temporarily_moved_cursor(start_y, start_x): for cmd_str in self._format_cmd_str(cmds, payload=payload): - sys.stdout.buffer.write(cmd_str) + self.stdbout.write(cmd_str) # catch kitty answer before the escape codes corrupt the console resp = [b''] while resp[-1] != b'\\': - resp.append(sys.stdin.buffer.read(1)) - if b''.join(resp[-4:-2]) == b'OK': + resp.append(self.stdbin.read(1)) + if str(b''.join(resp)).find('OK'): return else: - raise ImageDisplayError + raise ImageDisplayError('kitty replied "{}"'.format(b''.join(resp))) def clear(self, start_x, start_y, width, height): # let's assume that every time ranger call this @@ -605,8 +619,8 @@ class KittyImageDisplayer(ImageDisplayer): # TODO: implement this using the actual x, y, since the protocol supports it cmds = {'a': 'd', 'i': self.image_id} for cmd_str in self._format_cmd_str(cmds): - sys.stdout.buffer.write(cmd_str) - sys.stdout.flush() + self.stdbout.write(cmd_str) + self.stdbout.flush() # kitty doesn't seem to reply on deletes, checking like we do in draw() # will slows down scrolling with timeouts from select self.image_id -= 1 -- cgit 1.4.1-2-gfad0 From d33cc0ff06f761a1919c9c7508d8c3ffe88364ad Mon Sep 17 00:00:00 2001 From: mark-dawn Date: Thu, 22 Feb 2018 05:23:46 +0100 Subject: Unstretched Image Previews Implemented a way to obtain unstretched image previews Fixed wording in ranger.pod --- doc/ranger.pod | 3 +-- ranger/config/rc.conf | 2 +- ranger/ext/img_display.py | 39 ++++++++++++++++++--------------------- 3 files changed, 20 insertions(+), 24 deletions(-) (limited to 'doc') diff --git a/doc/ranger.pod b/doc/ranger.pod index 887f704d..46ca920e 100644 --- a/doc/ranger.pod +++ b/doc/ranger.pod @@ -244,8 +244,7 @@ To enable this feature, set the option C to urxvt-full. =head3 kitty This only works on Kitty. It requires PIL (or pillow) to work. -It is able to automatically work over network, -by switching to a slower transfer method. +Allows remote image previews, for example in an ssh session. To enable this feature, set the option C to kitty. diff --git a/ranger/config/rc.conf b/ranger/config/rc.conf index 305143c1..165e049b 100644 --- a/ranger/config/rc.conf +++ b/ranger/config/rc.conf @@ -102,7 +102,7 @@ set preview_images false # Preview images in full color using kitty image protocol. # Requires python PIL or pillow library. # If ranger does not share the local filesystem with kitty -# the transfer method is switched to encoding the whole image in +# the transfer method is switched to encode the whole image in # the protocol that, while slower, allows remote previews, # for example during an ssh session. # Tmux support is untested. diff --git a/ranger/ext/img_display.py b/ranger/ext/img_display.py index 387d61a3..925a4054 100644 --- a/ranger/ext/img_display.py +++ b/ranger/ext/img_display.py @@ -506,6 +506,7 @@ class KittyImageDisplayer(ImageDisplayer): # to init in _late_init() self.backend = None self.stream = None + self.pix_row, self.pix_col = (0, 0) def _late_init(self): # automatic check if we share the filesystem using a dummy file @@ -542,9 +543,14 @@ class KittyImageDisplayer(ImageDisplayer): # TODO: implement a wrapper class for Imagemagick process to # replicate the functionality we use from im + # get dimensions of a cell in pixels + ret = fcntl.ioctl(sys.stdout, termios.TIOCGWINSZ, + struct.pack('HHHH', 0, 0, 0, 0)) + n_cols, n_rows, x_px_tot, y_px_tot = struct.unpack('HHHH', ret) + self.pix_row, self.pix_col = x_px_tot / n_rows, y_px_tot / n_cols self.needs_late_init = False - def draw(self, path, start_x, start_y, width, height): # pylint: disable=too-many-locals + def draw(self, path, start_x, start_y, width, height): self.image_id += 1 # dictionary to store the command arguments for kitty # a is the display command, with T going for immediate output @@ -557,23 +563,16 @@ class KittyImageDisplayer(ImageDisplayer): self._late_init() image = self.backend.open(path) - # since kitty streches the image to fill the view box - # we need to resize the box to not get distortion - mismatch_ratio = (image.width / image.height) / (width * 0.5 / height) - if mismatch_ratio > 1.05: - new_h = height / mismatch_ratio - start_y += int((height - new_h) // 2) - height = int(new_h) - elif mismatch_ratio < 0.95: - new_w = width * mismatch_ratio - start_x += int((width - new_w) // 2) - width = int(new_w) - - # resize image to a smaller size. Ideally this should be - # image = self._resize_max_area(image, (), self.backend.LANCZOS) + box = (width * self.pix_row, height * self.pix_col) + + if image.width > box[0] or image.height > box[1]: + scale = min(box[0] / image.width, box[1] / image.height) + image = image.resize((int(scale * image.width), int(scale * image.height)), + self.backend.LANCZOS) + start_x += ((box[0] - image.width) // 2) // self.pix_row + start_y += ((box[1] - image.height) // 2) // self.pix_col if self.stream: - image = self._resize_max_area(image, (480 * 960), self.backend.LANCZOS) # encode the whole image as base64 # TODO: implement z compression # to possibly increase resolution in sent image @@ -584,8 +583,7 @@ class KittyImageDisplayer(ImageDisplayer): # s, v: size of the image to recompose the flattened data # c, r: size in cells of the viewbox cmds.update({'t': 'd', 'f': len(image.getbands()) * 8, - 's': image.width, 'v': image.height, - 'c': width, 'r': height}) + 's': image.width, 'v': image.height, }) payload = base64.standard_b64encode( bytearray().join(map(bytes, image.getdata()))) else: @@ -594,14 +592,13 @@ class KittyImageDisplayer(ImageDisplayer): # f: size of a pixel fragment (100 just mean that the file is png encoded, # the only format except raw RGB(A) bitmap that kitty understand) # c, r: size in cells of the viewbox - cmds.update({'t': 't', 'f': 100, - 'c': width, 'r': height}) + cmds.update({'t': 't', 'f': 100, }) with NamedTemporaryFile(prefix='rgr_thumb_', suffix='.png', delete=False) as tmpf: image.save(tmpf, format='png', compress_level=0) self.temp_paths.append(tmpf.name) payload = base64.standard_b64encode(tmpf.name.encode(self.fsenc)) - with temporarily_moved_cursor(start_y, start_x): + with temporarily_moved_cursor(int(start_y), int(start_x)): for cmd_str in self._format_cmd_str(cmds, payload=payload): self.stdbout.write(cmd_str) # catch kitty answer before the escape codes corrupt the console -- cgit 1.4.1-2-gfad0 From 8730f1ec6e20048f04298f866d4ed8b23836db57 Mon Sep 17 00:00:00 2001 From: mark-dawn Date: Fri, 23 Feb 2018 23:55:54 +0100 Subject: Fixed manpages, tmux handling Updated manapages Corrections to rc.conf Silenced the warning from pillow about image sizes Tmux session with kitty raises ImgDisplayUnsupportedError changed pager.py to also notify on ImgDispalyUnsupported Error --- doc/ranger.1 | 20 ++++++-------------- doc/ranger.pod | 1 + doc/rifle.1 | 4 ++-- ranger/config/rc.conf | 6 +++--- ranger/ext/img_display.py | 22 ++++++++++++++++------ ranger/gui/widgets/pager.py | 3 ++- 6 files changed, 30 insertions(+), 26 deletions(-) (limited to 'doc') diff --git a/doc/ranger.1 b/doc/ranger.1 index a0fc79a1..8d373a64 100644 --- a/doc/ranger.1 +++ b/doc/ranger.1 @@ -129,7 +129,7 @@ .\" ======================================================================== .\" .IX Title "RANGER 1" -.TH RANGER 1 "ranger-1.9.1" "2018-05-14" "ranger manual" +.TH RANGER 1 "ranger-1.9.1" "05/29/2018" "ranger manual" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l @@ -310,7 +310,8 @@ width of 8 and height of 11 are used. To use other values, set the options \fIterminology\fR .IX Subsection "terminology" .PP -This only works in terminology. It can render vectors graphics, but works only locally. +This only works in terminology. It can render vector graphics, but works only locally. +.PP To enable this feature, set the option \f(CW\*(C`preview_images_method\*(C'\fR to terminology. .PP \fIurxvt\fR @@ -334,19 +335,10 @@ To enable this feature, set the option \f(CW\*(C`preview_images_method\*(C'\fR t \fIkitty\fR .IX Subsection "kitty" .PP -This only works on Kitty. -It requires \s-1PIL\s0 or pillow at the moment to work. A nasty bug that can -corrupt the terminal window when scrolling quicly many images, that can -be solved by with the \f(CW\*(C`redraw_window\*(C'\fR command. -.PP -To enable this feature, set the option \f(CW\*(C`preview_images_method\*(C'\fR to kitty -.PP -\fIkitty-network\fR -.IX Subsection "kitty-network" +This only works on Kitty. It requires \s-1PIL\s0 (or pillow) to work. +Allows remote image previews, for example in an ssh session. .PP -The same as kitty, but uses a streaming method to allow previews remotely, -for example in an ssh session. However the system is slighly more computationally taxing -and the quality of the preview is reduced for bandwidth considerations. +To enable this feature, set the option \f(CW\*(C`preview_images_method\*(C'\fR to kitty. .SS "\s-1SELECTION\s0" .IX Subsection "SELECTION" The \fIselection\fR is defined as \*(L"All marked files \s-1IF THERE ARE ANY,\s0 otherwise diff --git a/doc/ranger.pod b/doc/ranger.pod index 46ca920e..b262ad44 100644 --- a/doc/ranger.pod +++ b/doc/ranger.pod @@ -223,6 +223,7 @@ C and C to the desired values. =head3 terminology This only works in terminology. It can render vector graphics, but works only locally. + To enable this feature, set the option C to terminology. =head3 urxvt diff --git a/doc/rifle.1 b/doc/rifle.1 index ad32e4f4..c53a6f39 100644 --- a/doc/rifle.1 +++ b/doc/rifle.1 @@ -1,4 +1,4 @@ -.\" Automatically generated by Pod::Man 4.07 (Pod::Simple 3.32) +.\" Automatically generated by Pod::Man 4.09 (Pod::Simple 3.35) .\" .\" Standard preamble: .\" ======================================================================== @@ -129,7 +129,7 @@ .\" ======================================================================== .\" .IX Title "RIFLE 1" -.TH RIFLE 1 "rifle-1.9.1" "05.03.2018" "rifle manual" +.TH RIFLE 1 "rifle-1.9.1" "05/29/2018" "rifle manual" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l diff --git a/ranger/config/rc.conf b/ranger/config/rc.conf index 165e049b..5317dff5 100644 --- a/ranger/config/rc.conf +++ b/ranger/config/rc.conf @@ -102,10 +102,10 @@ set preview_images false # Preview images in full color using kitty image protocol. # Requires python PIL or pillow library. # If ranger does not share the local filesystem with kitty -# the transfer method is switched to encode the whole image in -# the protocol that, while slower, allows remote previews, +# the transfer method is changed to encode the whole image; +# while slower, this allows remote previews, # for example during an ssh session. -# Tmux support is untested. +# Tmux is unsupported. set preview_images_method w3m # Default iTerm2 font size (see: preview_images_method: iterm2) diff --git a/ranger/ext/img_display.py b/ranger/ext/img_display.py index 925a4054..17c730df 100644 --- a/ranger/ext/img_display.py +++ b/ranger/ext/img_display.py @@ -20,6 +20,7 @@ import imghdr import os import struct import sys +import warnings from subprocess import Popen, PIPE import termios @@ -495,10 +496,6 @@ class KittyImageDisplayer(ImageDisplayer): def __init__(self): self.temp_paths = [] - if "screen" in os.environ['TERM']: - # TODO: probably need to modify the preamble - pass - # the rest of the initializations that require reading stdio or raising exceptions # are delayed to the first draw call, since curses # and ranger exception handler are not online at __init__() time @@ -509,6 +506,14 @@ class KittyImageDisplayer(ImageDisplayer): self.pix_row, self.pix_col = (0, 0) def _late_init(self): + # tmux + if "screen" in os.environ['TERM']: + # this doesn't seem to work, ranger freezes... + # commenting out the response check does nothing + # self.protocol_start = b'\033Ptmux;\033' + self.protocol_start + # self.protocol_end += b'\033\\' + raise ImgDisplayUnsupportedException('tmux support is not implemented with kitty') + # automatic check if we share the filesystem using a dummy file with NamedTemporaryFile() as tmpf: tmpf.write(bytearray([0xFF] * 3)) @@ -561,8 +566,13 @@ class KittyImageDisplayer(ImageDisplayer): # finish initialization if it is the first call if self.needs_late_init: self._late_init() - - image = self.backend.open(path) + with warnings.catch_warnings(record=True): # as warn: + warnings.simplefilter('ignore', self.backend.DecompressionBombWarning) + image = self.backend.open(path) + # TODO: find a way to send a message to the user that + # doesn't stop the image from displaying + # if warn: + # raise ImageDisplayError(str(warn[-1].message)) box = (width * self.pix_row, height * self.pix_col) if image.width > box[0] or image.height > box[1]: diff --git a/ranger/gui/widgets/pager.py b/ranger/gui/widgets/pager.py index 42adf1e9..9afbfd15 100644 --- a/ranger/gui/widgets/pager.py +++ b/ranger/gui/widgets/pager.py @@ -109,8 +109,9 @@ class Pager(Widget): # pylint: disable=too-many-instance-attributes try: self.fm.image_displayer.draw(self.image, self.x, self.y, self.wid, self.hei) - except ImgDisplayUnsupportedException: + except ImgDisplayUnsupportedException as ex: self.fm.settings.preview_images = False + self.fm.notify(ex, bad=True) except Exception as ex: # pylint: disable=broad-except self.fm.notify(ex, bad=True) else: -- cgit 1.4.1-2-gfad0