diff --git a/kittens/diff/images.py b/kittens/diff/images.py index 273d0957f..2da6fc13c 100644 --- a/kittens/diff/images.py +++ b/kittens/diff/images.py @@ -2,9 +2,10 @@ # vim:fileencoding=utf-8 # License: GPL v3 Copyright: 2018, Kovid Goyal -import shutil import warnings +from ..tui.images import can_display_images + class ImageSupportWarning(Warning): pass @@ -13,8 +14,7 @@ class ImageSupportWarning(Warning): def images_supported(): ans = getattr(images_supported, 'ans', None) if ans is None: - ans = shutil.which('convert') is not None - images_supported.ans = ans + images_supported.ans = ans = can_display_images() if not ans: warnings.warn('ImageMagick not found images cannot be displayed', ImageSupportWarning) return ans diff --git a/kittens/diff/main.py b/kittens/diff/main.py index 03bd12117..34cb53084 100644 --- a/kittens/diff/main.py +++ b/kittens/diff/main.py @@ -15,6 +15,7 @@ from kitty.key_encoding import ( ) from ..tui.handler import Handler +from ..tui.images import ImageManager from ..tui.loop import Loop from .collect import create_collection, data_for_path, set_highlight_data from .config import init_config @@ -45,6 +46,8 @@ def generate_diff(collection, context): class DiffHandler(Handler): + image_manager_class = ImageManager + def __init__(self, args, opts, left, right): self.state = INITIALIZING self.opts = opts diff --git a/kittens/tui/handler.py b/kittens/tui/handler.py index 8a579ae07..e5a64de2c 100644 --- a/kittens/tui/handler.py +++ b/kittens/tui/handler.py @@ -8,18 +8,25 @@ from kittens.tui.operations import commander class Handler: - def _initialize(self, screen_size, quit_loop, wakeup, start_job): + image_manager_class = None + + def _initialize(self, screen_size, quit_loop, wakeup, start_job, image_manager=None): self.screen_size, self.quit_loop = screen_size, quit_loop self.wakeup = wakeup self.start_job = start_job self.cmd = commander(self) + self.image_manager = image_manager def __enter__(self): + if self.image_manager is not None: + self.image_manager.__enter__() self.initialize() def __exit__(self, *a): del self.write_buf[:] self.finalize() + if self.image_manager is not None: + self.image_manager.__exit__(*a) def initialize(self): pass diff --git a/kittens/tui/images.py b/kittens/tui/images.py new file mode 100644 index 000000000..8c951075f --- /dev/null +++ b/kittens/tui/images.py @@ -0,0 +1,65 @@ +#!/usr/bin/env python +# vim:fileencoding=utf-8 +# License: GPL v3 Copyright: 2018, Kovid Goyal + +import codecs +import shutil +import sys +import tempfile +from base64 import standard_b64encode + +from .operations import serialize_gr_command + +try: + fsenc = sys.getfilesystemencoding() or 'utf-8' + codecs.lookup(fsenc) +except Exception: + fsenc = 'utf-8' + + +def can_display_images(): + ans = getattr(can_display_images, 'ans', None) + if ans is None: + ans = shutil.which('convert') is not None + can_display_images.ans = ans + return ans + + +class ImageManager: + + def __init__(self, handler): + self.handler = handler + self.sent_ids = set() + self.filesystem_ok = None + + def __enter__(self): + self.tdir = tempfile.mkdtemp(prefix='kitten-images-') + with tempfile.NamedTemporaryFile(dir=self.tdir, delete=False) as f: + f.write(b'abcd') + self.handler.write(serialize_gr_command(dict(a='q', s=1, v=1, i=1, t='f'), standard_b64encode(f.name.encode(fsenc)))) + self.sent_ids.add(1) + + def __exit__(self, *a): + shutil.rmtree(self.tdir, ignore_errors=True) + self.handler.cmd.clear_images_on_screen(delete_data=True) + self.delete_all_sent_images() + del self.handler + + def delete_all_sent_images(self): + for img_id in self.sent_ids: + self.handler.write(serialize_gr_command({'a': 'D', 'i': str(img_id)})) + self.sent_ids = set() + + def handle_response(self, apc): + cdata, payload = apc[1:].partition(';')[::2] + control = {} + for x in cdata.split(','): + k, v = x.partition('=')[::2] + control[k] = v + try: + image_id = int(control.get('i', '0')) + except Exception: + image_id = 0 + if image_id == 1: + self.filesystem_ok = payload == 'OK' + return diff --git a/kittens/tui/loop.py b/kittens/tui/loop.py index d9683e32c..392439d68 100644 --- a/kittens/tui/loop.py +++ b/kittens/tui/loop.py @@ -27,7 +27,6 @@ from kitty.utils import screen_size_function from .handler import Handler from .operations import clear_screen, init_state, reset_state - screen_size = screen_size_function() @@ -271,6 +270,9 @@ class Loop: self.handler.on_eot() return self.handler.on_key(k) + elif apc.startswith('G'): + if self.handler.image_manager is not None: + self.handler.image_manager.handle_response(apc) def _write_ready(self, handler): if len(handler.write_buf) > self.iov_limit: @@ -359,9 +361,12 @@ class Loop: signal.signal(signal.SIGINT, self._on_sigint) handler.write_buf = [] handler._term_manager = term_manager + image_manager = None + if handler.image_manager_class is not None: + image_manager = handler.image_manager_class(handler) keep_going = True try: - handler._initialize(screen_size(), self.quit, self.wakeup, self.start_job) + handler._initialize(screen_size(), self.quit, self.wakeup, self.start_job, image_manager) with handler: while keep_going: has_data_to_write = bool(handler.write_buf) diff --git a/kittens/tui/operations.py b/kittens/tui/operations.py index 4fe017cb7..962456b09 100644 --- a/kittens/tui/operations.py +++ b/kittens/tui/operations.py @@ -149,6 +149,22 @@ def styled(text, fg=None, bg=None, fg_intense=False, bg_intense=False, italic=No return '\033[{}m{}\033[{}m'.format(';'.join(start), text, ';'.join(end)) +def serialize_gr_command(cmd, payload=None): + cmd = ','.join('{}={}'.format(k, v) for k, v in cmd.items()) + ans = [] + w = ans.append + w(b'\033_G'), w(cmd.encode('ascii')) + if payload: + w(b';') + w(payload) + w(b'\033\\') + return b''.join(ans) + + +def clear_images_on_screen(delete_data=False) -> str: + return serialize_gr_command({'a': 'D' if delete_data else 'd'}) + + def init_state(alternate_screen=True): ans = ( S7C1T + SAVE_CURSOR + SAVE_PRIVATE_MODE_VALUES + reset_mode('LNM') +