Get rid of kitty's special OSC 52 protocol

A better solution from an ecosystem perspective is to just work with the
original protocol. I have modified kitty's escape parser to special case
OSC 52 handling without changing its max escape code size.

Basically, it works by splitting up OSC 52 escape codes longer than the
max size into a series of partial OSC 52 escape codes. These get
dispatched to the UI layer where it accumulates them upto the 8MB limit
and then sends to clipboard when the partial sequence ends.

See https://github.com/ranger/ranger/issues/1861
This commit is contained in:
Kovid Goyal
2021-07-23 22:12:04 +05:30
parent 096c4c78c7
commit 8f214c51c0
12 changed files with 147 additions and 71 deletions

View File

@@ -12,8 +12,8 @@ from functools import partial
from gettext import gettext as _
from itertools import chain
from typing import (
Any, Callable, Deque, Dict, Iterable, List, Optional, Pattern, Sequence,
Tuple, Union
Any, Callable, Deque, Dict, Iterable, List, NamedTuple, Optional, Pattern,
Sequence, Tuple, Union
)
from .child import ProcessDesc
@@ -72,6 +72,11 @@ class PipeData(TypedDict):
text: str
class ClipboardPending(NamedTuple):
where: str
data: str
class DynamicColor(IntEnum):
default_fg, default_bg, cursor_color, highlight_fg, highlight_bg = range(1, 6)
@@ -337,7 +342,7 @@ class Window:
self.tab_id = tab.id
self.os_window_id = tab.os_window_id
self.tabref: Callable[[], Optional[TabType]] = weakref.ref(tab)
self.clipboard_control_buffers = {'p': '', 'c': ''}
self.clipboard_pending: Optional[ClipboardPending] = None
self.destroyed = False
self.geometry: WindowGeometry = WindowGeometry(0, 0, 0, 0, 0, 0)
self.needs_layout = True
@@ -777,10 +782,25 @@ class Window:
def send_cmd_response(self, response: Any) -> None:
self.screen.send_escape_code_to_child(DCS, '@kitty-cmd' + json.dumps(response))
def clipboard_control(self, data: str) -> None:
def clipboard_control(self, data: str, is_partial: bool = False) -> None:
where, text = data.partition(';')[::2]
if is_partial:
if self.clipboard_pending is None:
self.clipboard_pending = ClipboardPending(where, text)
else:
self.clipboard_pending = self.clipboard_pending._replace(data=self.clipboard_pending[1] + text)
if len(self.clipboard_pending.data) > 8 * 1024 * 1024:
log_error('Discarding part of too large OSC 52 paste request')
self.clipboard_pending = self.clipboard_pending._replace(data='')
return
if not where:
where = 's0'
if self.clipboard_pending is not None:
text = self.clipboard_pending.data + text
where = self.clipboard_pending.where
self.clipboard_pending = None
else:
where = 's0'
cc = get_options().clipboard_control
if text == '?':
response = None
@@ -802,22 +822,13 @@ class Window:
except Exception:
text = ''
def write(key: str, func: Callable[[str], None]) -> None:
if text:
if ('no-append' in cc or
len(self.clipboard_control_buffers[key]) > 1024*1024):
self.clipboard_control_buffers[key] = ''
self.clipboard_control_buffers[key] += text
else:
self.clipboard_control_buffers[key] = ''
func(self.clipboard_control_buffers[key])
if 's' in where or 'c' in where:
if 'write-clipboard' in cc:
write('c', set_clipboard_string)
set_clipboard_string(text)
if 'p' in where:
if 'write-primary' in cc:
write('p', set_primary_selection)
set_primary_selection(text)
self.clipboard_pending = None
def manipulate_title_stack(self, pop: bool, title: str, icon: Any) -> None:
if title: