diff: More work on images

This commit is contained in:
Kovid Goyal 2018-05-09 14:06:15 +05:30
parent 79dcc8c771
commit 2e69b904da
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
6 changed files with 102 additions and 6 deletions

View File

@ -2,9 +2,10 @@
# vim:fileencoding=utf-8
# License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>
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

View File

@ -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

View File

@ -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

65
kittens/tui/images.py Normal file
View File

@ -0,0 +1,65 @@
#!/usr/bin/env python
# vim:fileencoding=utf-8
# License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>
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

View File

@ -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)

View File

@ -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') +