Report support for OSC52 write to clipboard in DA1

There are apparently some applications that want to only turn on OSC52
if they can be sure the terminal supports it.
https://github.com/contour-terminal/vt-extensions/blob/master/clipboard-extension.md

Seems harmless enough, though IMO the correct query mechanism for
runtime controllable settings is XTGETTCAP, but, let's be a
good citizen and co-operate. The overhead is not too large and I
have more important windmills to tilt at.

Fixes #8788
This commit is contained in:
Kovid Goyal 2025-07-07 08:45:58 +05:30
parent b6b027802e
commit eabddc2870
No known key found for this signature in database
GPG key ID: 06BC317B515ACE7C
4 changed files with 27 additions and 2 deletions

View file

@ -2647,7 +2647,7 @@ report_device_attributes(Screen *self, unsigned int mode, char start_modifier) {
if (mode == 0) {
switch(start_modifier) {
case 0:
write_escape_code_to_child(self, ESC_CSI, "?62;c");
CALLBACK("on_da1", NULL);
break;
case '>':
write_escape_code_to_child(self, ESC_CSI, ">1;" xstr(PRIMARY_VERSION) ";" xstr(SECONDARY_VERSION) "c"); // VT-220 + primary version + secondary version

View file

@ -88,6 +88,7 @@ from .fast_data_types import (
wakeup_main_loop,
)
from .keys import keyboard_mode_name, mod_mask
from .options.types import Options
from .progress import Progress
from .rgb import to_color
from .terminfo import get_capabilities
@ -544,6 +545,14 @@ def color_control(cp: ColorProfile, code: int, value: str | bytes | memoryview =
return ''
def da1(opts: Options) -> str:
ans = '?62;'
if 'write-clipboard' in opts.clipboard_control:
# see https://github.com/contour-terminal/vt-extensions/blob/master/clipboard-extension.md
ans += '52;'
return ans + 'c'
class EdgeWidths:
left: float | None
top: float | None
@ -1279,6 +1288,9 @@ class Window:
return True
return False
def on_da1(self) -> None:
self.screen.send_escape_code_to_child(ESC_CSI, da1(get_options()))
def on_bell(self) -> None:
cb = get_options().command_on_bell
if cb and cb != ['none']:

View file

@ -24,7 +24,7 @@ from kitty.options.types import Options, defaults
from kitty.rgb import to_color
from kitty.types import MouseEvent
from kitty.utils import read_screen_size
from kitty.window import decode_cmdline, process_remote_print, process_title_from_child
from kitty.window import da1, decode_cmdline, process_remote_print, process_title_from_child
def parse_bytes(screen, data, dump_callback=None):
@ -132,10 +132,14 @@ class Callbacks:
self.last_cmd_cmdline = ''
self.last_cmd_at = 0
self.num_of_resize_events = 0
self.da1 = []
def on_bell(self) -> None:
self.bell_count += 1
def on_da1(self) -> None:
self.da1.append(da1(get_options()))
def on_activity_since_last_focus(self) -> None:
pass

View file

@ -407,6 +407,15 @@ class TestScreen(BaseTest):
parse_bytes(s, b'\x1b[?2048h') # ]
self.ae(c.num_of_resize_events, 2)
def test_da1(self):
s = self.create_screen()
parse_bytes(s, b'\x1b[c\x1b[0c') # ]]
self.ae(s.callbacks.da1, ['?62;52;c', '?62;52;c']) # ]]
s.callbacks.clear()
self.create_screen(options={'clipboard_control': 'read-clipboard'})
parse_bytes(s, b'\x1b[c') # ]]
self.ae(s.callbacks.da1, ['?62;c']) # ]]
def test_cursor_after_resize(self):
def draw(text, end_line=True):