Get kitty building with the new VT parser

This commit is contained in:
Kovid Goyal 2023-10-29 13:13:11 +05:30
parent b083ad9038
commit 5f809bf249
No known key found for this signature in database
GPG key ID: 06BC317B515ACE7C
20 changed files with 1482 additions and 2136 deletions

View file

@ -52,7 +52,7 @@ def parse_flag(keymap: KeymapType, type_map: Dict[str, Any], command_class: str)
q = ' && '.join(f"g.{attr} != '{x}'" for x in sorted(allowed_values))
lines.append(f'''
case {attr}: {{
g.{attr} = screen->parser_buf[pos++] & 0xff;
g.{attr} = parser_buf[pos++];
if ({q}) {{
REPORT_ERROR("Malformed {command_class} control block, unknown flag value for {attr}: 0x%x", g.{attr});
return;
@ -116,22 +116,23 @@ def generate(
parr = 'static uint8_t payload[4096];'
payload_case = f'''
case PAYLOAD: {{
sz = screen->parser_buf_pos - pos;
sz = parser_buf_pos - pos;
g.payload_sz = sizeof(payload);
if (!base64_decode32(screen->parser_buf + pos, sz, payload, &g.payload_sz)) {{
if (!base64_decode8(parser_buf + pos, sz, payload, &g.payload_sz)) {{
REPORT_ERROR("Failed to parse {command_class} command payload with error: payload size (%zu) too large", sz); return; }}
pos = screen->parser_buf_pos;
pos = parser_buf_pos;
}}
break;
'''
callback = f'{callback_name}(screen, &g, payload)'
callback = f'{callback_name}(self->screen, &g, payload)'
else:
payload_after_value = payload = parr = payload_case = ''
callback = f'{callback_name}(screen, &g)'
callback = f'{callback_name}(self->screen, &g)'
return f'''
#include "base64.h"
static inline void
{function_name}(Screen *screen, PyObject UNUSED *dump_callback) {{
{function_name}(PS *self, const uint8_t *parser_buf, const size_t parser_buf_pos) {{
unsigned int pos = 1;
enum PARSER_STATES {{ KEY, EQUAL, UINT, INT, FLAG, AFTER_VALUE {payload} }};
enum PARSER_STATES state = KEY, value_state = FLAG;
@ -144,12 +145,12 @@ def generate(
{parr}
{keys_enum}
enum KEYS key = '{initial_key}';
if (screen->parser_buf[pos] == ';') state = AFTER_VALUE;
if (parser_buf[pos] == ';') state = AFTER_VALUE;
while (pos < screen->parser_buf_pos) {{
while (pos < parser_buf_pos) {{
switch(state) {{
case KEY:
key = screen->parser_buf[pos++];
key = parser_buf[pos++];
state = EQUAL;
switch(key) {{
{handle_key}
@ -160,8 +161,8 @@ def generate(
break;
case EQUAL:
if (screen->parser_buf[pos++] != '=') {{
REPORT_ERROR("Malformed {command_class} control block, no = after key, found: 0x%x instead", screen->parser_buf[pos-1]);
if (parser_buf[pos++] != '=') {{
REPORT_ERROR("Malformed {command_class} control block, no = after key, found: 0x%x instead", parser_buf[pos-1]);
return;
}}
state = value_state;
@ -178,16 +179,16 @@ def generate(
case INT:
#define READ_UINT \\
for (i = pos; i < MIN(screen->parser_buf_pos, pos + 10); i++) {{ \\
if (screen->parser_buf[i] < '0' || screen->parser_buf[i] > '9') break; \\
for (i = pos; i < MIN(parser_buf_pos, pos + 10); i++) {{ \\
if (parser_buf[i] < '0' || parser_buf[i] > '9') break; \\
}} \\
if (i == pos) {{ REPORT_ERROR("Malformed {command_class} control block, expecting an integer value for key: %c", key & 0xFF); return; }} \\
lcode = utoi(screen->parser_buf + pos, i - pos); pos = i; \\
lcode = utoi(parser_buf + pos, i - pos); pos = i; \\
if (lcode > UINT32_MAX) {{ REPORT_ERROR("Malformed {command_class} control block, number is too large"); return; }} \\
code = lcode;
is_negative = false;
if(screen->parser_buf[pos] == '-') {{ is_negative = true; pos++; }}
if(parser_buf[pos] == '-') {{ is_negative = true; pos++; }}
#define I(x) case x: g.x = is_negative ? 0 - (int32_t)code : (int32_t)code; break
READ_UINT;
switch(key) {{
@ -210,10 +211,10 @@ def generate(
#undef READ_UINT
case AFTER_VALUE:
switch (screen->parser_buf[pos++]) {{
switch (parser_buf[pos++]) {{
default:
REPORT_ERROR("Malformed {command_class} control block, expecting a comma or semi-colon after a value, found: 0x%x",
screen->parser_buf[pos - 1]);
parser_buf[pos - 1]);
return;
case ',':
state = KEY;

View file

@ -112,11 +112,11 @@ def read_data_from_shared_memory(shm_name: str) -> Any:
return json.loads(shm.read_data_with_size())
def get_ssh_data(msg: str, request_id: str) -> Iterator[bytes]:
def get_ssh_data(msgb: memoryview, request_id: str) -> Iterator[bytes]:
from base64 import standard_b64decode
yield b'\nKITTY_DATA_START\n' # to discard leading data
try:
msg = standard_b64decode(msg).decode('utf-8')
msg = standard_b64decode(msgb).decode('utf-8')
md = dict(x.split('=', 1) for x in msg.split(':'))
pw = md['pw']
pwfilename = md['pwfile']

View file

@ -584,7 +584,7 @@ def add_child(self, window: Window) -> None:
self.child_monitor.add_child(window.id, window.child.pid, window.child.child_fd, window.screen)
self.window_id_map[window.id] = window
def _handle_remote_command(self, cmd: str, window: Optional[Window] = None, peer_id: int = 0) -> RCResponse:
def _handle_remote_command(self, cmd: memoryview, window: Optional[Window] = None, peer_id: int = 0) -> RCResponse:
from .remote_control import is_cmd_allowed, parse_cmd, remote_control_allowed
response = None
window = window or None
@ -778,7 +778,7 @@ def peer_message_received(self, msg_bytes: bytes, peer_id: int, is_remote_contro
cmd_prefix = b'\x1bP@kitty-cmd'
terminator = b'\x1b\\'
if msg_bytes.startswith(cmd_prefix) and msg_bytes.endswith(terminator):
cmd = msg_bytes[len(cmd_prefix):-len(terminator)].decode('utf-8')
cmd = memoryview(msg_bytes[len(cmd_prefix):-len(terminator)])
response = self._handle_remote_command(cmd, peer_id=peer_id)
if response is None:
return None
@ -834,7 +834,7 @@ def peer_message_received(self, msg_bytes: bytes, peer_id: int, is_remote_contro
log_error('Unknown message received over single instance socket, ignoring')
return None
def handle_remote_cmd(self, cmd: str, window: Optional[Window] = None) -> None:
def handle_remote_cmd(self, cmd: memoryview, window: Optional[Window] = None) -> None:
response = self._handle_remote_command(cmd, window)
if response is not None and not isinstance(response, AsyncResponse) and window is not None:
window.send_cmd_response(response)

199
kitty/charsets.c generated
View file

@ -10,205 +10,6 @@
#include "data-types.h"
static uint32_t charset_translations[5][256] = {
/* 8-bit Latin-1 mapped to Unicode -- trivial mapping */
{
0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007,
0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f,
0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017,
0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f,
0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,
0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,
0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,
0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f,
0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,
0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,
0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057,
0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f,
0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,
0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f,
0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077,
0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f,
0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087,
0x0088, 0x0089, 0x008a, 0x008b, 0x008c, 0x008d, 0x008e, 0x008f,
0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097,
0x0098, 0x0099, 0x009a, 0x009b, 0x009c, 0x009d, 0x009e, 0x009f,
0x00a0, 0x00a1, 0x00a2, 0x00a3, 0x00a4, 0x00a5, 0x00a6, 0x00a7,
0x00a8, 0x00a9, 0x00aa, 0x00ab, 0x00ac, 0x00ad, 0x00ae, 0x00af,
0x00b0, 0x00b1, 0x00b2, 0x00b3, 0x00b4, 0x00b5, 0x00b6, 0x00b7,
0x00b8, 0x00b9, 0x00ba, 0x00bb, 0x00bc, 0x00bd, 0x00be, 0x00bf,
0x00c0, 0x00c1, 0x00c2, 0x00c3, 0x00c4, 0x00c5, 0x00c6, 0x00c7,
0x00c8, 0x00c9, 0x00ca, 0x00cb, 0x00cc, 0x00cd, 0x00ce, 0x00cf,
0x00d0, 0x00d1, 0x00d2, 0x00d3, 0x00d4, 0x00d5, 0x00d6, 0x00d7,
0x00d8, 0x00d9, 0x00da, 0x00db, 0x00dc, 0x00dd, 0x00de, 0x00df,
0x00e0, 0x00e1, 0x00e2, 0x00e3, 0x00e4, 0x00e5, 0x00e6, 0x00e7,
0x00e8, 0x00e9, 0x00ea, 0x00eb, 0x00ec, 0x00ed, 0x00ee, 0x00ef,
0x00f0, 0x00f1, 0x00f2, 0x00f3, 0x00f4, 0x00f5, 0x00f6, 0x00f7,
0x00f8, 0x00f9, 0x00fa, 0x00fb, 0x00fc, 0x00fd, 0x00fe, 0x00ff
},
/* VT100 graphics mapped to Unicode */
{
0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007,
0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f,
0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017,
0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f,
0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,
0x0028, 0x0029, 0x002a, 0x2192, 0x2190, 0x2191, 0x2193, 0x002f,
0x2588, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,
0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f,
0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,
0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,
0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057,
0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x00a0,
0x25c6, 0x2592, 0x2409, 0x240c, 0x240d, 0x240a, 0x00b0, 0x00b1,
0x2591, 0x240b, 0x2518, 0x2510, 0x250c, 0x2514, 0x253c, 0x23ba,
0x23bb, 0x2500, 0x23bc, 0x23bd, 0x251c, 0x2524, 0x2534, 0x252c,
0x2502, 0x2264, 0x2265, 0x03c0, 0x2260, 0x00a3, 0x00b7, 0x007f,
0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087,
0x0088, 0x0089, 0x008a, 0x008b, 0x008c, 0x008d, 0x008e, 0x008f,
0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097,
0x0098, 0x0099, 0x009a, 0x009b, 0x009c, 0x009d, 0x009e, 0x009f,
0x00a0, 0x00a1, 0x00a2, 0x00a3, 0x00a4, 0x00a5, 0x00a6, 0x00a7,
0x00a8, 0x00a9, 0x00aa, 0x00ab, 0x00ac, 0x00ad, 0x00ae, 0x00af,
0x00b0, 0x00b1, 0x00b2, 0x00b3, 0x00b4, 0x00b5, 0x00b6, 0x00b7,
0x00b8, 0x00b9, 0x00ba, 0x00bb, 0x00bc, 0x00bd, 0x00be, 0x00bf,
0x00c0, 0x00c1, 0x00c2, 0x00c3, 0x00c4, 0x00c5, 0x00c6, 0x00c7,
0x00c8, 0x00c9, 0x00ca, 0x00cb, 0x00cc, 0x00cd, 0x00ce, 0x00cf,
0x00d0, 0x00d1, 0x00d2, 0x00d3, 0x00d4, 0x00d5, 0x00d6, 0x00d7,
0x00d8, 0x00d9, 0x00da, 0x00db, 0x00dc, 0x00dd, 0x00de, 0x00df,
0x00e0, 0x00e1, 0x00e2, 0x00e3, 0x00e4, 0x00e5, 0x00e6, 0x00e7,
0x00e8, 0x00e9, 0x00ea, 0x00eb, 0x00ec, 0x00ed, 0x00ee, 0x00ef,
0x00f0, 0x00f1, 0x00f2, 0x00f3, 0x00f4, 0x00f5, 0x00f6, 0x00f7,
0x00f8, 0x00f9, 0x00fa, 0x00fb, 0x00fc, 0x00fd, 0x00fe, 0x00ff
},
/* IBM Codepage 437 mapped to Unicode */
{
0x0000, 0x263a, 0x263b, 0x2665, 0x2666, 0x2663, 0x2660, 0x2022,
0x25d8, 0x25cb, 0x25d9, 0x2642, 0x2640, 0x266a, 0x266b, 0x263c,
0x25b6, 0x25c0, 0x2195, 0x203c, 0x00b6, 0x00a7, 0x25ac, 0x21a8,
0x2191, 0x2193, 0x2192, 0x2190, 0x221f, 0x2194, 0x25b2, 0x25bc,
0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,
0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,
0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,
0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f,
0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,
0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,
0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057,
0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f,
0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,
0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f,
0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077,
0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x2302,
0x00c7, 0x00fc, 0x00e9, 0x00e2, 0x00e4, 0x00e0, 0x00e5, 0x00e7,
0x00ea, 0x00eb, 0x00e8, 0x00ef, 0x00ee, 0x00ec, 0x00c4, 0x00c5,
0x00c9, 0x00e6, 0x00c6, 0x00f4, 0x00f6, 0x00f2, 0x00fb, 0x00f9,
0x00ff, 0x00d6, 0x00dc, 0x00a2, 0x00a3, 0x00a5, 0x20a7, 0x0192,
0x00e1, 0x00ed, 0x00f3, 0x00fa, 0x00f1, 0x00d1, 0x00aa, 0x00ba,
0x00bf, 0x2310, 0x00ac, 0x00bd, 0x00bc, 0x00a1, 0x00ab, 0x00bb,
0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556,
0x2555, 0x2563, 0x2551, 0x2557, 0x255d, 0x255c, 0x255b, 0x2510,
0x2514, 0x2534, 0x252c, 0x251c, 0x2500, 0x253c, 0x255e, 0x255f,
0x255a, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256c, 0x2567,
0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256b,
0x256a, 0x2518, 0x250c, 0x2588, 0x2584, 0x258c, 0x2590, 0x2580,
0x03b1, 0x00df, 0x0393, 0x03c0, 0x03a3, 0x03c3, 0x00b5, 0x03c4,
0x03a6, 0x0398, 0x03a9, 0x03b4, 0x221e, 0x03c6, 0x03b5, 0x2229,
0x2261, 0x00b1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00f7, 0x2248,
0x00b0, 0x2219, 0x00b7, 0x221a, 0x207f, 0x00b2, 0x25a0, 0x00a0
},
// VAX 42 map
{
0x0000, 0x263a, 0x263b, 0x2665, 0x2666, 0x2663, 0x2660, 0x2022,
0x25d8, 0x25cb, 0x25d9, 0x2642, 0x2640, 0x266a, 0x266b, 0x263c,
0x25b6, 0x25c0, 0x2195, 0x203c, 0x00b6, 0x00a7, 0x25ac, 0x21a8,
0x2191, 0x2193, 0x2192, 0x2190, 0x221f, 0x2194, 0x25b2, 0x25bc,
0x0020, 0x043b, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,
0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,
0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,
0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x0435,
0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,
0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,
0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057,
0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f,
0x0060, 0x0441, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,
0x0435, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x043a,
0x0070, 0x0071, 0x0442, 0x0073, 0x043b, 0x0435, 0x0076, 0x0077,
0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x2302,
0x00c7, 0x00fc, 0x00e9, 0x00e2, 0x00e4, 0x00e0, 0x00e5, 0x00e7,
0x00ea, 0x00eb, 0x00e8, 0x00ef, 0x00ee, 0x00ec, 0x00c4, 0x00c5,
0x00c9, 0x00e6, 0x00c6, 0x00f4, 0x00f6, 0x00f2, 0x00fb, 0x00f9,
0x00ff, 0x00d6, 0x00dc, 0x00a2, 0x00a3, 0x00a5, 0x20a7, 0x0192,
0x00e1, 0x00ed, 0x00f3, 0x00fa, 0x00f1, 0x00d1, 0x00aa, 0x00ba,
0x00bf, 0x2310, 0x00ac, 0x00bd, 0x00bc, 0x00a1, 0x00ab, 0x00bb,
0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556,
0x2555, 0x2563, 0x2551, 0x2557, 0x255d, 0x255c, 0x255b, 0x2510,
0x2514, 0x2534, 0x252c, 0x251c, 0x2500, 0x253c, 0x255e, 0x255f,
0x255a, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256c, 0x2567,
0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256b,
0x256a, 0x2518, 0x250c, 0x2588, 0x2584, 0x258c, 0x2590, 0x2580,
0x03b1, 0x00df, 0x0393, 0x03c0, 0x03a3, 0x03c3, 0x00b5, 0x03c4,
0x03a6, 0x0398, 0x03a9, 0x03b4, 0x221e, 0x03c6, 0x03b5, 0x2229,
0x2261, 0x00b1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00f7, 0x2248,
0x00b0, 0x2219, 0x00b7, 0x221a, 0x207f, 0x00b2, 0x25a0, 0x00a0
},
/* UK mapping, same as 8-bit Latin1 except the pound sign replaces # */
{
0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007,
0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f,
0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017,
0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f,
0x0020, 0x0021, 0x0022, 0x00a3, 0x0024, 0x0025, 0x0026, 0x0027,
0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,
0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,
0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f,
0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,
0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,
0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057,
0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f,
0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,
0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f,
0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077,
0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f,
0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087,
0x0088, 0x0089, 0x008a, 0x008b, 0x008c, 0x008d, 0x008e, 0x008f,
0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097,
0x0098, 0x0099, 0x009a, 0x009b, 0x009c, 0x009d, 0x009e, 0x009f,
0x00a0, 0x00a1, 0x00a2, 0x00a3, 0x00a4, 0x00a5, 0x00a6, 0x00a7,
0x00a8, 0x00a9, 0x00aa, 0x00ab, 0x00ac, 0x00ad, 0x00ae, 0x00af,
0x00b0, 0x00b1, 0x00b2, 0x00b3, 0x00b4, 0x00b5, 0x00b6, 0x00b7,
0x00b8, 0x00b9, 0x00ba, 0x00bb, 0x00bc, 0x00bd, 0x00be, 0x00bf,
0x00c0, 0x00c1, 0x00c2, 0x00c3, 0x00c4, 0x00c5, 0x00c6, 0x00c7,
0x00c8, 0x00c9, 0x00ca, 0x00cb, 0x00cc, 0x00cd, 0x00ce, 0x00cf,
0x00d0, 0x00d1, 0x00d2, 0x00d3, 0x00d4, 0x00d5, 0x00d6, 0x00d7,
0x00d8, 0x00d9, 0x00da, 0x00db, 0x00dc, 0x00dd, 0x00de, 0x00df,
0x00e0, 0x00e1, 0x00e2, 0x00e3, 0x00e4, 0x00e5, 0x00e6, 0x00e7,
0x00e8, 0x00e9, 0x00ea, 0x00eb, 0x00ec, 0x00ed, 0x00ee, 0x00ef,
0x00f0, 0x00f1, 0x00f2, 0x00f3, 0x00f4, 0x00f5, 0x00f6, 0x00f7,
0x00f8, 0x00f9, 0x00fa, 0x00fb, 0x00fc, 0x00fd, 0x00fe, 0x00ff
},
};
uint32_t*
translation_table(uint32_t which) {
switch(which){
case 'B':
return charset_translations[0];
case '0':
return charset_translations[1];
case 'U':
return charset_translations[2];
case 'V':
return charset_translations[3];
case 'A':
return charset_translations[4];
default:
return charset_translations[0];
}
}
uint32_t *latin1_charset = charset_translations[0];
// UTF-8 decode taken from: https://bjoern.hoehrmann.de/utf-8/decoder/dfa/
static const uint8_t utf8_data[] = {

View file

@ -11,9 +11,10 @@
from .conf.utils import uniq
from .constants import supports_primary_selection
from .fast_data_types import (
ESC_OSC,
GLFW_CLIPBOARD,
GLFW_PRIMARY_SELECTION,
OSC,
find_in_memoryview,
get_boss,
get_clipboard_mime,
get_options,
@ -335,11 +336,17 @@ def __init__(self, window_id: int) -> None:
self.currently_asking_permission_for: Optional[ReadRequest] = None
self.in_flight_write_request: Optional[WriteRequest] = None
def parse_osc_5522(self, data: str) -> None:
def parse_osc_5522(self, data: memoryview) -> None:
import base64
from .notify import sanitize_id
metadata, _, epayload = data.partition(';')
idx = find_in_memoryview(data, ord(b';'))
if idx > -1:
metadata = str(data[:idx], "utf-8", "replace")
epayload = data[idx+1:]
else:
metadata = str(data, "utf-8", "replace")
epayload = data[len(data):]
m: Dict[str, str] = {}
for record in metadata.split(':'):
try:
@ -381,12 +388,12 @@ def parse_osc_5522(self, data: str) -> None:
wr.add_base64_data(epayload, mime)
except OSError:
if w is not None:
w.screen.send_escape_code_to_child(OSC, wr.encode_response(status='EIO'))
w.screen.send_escape_code_to_child(ESC_OSC, wr.encode_response(status='EIO'))
self.in_flight_write_request = None
raise
except Exception:
if w is not None:
w.screen.send_escape_code_to_child(OSC, wr.encode_response(status='EINVAL'))
w.screen.send_escape_code_to_child(ESC_OSC, wr.encode_response(status='EINVAL'))
self.in_flight_write_request = None
raise
else:
@ -394,18 +401,24 @@ def parse_osc_5522(self, data: str) -> None:
wr.commit()
self.in_flight_write_request = None
if w is not None:
w.screen.send_escape_code_to_child(OSC, wr.encode_response(status='DONE'))
w.screen.send_escape_code_to_child(ESC_OSC, wr.encode_response(status='DONE'))
def parse_osc_52(self, data: str, is_partial: bool = False) -> None:
where, text = data.partition(';')[::2]
if text == '?':
def parse_osc_52(self, data: memoryview, is_partial: bool = False) -> None:
idx = find_in_memoryview(data, ord(b';'))
if idx > -1:
where = str(data[idx:], "utf-8", 'replace')
data = data[idx+1:]
else:
where = str(data, "utf-8", 'replace')
data = data[len(data):]
if len(data) == 1 and data.tobytes() == b'?':
rr = ReadRequest(is_primary_selection=ClipboardType.from_osc52_where_field(where) is ClipboardType.primary_selection)
self.handle_read_request(rr)
else:
wr = self.in_flight_write_request
if wr is None:
wr = self.in_flight_write_request = WriteRequest(ClipboardType.from_osc52_where_field(where) is ClipboardType.primary_selection)
wr.add_base64_data(text)
wr.add_base64_data(data)
if is_partial:
return
self.in_flight_write_request = None
@ -426,7 +439,7 @@ def fulfill_write_request(self, wr: WriteRequest, allowed: bool = True) -> None:
if not allowed or not cp.enabled:
self.in_flight_write_request = None
if w is not None:
w.screen.send_escape_code_to_child(OSC, wr.encode_response(status='EPERM' if not allowed else 'ENOSYS'))
w.screen.send_escape_code_to_child(ESC_OSC, wr.encode_response(status='EPERM' if not allowed else 'ENOSYS'))
def fulfill_legacy_write_request(self, wr: WriteRequest, allowed: bool = True) -> None:
cp = get_boss().primary_selection if wr.is_primary_selection else get_boss().clipboard
@ -455,12 +468,12 @@ def fulfill_read_request(self, rr: ReadRequest, allowed: bool = True) -> None:
return
cp = get_boss().primary_selection if rr.is_primary_selection else get_boss().clipboard
if not cp.enabled:
w.screen.send_escape_code_to_child(OSC, rr.encode_response(status='ENOSYS'))
w.screen.send_escape_code_to_child(ESC_OSC, rr.encode_response(status='ENOSYS'))
return
if not allowed:
w.screen.send_escape_code_to_child(OSC, rr.encode_response(status='EPERM'))
w.screen.send_escape_code_to_child(ESC_OSC, rr.encode_response(status='EPERM'))
return
w.screen.send_escape_code_to_child(OSC, rr.encode_response(status='OK'))
w.screen.send_escape_code_to_child(ESC_OSC, rr.encode_response(status='OK'))
current_mime = ''
@ -468,7 +481,7 @@ def write_chunks(data: bytes) -> None:
assert w is not None
mv = memoryview(data)
while mv:
w.screen.send_escape_code_to_child(OSC, rr.encode_response(payload=mv[:4096], mime=current_mime))
w.screen.send_escape_code_to_child(ESC_OSC, rr.encode_response(payload=mv[:4096], mime=current_mime))
mv = mv[4096:]
for mime in rr.mime_types:
@ -477,20 +490,20 @@ def write_chunks(data: bytes) -> None:
payload = ' '.join(cp.get_available_mime_types_for_paste()).encode('utf-8')
if payload:
payload += b'\n'
w.screen.send_escape_code_to_child(OSC, rr.encode_response(payload=payload, mime=current_mime))
w.screen.send_escape_code_to_child(ESC_OSC, rr.encode_response(payload=payload, mime=current_mime))
continue
try:
cp.get_mime(mime, write_chunks)
except Exception as e:
log_error(f'Failed to read requested mime type {mime} with error: {e}')
w.screen.send_escape_code_to_child(OSC, rr.encode_response(status='DONE'))
w.screen.send_escape_code_to_child(ESC_OSC, rr.encode_response(status='DONE'))
def reject_read_request(self, rr: ReadRequest) -> None:
if rr.protocol_type is ProtocolType.osc_52:
return self.fulfill_legacy_read_request(rr, False)
w = get_boss().window_id_map.get(self.window_id)
if w is not None:
w.screen.send_escape_code_to_child(OSC, rr.encode_response(status='EPERM'))
w.screen.send_escape_code_to_child(ESC_OSC, rr.encode_response(status='EPERM'))
def fulfill_legacy_read_request(self, rr: ReadRequest, allowed: bool = True) -> None:
cp = get_boss().primary_selection if rr.is_primary_selection else get_boss().clipboard
@ -500,7 +513,7 @@ def fulfill_legacy_read_request(self, rr: ReadRequest, allowed: bool = True) ->
if cp.enabled and allowed:
text = cp.get_text()
loc = 'p' if rr.is_primary_selection else 'c'
w.screen.send_escape_code_to_child(OSC, encode_osc52(loc, text))
w.screen.send_escape_code_to_child(ESC_OSC, encode_osc52(loc, text))
def ask_to_read_clipboard(self, rr: ReadRequest) -> None:
if rr.mime_types == (TARGETS_MIME,):

View file

@ -55,19 +55,6 @@
// *Delete*: Is ignored.
#define DEL 0x7f
#ifndef NO_C1_CONTROLS
#define IND 0x84
#define NEL 0x85
#define HTS 0x88
#define RI 0x8d
#define DCS 0x90
#define CSI 0x9b
#define ST 0x9c
#define OSC 0x9d
#define PM 0x9e
#define APC 0x9f
#endif
// Sharp control codes
// -------------------

View file

@ -320,6 +320,17 @@ expand_ansi_c_escapes(PyObject *self UNUSED, PyObject *src) {
return ans;
}
static PyObject*
find_in_memoryview(PyObject *self UNUSED, PyObject *args) {
const char *buf; Py_ssize_t sz;
unsigned char q;
if (!PyArg_ParseTuple(args, "y#b", &buf, &sz, &q)) return NULL;
const char *p = memchr(buf, q, sz);
Py_ssize_t ans = -1;
if (p) ans = p - buf;
return PyLong_FromSsize_t(ans);
}
static PyMethodDef module_methods[] = {
{"wcwidth", (PyCFunction)wcwidth_wrap, METH_O, ""},
{"expand_ansi_c_escapes", (PyCFunction)expand_ansi_c_escapes, METH_O, ""},
@ -335,13 +346,12 @@ static PyMethodDef module_methods[] = {
{"base64_encode", (PyCFunction)pybase64_encode, METH_VARARGS, ""},
{"base64_decode", (PyCFunction)pybase64_decode, METH_VARARGS, ""},
{"thread_write", (PyCFunction)cm_thread_write, METH_VARARGS, ""},
{"parse_bytes", (PyCFunction)parse_bytes, METH_VARARGS, ""},
{"parse_bytes_dump", (PyCFunction)parse_bytes_dump, METH_VARARGS, ""},
{"redirect_std_streams", (PyCFunction)redirect_std_streams, METH_VARARGS, ""},
{"locale_is_valid", (PyCFunction)locale_is_valid, METH_VARARGS, ""},
{"shm_open", (PyCFunction)py_shm_open, METH_VARARGS, ""},
{"shm_unlink", (PyCFunction)py_shm_unlink, METH_VARARGS, ""},
{"wrapped_kitten_names", (PyCFunction)wrapped_kittens, METH_NOARGS, ""},
{"find_in_memoryview", (PyCFunction)find_in_memoryview, METH_VARARGS, ""},
#ifdef __APPLE__
METHODB(user_cache_dir, METH_NOARGS),
METHODB(process_group_map, METH_NOARGS),
@ -481,11 +491,12 @@ PyInit_fast_data_types(void) {
PyModule_AddIntMacro(m, DECCOLM);
PyModule_AddIntMacro(m, DECOM);
PyModule_AddIntMacro(m, IRM);
PyModule_AddIntMacro(m, CSI);
PyModule_AddIntMacro(m, DCS);
PyModule_AddIntMacro(m, APC);
PyModule_AddIntMacro(m, OSC);
PyModule_AddIntMacro(m, FILE_TRANSFER_CODE);
PyModule_AddIntMacro(m, ESC_CSI);
PyModule_AddIntMacro(m, ESC_OSC);
PyModule_AddIntMacro(m, ESC_APC);
PyModule_AddIntMacro(m, ESC_DCS);
PyModule_AddIntMacro(m, ESC_PM);
#ifdef __APPLE__
// Apple says its SHM_NAME_MAX but SHM_NAME_MAX is not actually declared in typical CrApple style.
// This value is based on experimentation and from qsharedmemory.cpp in Qt

View file

@ -263,8 +263,6 @@ CELL_BG_PROGRAM: int
CELL_FG_PROGRAM: int
CELL_PROGRAM: int
CELL_SPECIAL_PROGRAM: int
CSI: int
DCS: int
DECORATION: int
DIM: int
GRAPHICS_ALPHA_MASK_PROGRAM: int
@ -274,8 +272,12 @@ MARK: int
MARK_MASK: int
DECORATION_MASK: int
NUM_UNDERLINE_STYLES: int
OSC: int
FILE_TRANSFER_CODE: int
ESC_CSI: int
ESC_OSC: int
ESC_DCS: int
ESC_APC: int
ESC_PM: int
REVERSE: int
SCROLL_FULL: int
SCROLL_LINE: int
@ -1564,3 +1566,4 @@ def cocoa_recreate_global_menu() -> None: ...
def cocoa_clear_global_shortcuts() -> None: ...
def update_pointer_shape(os_window_id: int) -> None: ...
def os_window_focus_counters() -> Dict[int, int]: ...
def find_in_memoryview(buf: Union[bytes, memoryview, bytearray], chr: int) -> int: ...

View file

@ -20,7 +20,7 @@
from typing import IO, Any, Callable, DefaultDict, Deque, Dict, Iterable, Iterator, List, Optional, Tuple, Union
from kittens.transfer.utils import IdentityCompressor, ZlibCompressor, abspath, expand_home, home_path
from kitty.fast_data_types import FILE_TRANSFER_CODE, OSC, AES256GCMDecrypt, add_timer, base64_decode, base64_encode, get_boss, get_options
from kitty.fast_data_types import ESC_OSC, FILE_TRANSFER_CODE, AES256GCMDecrypt, add_timer, base64_decode, base64_encode, get_boss, get_options
from kitty.types import run_once
from .utils import log_error
@ -855,7 +855,7 @@ def prune_expired(self) -> None:
if self.active_sends[a].is_expired:
self.drop_send(a)
def handle_serialized_command(self, data: str) -> None:
def handle_serialized_command(self, data: memoryview) -> None:
try:
cmd = FileTransmissionCommand.deserialize(data)
except Exception as e:
@ -1147,7 +1147,7 @@ def write_ftc_to_child(self, payload: FileTransmissionCommand, appendleft: bool
window = boss.window_id_map.get(self.window_id)
if window is not None:
data = tuple(payload.get_serialized_fields(prefix_with_osc_code=True))
queued = window.screen.send_escape_code_to_child(OSC, data)
queued = window.screen.send_escape_code_to_child(ESC_OSC, data)
if not queued:
if use_pending:
if appendleft:

View file

@ -394,7 +394,7 @@ HANDLER(handle_move_event) {
} else {
if (!mouse_cell_changed && screen->modes.mouse_tracking_protocol != SGR_PIXEL_PROTOCOL) return;
int sz = encode_mouse_button(w, button, button >=0 ? DRAG : MOVE, modifiers);
if (sz > 0) { mouse_event_buf[sz] = 0; write_escape_code_to_child(screen, CSI, mouse_event_buf); }
if (sz > 0) { mouse_event_buf[sz] = 0; write_escape_code_to_child(screen, ESC_CSI, mouse_event_buf); }
}
}
@ -566,7 +566,7 @@ HANDLER(handle_button_event) {
if (!dispatch_mouse_event(w, button, is_release ? -1 : 1, modifiers, screen->modes.mouse_tracking_mode != 0)) {
if (screen->modes.mouse_tracking_mode != 0) {
int sz = encode_mouse_button(w, button, is_release ? RELEASE : PRESS, modifiers);
if (sz > 0) { mouse_event_buf[sz] = 0; write_escape_code_to_child(screen, CSI, mouse_event_buf); }
if (sz > 0) { mouse_event_buf[sz] = 0; write_escape_code_to_child(screen, ESC_CSI, mouse_event_buf); }
}
}
// the windows array might have been re-alloced in dispatch_mouse_event
@ -957,7 +957,7 @@ scroll_event(double xoffset, double yoffset, int flags, int modifiers) {
if (sz > 0) {
mouse_event_buf[sz] = 0;
for (s = abs(s); s > 0; s--) {
write_escape_code_to_child(screen, CSI, mouse_event_buf);
write_escape_code_to_child(screen, ESC_CSI, mouse_event_buf);
}
}
} else {
@ -974,7 +974,7 @@ scroll_event(double xoffset, double yoffset, int flags, int modifiers) {
if (sz > 0) {
mouse_event_buf[sz] = 0;
for (s = abs(s); s > 0; s--) {
write_escape_code_to_child(screen, CSI, mouse_event_buf);
write_escape_code_to_child(screen, ESC_CSI, mouse_event_buf);
}
}
}
@ -999,7 +999,7 @@ send_mouse_event(PyObject *self UNUSED, PyObject *args, PyObject *kw) {
int sz = encode_mouse_event_impl(&mpos, screen->modes.mouse_tracking_protocol, button, action, mods);
if (sz > 0) {
mouse_event_buf[sz] = 0;
write_escape_code_to_child(screen, CSI, mouse_event_buf);
write_escape_code_to_child(screen, ESC_CSI, mouse_event_buf);
Py_RETURN_TRUE;
}
}

View file

@ -2,8 +2,9 @@
#pragma once
static inline void parse_graphics_code(Screen *screen,
PyObject UNUSED *dump_callback) {
#include "base64.h"
static inline void parse_graphics_code(PS *self, const uint8_t *parser_buf,
const size_t parser_buf_pos) {
unsigned int pos = 1;
enum PARSER_STATES { KEY, EQUAL, UINT, INT, FLAG, AFTER_VALUE, PAYLOAD };
enum PARSER_STATES state = KEY, value_state = FLAG;
@ -48,13 +49,13 @@ static inline void parse_graphics_code(Screen *screen,
};
enum KEYS key = 'a';
if (screen->parser_buf[pos] == ';')
if (parser_buf[pos] == ';')
state = AFTER_VALUE;
while (pos < screen->parser_buf_pos) {
while (pos < parser_buf_pos) {
switch (state) {
case KEY:
key = screen->parser_buf[pos++];
key = parser_buf[pos++];
state = EQUAL;
switch (key) {
case action:
@ -153,10 +154,10 @@ static inline void parse_graphics_code(Screen *screen,
break;
case EQUAL:
if (screen->parser_buf[pos++] != '=') {
if (parser_buf[pos++] != '=') {
REPORT_ERROR("Malformed GraphicsCommand control block, no = after key, "
"found: 0x%x instead",
screen->parser_buf[pos - 1]);
parser_buf[pos - 1]);
return;
}
state = value_state;
@ -166,7 +167,7 @@ static inline void parse_graphics_code(Screen *screen,
switch (key) {
case action: {
g.action = screen->parser_buf[pos++] & 0xff;
g.action = parser_buf[pos++];
if (g.action != 'T' && g.action != 'a' && g.action != 'c' &&
g.action != 'd' && g.action != 'f' && g.action != 'p' &&
g.action != 'q' && g.action != 't') {
@ -178,7 +179,7 @@ static inline void parse_graphics_code(Screen *screen,
} break;
case delete_action: {
g.delete_action = screen->parser_buf[pos++] & 0xff;
g.delete_action = parser_buf[pos++];
if (g.delete_action != 'A' && g.delete_action != 'C' &&
g.delete_action != 'F' && g.delete_action != 'I' &&
g.delete_action != 'N' && g.delete_action != 'P' &&
@ -197,7 +198,7 @@ static inline void parse_graphics_code(Screen *screen,
} break;
case transmission_type: {
g.transmission_type = screen->parser_buf[pos++] & 0xff;
g.transmission_type = parser_buf[pos++];
if (g.transmission_type != 'd' && g.transmission_type != 'f' &&
g.transmission_type != 's' && g.transmission_type != 't') {
REPORT_ERROR("Malformed GraphicsCommand control block, unknown flag "
@ -208,7 +209,7 @@ static inline void parse_graphics_code(Screen *screen,
} break;
case compressed: {
g.compressed = screen->parser_buf[pos++] & 0xff;
g.compressed = parser_buf[pos++];
if (g.compressed != 'z') {
REPORT_ERROR("Malformed GraphicsCommand control block, unknown flag "
"value for compressed: 0x%x",
@ -225,8 +226,8 @@ static inline void parse_graphics_code(Screen *screen,
case INT:
#define READ_UINT \
for (i = pos; i < MIN(screen->parser_buf_pos, pos + 10); i++) { \
if (screen->parser_buf[i] < '0' || screen->parser_buf[i] > '9') \
for (i = pos; i < MIN(parser_buf_pos, pos + 10); i++) { \
if (parser_buf[i] < '0' || parser_buf[i] > '9') \
break; \
} \
if (i == pos) { \
@ -235,7 +236,7 @@ static inline void parse_graphics_code(Screen *screen,
key & 0xFF); \
return; \
} \
lcode = utoi(screen->parser_buf + pos, i - pos); \
lcode = utoi(parser_buf + pos, i - pos); \
pos = i; \
if (lcode > UINT32_MAX) { \
REPORT_ERROR( \
@ -245,7 +246,7 @@ static inline void parse_graphics_code(Screen *screen,
code = lcode;
is_negative = false;
if (screen->parser_buf[pos] == '-') {
if (parser_buf[pos] == '-') {
is_negative = true;
pos++;
}
@ -302,11 +303,11 @@ static inline void parse_graphics_code(Screen *screen,
#undef READ_UINT
case AFTER_VALUE:
switch (screen->parser_buf[pos++]) {
switch (parser_buf[pos++]) {
default:
REPORT_ERROR("Malformed GraphicsCommand control block, expecting a "
"comma or semi-colon after a value, found: 0x%x",
screen->parser_buf[pos - 1]);
parser_buf[pos - 1]);
return;
case ',':
state = KEY;
@ -318,16 +319,15 @@ static inline void parse_graphics_code(Screen *screen,
break;
case PAYLOAD: {
sz = screen->parser_buf_pos - pos;
sz = parser_buf_pos - pos;
g.payload_sz = sizeof(payload);
if (!base64_decode32(screen->parser_buf + pos, sz, payload,
&g.payload_sz)) {
if (!base64_decode8(parser_buf + pos, sz, payload, &g.payload_sz)) {
REPORT_ERROR("Failed to parse GraphicsCommand command payload with "
"error: payload size (%zu) too large",
sz);
return;
}
pos = screen->parser_buf_pos;
pos = parser_buf_pos;
} break;
} // end switch
@ -373,5 +373,5 @@ static inline void parse_graphics_code(Screen *screen,
"offset_from_parent_y", (int)g.offset_from_parent_y, "payload_sz",
g.payload_sz, payload, g.payload_sz);
screen_handle_graphics_command(screen, &g, payload);
screen_handle_graphics_command(self->screen, &g, payload);
}

File diff suppressed because it is too large Load diff

View file

@ -52,7 +52,7 @@ def encode_response_for_peer(response: Any) -> bytes:
return b'\x1bP@kitty-cmd' + json.dumps(response).encode('utf-8') + b'\x1b\\'
def parse_cmd(serialized_cmd: str, encryption_key: EllipticCurveKey) -> Dict[str, Any]:
def parse_cmd(serialized_cmd: memoryview, encryption_key: EllipticCurveKey) -> Dict[str, Any]:
try:
pcmd = json.loads(serialized_cmd)
except Exception:

View file

@ -10,6 +10,7 @@
if (PyModule_AddFunctions(module, module_methods) != 0) return false; \
}
#include "control-codes.h"
#include "state.h"
#include "iqsort.h"
#include "fonts.h"
@ -24,8 +25,8 @@
#include "modes.h"
#include "wcwidth-std.h"
#include "wcswidth.h"
#include "control-codes.h"
#include "keys.h"
#include "vt-parser.h"
static const ScreenModes empty_modes = {0, .mDECAWM=true, .mDECTCEM=true, .mDECARM=true};
@ -81,14 +82,6 @@ static void update_overlay_position(Screen *self);
static void render_overlay_line(Screen *self, Line *line, FONTS_DATA_HANDLE fonts_data);
static void update_overlay_line_data(Screen *self, uint8_t *data);
#define RESET_CHARSETS \
self->g0_charset = translation_table(0); \
self->g1_charset = self->g0_charset; \
self->g_charset = self->g0_charset; \
self->current_charset = 0; \
self->utf8_state = 0; \
self->utf8_codepoint = 0; \
self->use_latin1 = false;
#define CALLBACK(...) \
if (self->callbacks != Py_None) { \
PyObject *callback_ret = PyObject_CallMethod(self->callbacks, __VA_ARGS__); \
@ -114,6 +107,8 @@ new(PyTypeObject *type, PyObject *args, PyObject UNUSED *kwds) {
Py_CLEAR(self); PyErr_Format(PyExc_RuntimeError, "Failed to create Screen write_buf_lock mutex: %s", strerror(ret));
return NULL;
}
self->vt_parser = alloc_vt_parser(window_id);
if (self->vt_parser == NULL) { Py_CLEAR(self); return PyErr_NoMemory(); }
self->reload_all_gpu_data = true;
self->cell_size.width = cell_width; self->cell_size.height = cell_height;
self->columns = columns; self->lines = lines;
@ -127,7 +122,7 @@ new(PyTypeObject *type, PyObject *args, PyObject UNUSED *kwds) {
self->scroll_changed = false;
self->margin_top = 0; self->margin_bottom = self->lines - 1;
self->history_line_added_count = 0;
RESET_CHARSETS;
reset_vt_parser(self->vt_parser);
self->callbacks = callbacks; Py_INCREF(callbacks);
self->test_child = test_child; Py_INCREF(test_child);
self->cursor = alloc_cursor();
@ -194,7 +189,7 @@ screen_reset(Screen *self) {
#define R(name) self->color_profile->overridden.name.val = 0
R(default_fg); R(default_bg); R(cursor_color); R(highlight_fg); R(highlight_bg);
#undef R
RESET_CHARSETS;
reset_vt_parser(self->vt_parser);
self->margin_top = 0; self->margin_bottom = self->lines - 1;
screen_normal_keypad_mode(self);
init_tabstops(self->main_tabstops, self->columns);
@ -207,10 +202,6 @@ screen_reset(Screen *self) {
set_dynamic_color(self, 110, NULL);
set_dynamic_color(self, 111, NULL);
set_color_table_color(self, 104, NULL);
self->parser_state = 0;
self->parser_text_start = 0;
self->parser_buf_pos = 0;
self->parser_has_pending_text = false;
}
void
@ -466,6 +457,7 @@ static void
dealloc(Screen* self) {
pthread_mutex_destroy(&self->read_buf_lock);
pthread_mutex_destroy(&self->write_buf_lock);
free_vt_parser(self->vt_parser); self->vt_parser = NULL;
Py_CLEAR(self->main_grman);
Py_CLEAR(self->alt_grman);
Py_CLEAR(self->last_reported_cwd);
@ -495,34 +487,6 @@ dealloc(Screen* self) {
// Draw text {{{
void
screen_change_charset(Screen *self, uint32_t which) {
switch(which) {
case 0:
self->current_charset = 0;
self->g_charset = self->g0_charset;
break;
case 1:
self->current_charset = 1;
self->g_charset = self->g1_charset;
break;
}
}
void
screen_designate_charset(Screen *self, uint32_t which, uint32_t as) {
switch(which) {
case 0:
self->g0_charset = translation_table(as);
if (self->current_charset == 0) self->g_charset = self->g0_charset;
break;
case 1:
self->g1_charset = translation_table(as);
if (self->current_charset == 1) self->g_charset = self->g1_charset;
break;
}
}
static void
move_widened_char(Screen *self, CPUCell* cpu_cell, GPUCell *gpu_cell, index_type xpos, index_type ypos) {
self->cursor->x = xpos; self->cursor->y = ypos;
@ -681,8 +645,8 @@ draw_combining_char(Screen *self, char_type ch) {
}
static void
draw_codepoint(Screen *self, char_type och, bool from_input_stream) {
if (is_ignored_char(och)) return;
draw_codepoint(Screen *self, char_type ch, bool from_input_stream) {
if (is_ignored_char(ch)) return;
if (!self->has_activity_since_last_focus && !self->has_focus && self->callbacks != Py_None) {
PyObject *ret = PyObject_CallMethod(self->callbacks, "on_activity_since_last_focus", NULL);
if (ret == NULL) PyErr_Print();
@ -691,7 +655,6 @@ draw_codepoint(Screen *self, char_type och, bool from_input_stream) {
Py_DECREF(ret);
}
}
uint32_t ch = och < 256 ? self->g_charset[och] : och;
if (UNLIKELY(is_combining_char(ch))) {
if (UNLIKELY(is_flag_codepoint(ch))) {
if (draw_second_flag_codepoint(self, ch)) return;
@ -816,23 +779,23 @@ write_to_child(Screen *self, const char *data, size_t sz) {
}
static void
get_prefix_and_suffix_for_escape_code(const Screen *self, unsigned char which, const char ** prefix, const char ** suffix) {
*suffix = self->modes.eight_bit_controls ? "\x9c" : "\033\\";
get_prefix_and_suffix_for_escape_code(unsigned char which, const char ** prefix, const char ** suffix) {
*suffix = "\033\\";
switch(which) {
case DCS:
*prefix = self->modes.eight_bit_controls ? "\x90" : "\033P";
case ESC_DCS:
*prefix = "\033P";
break;
case CSI:
*prefix = self->modes.eight_bit_controls ? "\x9b" : "\033["; *suffix = "";
case ESC_CSI:
*prefix = "\033["; *suffix = "";
break;
case OSC:
*prefix = self->modes.eight_bit_controls ? "\x9d" : "\033]";
case ESC_OSC:
*prefix = "\033]";
break;
case PM:
*prefix = self->modes.eight_bit_controls ? "\x9e" : "\033^";
case ESC_PM:
*prefix = "\033^";
break;
case APC:
*prefix = self->modes.eight_bit_controls ? "\x9f" : "\033_";
case ESC_APC:
*prefix = "\033_";
break;
default:
fatal("Unknown escape code to write: %u", which);
@ -843,7 +806,7 @@ bool
write_escape_code_to_child(Screen *self, unsigned char which, const char *data) {
bool written = false;
const char *prefix, *suffix;
get_prefix_and_suffix_for_escape_code(self, which, &prefix, &suffix);
get_prefix_and_suffix_for_escape_code(which, &prefix, &suffix);
if (self->window_id) {
if (suffix[0]) {
written = schedule_write_to_child(self->window_id, 3, prefix, strlen(prefix), data, strlen(data), suffix, strlen(suffix));
@ -863,7 +826,7 @@ static bool
write_escape_code_to_child_python(Screen *self, unsigned char which, PyObject *data) {
bool written = false;
const char *prefix, *suffix;
get_prefix_and_suffix_for_escape_code(self, which, &prefix, &suffix);
get_prefix_and_suffix_for_escape_code(which, &prefix, &suffix);
if (self->window_id) written = schedule_write_to_child_python(self->window_id, prefix, data, suffix);
if (self->test_child != Py_None) {
write_to_test_child(self, prefix, strlen(prefix));
@ -911,7 +874,7 @@ void
screen_handle_graphics_command(Screen *self, const GraphicsCommand *cmd, const uint8_t *payload) {
unsigned int x = self->cursor->x, y = self->cursor->y;
const char *response = grman_handle_command(self->grman, cmd, payload, self->cursor, &self->is_dirty, self->cell_size);
if (response != NULL) write_escape_code_to_child(self, APC, response);
if (response != NULL) write_escape_code_to_child(self, ESC_APC, response);
if (x != self->cursor->x || y != self->cursor->y) {
bool in_margins = cursor_within_margins(self);
if (self->cursor->x >= self->columns) { self->cursor->x = 0; self->cursor->y++; }
@ -1070,11 +1033,6 @@ screen_reset_mode(Screen *self, unsigned int mode) {
set_mode_from_const(self, mode, false);
}
void
screen_set_8bit_controls(Screen *self, bool yes) {
self->modes.eight_bit_controls = yes;
}
uint8_t
screen_current_key_encoding_flags(Screen *self) {
for (unsigned i = arraysz(self->main_key_encoding_flags); i-- > 0; ) {
@ -1090,7 +1048,7 @@ screen_report_key_encoding_flags(Screen *self) {
debug("\x1b[35mReporting key encoding flags: %u\x1b[39m\n", screen_current_key_encoding_flags(self));
}
snprintf(buf, sizeof(buf), "?%uu", screen_current_key_encoding_flags(self));
write_escape_code_to_child(self, CSI, buf);
write_escape_code_to_child(self, ESC_CSI, buf);
}
void
@ -1534,14 +1492,6 @@ screen_linefeed(Screen *self) {
} \
}
#define COPY_CHARSETS(self, sp) \
sp->utf8_state = self->utf8_state; \
sp->utf8_codepoint = self->utf8_codepoint; \
sp->g0_charset = self->g0_charset; \
sp->g1_charset = self->g1_charset; \
sp->current_charset = self->current_charset; \
sp->use_latin1 = self->use_latin1;
void
screen_save_cursor(Screen *self) {
Savepoint *sp = self->linebuf == self->main_linebuf ? &self->main_savepoint : &self->alt_savepoint;
@ -1549,7 +1499,6 @@ screen_save_cursor(Screen *self) {
sp->mDECOM = self->modes.mDECOM;
sp->mDECAWM = self->modes.mDECAWM;
sp->mDECSCNM = self->modes.mDECSCNM;
COPY_CHARSETS(self, sp);
sp->is_valid = true;
}
@ -1626,11 +1575,8 @@ screen_restore_cursor(Screen *self) {
if (!sp->is_valid) {
screen_cursor_position(self, 1, 1);
screen_reset_mode(self, DECOM);
RESET_CHARSETS;
screen_reset_mode(self, DECSCNM);
} else {
COPY_CHARSETS(sp, self);
self->g_charset = self->current_charset ? self->g1_charset : self->g0_charset;
set_mode_from_const(self, DECOM, sp->mDECOM);
set_mode_from_const(self, DECAWM, sp->mDECAWM);
set_mode_from_const(self, DECSCNM, sp->mDECSCNM);
@ -1965,12 +1911,6 @@ screen_erase_characters(Screen *self, unsigned int count) {
// Device control {{{
void
screen_use_latin1(Screen *self, bool on) {
self->use_latin1 = on; self->utf8_state = 0; self->utf8_codepoint = 0;
CALLBACK("use_utf8", "O", on ? Py_False : Py_True);
}
bool
screen_invert_colors(Screen *self) {
bool inverted = false;
@ -1998,10 +1938,10 @@ 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, CSI, "?62;c");
write_escape_code_to_child(self, ESC_CSI, "?62;c");
break;
case '>':
write_escape_code_to_child(self, CSI, ">1;" xstr(PRIMARY_VERSION) ";" xstr(SECONDARY_VERSION) "c"); // VT-220 + primary version + secondary version
write_escape_code_to_child(self, ESC_CSI, ">1;" xstr(PRIMARY_VERSION) ";" xstr(SECONDARY_VERSION) "c"); // VT-220 + primary version + secondary version
break;
}
}
@ -2010,7 +1950,7 @@ report_device_attributes(Screen *self, unsigned int mode, char start_modifier) {
void
screen_xtversion(Screen *self, unsigned int mode) {
if (mode == 0) {
write_escape_code_to_child(self, DCS, ">|kitty(" XT_VERSION ")");
write_escape_code_to_child(self, ESC_DCS, ">|kitty(" XT_VERSION ")");
}
}
@ -2038,7 +1978,7 @@ screen_report_size(Screen *self, unsigned int which) {
}
if (code) {
snprintf(buf, sizeof(buf), "%u;%u;%ut", code, height, width);
write_escape_code_to_child(self, CSI, buf);
write_escape_code_to_child(self, ESC_CSI, buf);
}
}
@ -2059,7 +1999,7 @@ report_device_status(Screen *self, unsigned int which, bool private) {
static char buf[64];
switch(which) {
case 5: // device status
write_escape_code_to_child(self, CSI, "0n");
write_escape_code_to_child(self, ESC_CSI, "0n");
break;
case 6: // cursor position
x = self->cursor->x; y = self->cursor->y;
@ -2070,7 +2010,7 @@ report_device_status(Screen *self, unsigned int which, bool private) {
if (self->modes.mDECOM) y -= MAX(y, self->margin_top);
// 1-based indexing
int sz = snprintf(buf, sizeof(buf) - 1, "%s%u;%uR", (private ? "?": ""), y + 1, x + 1);
if (sz > 0) write_escape_code_to_child(self, CSI, buf);
if (sz > 0) write_escape_code_to_child(self, ESC_CSI, buf);
break;
}
}
@ -2114,7 +2054,7 @@ report_mode_status(Screen *self, unsigned int which, bool private) {
ans = self->pending_mode.activated_at ? 1 : 2; break;
}
int sz = snprintf(buf, sizeof(buf) - 1, "%s%u;%u$y", (private ? "?" : ""), which, ans);
if (sz > 0) write_escape_code_to_child(self, CSI, buf);
if (sz > 0) write_escape_code_to_child(self, ESC_CSI, buf);
}
void
@ -2173,7 +2113,7 @@ set_icon(Screen *self, PyObject *icon) {
void
set_dynamic_color(Screen *self, unsigned int code, PyObject *color) {
if (color == NULL) { CALLBACK("set_dynamic_color", "Is", code, ""); }
if (color == NULL) { CALLBACK("set_dynamic_color", "I", code); }
else { CALLBACK("set_dynamic_color", "IO", code, color); }
}
@ -2185,36 +2125,29 @@ clipboard_control(Screen *self, int code, PyObject *data) {
void
file_transmission(Screen *self, PyObject *data) {
if (PyUnicode_READY(data) != 0) { PyErr_Clear(); return; }
CALLBACK("file_transmission", "O", data);
}
static void
parse_prompt_mark(Screen *self, PyObject *parts, PromptKind *pk) {
for (Py_ssize_t i = 0; i < PyList_GET_SIZE(parts); i++) {
PyObject *token = PyList_GET_ITEM(parts, i);
if (PyUnicode_CompareWithASCIIString(token, "k=s") == 0) *pk = SECONDARY_PROMPT;
else if (PyUnicode_CompareWithASCIIString(token, "redraw=0") == 0) self->prompt_settings.redraws_prompts_at_all = 0;
parse_prompt_mark(Screen *self, char *buf, PromptKind *pk) {
char *saveptr, *str = buf;
while (true) {
const char *token = strtok_r(str, ";", &saveptr); str = NULL;
if (token == NULL) return;
if (strcmp(token, "k=s") == 0) *pk = SECONDARY_PROMPT;
else if (strcmp(token, "redraw=0") == 0) self->prompt_settings.redraws_prompts_at_all = 0;
}
}
void
shell_prompt_marking(Screen *self, PyObject *data) {
if (PyUnicode_READY(data) != 0) { PyErr_Clear(); return; }
if (PyUnicode_GET_LENGTH(data) > 0 && self->cursor->y < self->lines) {
Py_UCS4 ch = PyUnicode_READ_CHAR(data, 0);
shell_prompt_marking(Screen *self, char *buf) {
if (self->cursor->y < self->lines) {
char ch = buf[0];
switch (ch) {
case 'A': {
PromptKind pk = PROMPT_START;
self->prompt_settings.redraws_prompts_at_all = 1;
if (PyUnicode_FindChar(data, ';', 0, PyUnicode_GET_LENGTH(data), 1)) {
RAII_PyObject(sep, PyUnicode_FromString(";"));
if (sep) {
RAII_PyObject(parts, PyUnicode_Split(data, sep, -1));
if (parts) parse_prompt_mark(self, parts, &pk);
}
}
if (PyErr_Occurred()) PyErr_Print();
parse_prompt_mark(self, buf+1, &pk);
self->linebuf->line_attrs[self->cursor->y].prompt_kind = pk;
if (pk == PROMPT_START)
CALLBACK("cmd_output_marking", "O", Py_False);
@ -2225,11 +2158,7 @@ shell_prompt_marking(Screen *self, PyObject *data) {
break;
}
}
if (global_state.debug_rendering) {
fprintf(stderr, "prompt_marking: x=%d y=%d op=", self->cursor->x, self->cursor->y);
PyObject_Print(data, stderr, 0);
fprintf(stderr, "\n");
}
if (global_state.debug_rendering) fprintf(stderr, "prompt_marking: x=%d y=%d op=%s\n", self->cursor->x, self->cursor->y, buf);
}
static bool
@ -2262,7 +2191,7 @@ screen_history_scroll_to_prompt(Screen *self, int num_of_prompts_to_jump) {
void
set_color_table_color(Screen *self, unsigned int code, PyObject *color) {
if (color == NULL) { CALLBACK("set_color_table_color", "Is", code, ""); }
if (color == NULL) { CALLBACK("set_color_table_color", "I", code); }
else { CALLBACK("set_color_table_color", "IO", code, color); }
}
@ -2315,7 +2244,7 @@ screen_report_color_stack(Screen *self) {
colorprofile_report_stack(self->color_profile, &idx, &count);
char buf[128] = {0};
snprintf(buf, arraysz(buf), "%u;%u#Q", idx, count);
write_escape_code_to_child(self, CSI, buf);
write_escape_code_to_child(self, ESC_CSI, buf);
}
void screen_handle_kitty_dcs(Screen *self, const char *callback_name, PyObject *cmd) {
@ -2323,17 +2252,15 @@ void screen_handle_kitty_dcs(Screen *self, const char *callback_name, PyObject *
}
void
screen_request_capabilities(Screen *self, char c, PyObject *q) {
screen_request_capabilities(Screen *self, char c, const char *query) {
static char buf[128];
int shape = 0;
const char *query;
switch(c) {
case '+':
CALLBACK("request_capabilities", "O", q);
break;
case '+': {
CALLBACK("request_capabilities", "s", query);
} break;
case '$':
// report status DECRQSS
query = PyUnicode_AsUTF8(q);
if (strcmp(" q", query) == 0) {
// cursor shape DECSCUSR
switch(self->cursor->shape) {
@ -2358,7 +2285,7 @@ screen_request_capabilities(Screen *self, char c, PyObject *q) {
} else {
shape = snprintf(buf, sizeof(buf), "0$r");
}
if (shape > 0) write_escape_code_to_child(self, DCS, buf);
if (shape > 0) write_escape_code_to_child(self, ESC_DCS, buf);
break;
}
}
@ -3573,17 +3500,12 @@ draw(Screen *self, PyObject *src) {
Py_RETURN_NONE;
}
extern void
parse_sgr(Screen *screen, uint32_t *buf, unsigned int num, int *params, PyObject *dump_callback, const char *report_name, Region *region);
static PyObject*
apply_sgr(Screen *self, PyObject *src) {
if (!PyUnicode_Check(src)) { PyErr_SetString(PyExc_TypeError, "A unicode string is required"); return NULL; }
if (PyUnicode_READY(src) != 0) { return PyErr_NoMemory(); }
Py_UCS4 *buf = PyUnicode_AsUCS4Copy(src);
if (!buf) return NULL;
int params[MAX_PARAMS] = {0};
parse_sgr(self, buf, PyUnicode_GET_LENGTH(src), params, NULL, "parse_sgr", NULL);
parse_sgr(self, (const uint8_t*)PyUnicode_AsUTF8(src), PyUnicode_GET_LENGTH(src), params, "parse_sgr", NULL);
Py_RETURN_NONE;
}
@ -4356,9 +4278,9 @@ paste_(Screen *self, PyObject *bytes, bool allow_bracketed_paste) {
} else {
PyErr_SetString(PyExc_TypeError, "Must paste() bytes"); return NULL;
}
if (allow_bracketed_paste && self->modes.mBRACKETED_PASTE) write_escape_code_to_child(self, CSI, BRACKETED_PASTE_START);
if (allow_bracketed_paste && self->modes.mBRACKETED_PASTE) write_escape_code_to_child(self, ESC_CSI, BRACKETED_PASTE_START);
write_to_child(self, data, sz);
if (allow_bracketed_paste && self->modes.mBRACKETED_PASTE) write_escape_code_to_child(self, CSI, BRACKETED_PASTE_END);
if (allow_bracketed_paste && self->modes.mBRACKETED_PASTE) write_escape_code_to_child(self, ESC_CSI, BRACKETED_PASTE_END);
Py_RETURN_NONE;
}
@ -4381,7 +4303,7 @@ focus_changed(Screen *self, PyObject *has_focus_) {
self->has_focus = has_focus;
if (has_focus) self->has_activity_since_last_focus = false;
else if (screen_is_overlay_active(self)) deactivate_overlay_line(self);
if (self->modes.mFOCUS_TRACKING) write_escape_code_to_child(self, CSI, has_focus ? "I" : "O");
if (self->modes.mFOCUS_TRACKING) write_escape_code_to_child(self, ESC_CSI, has_focus ? "I" : "O");
Py_RETURN_TRUE;
}
Py_RETURN_FALSE;
@ -4521,7 +4443,6 @@ line_edge_colors(Screen *self, PyObject *a UNUSED) {
WRAP0(update_only_line_graphics_data)
WRAP0(bell)
#define MND(name, args) {#name, (PyCFunction)name, args, #name},
#define MODEFUNC(name) MND(name, METH_NOARGS) MND(set_##name, METH_O)
@ -4637,6 +4558,7 @@ static PyGetSetDef getsetters[] = {
static PyMemberDef members[] = {
{"callbacks", T_OBJECT_EX, offsetof(Screen, callbacks), 0, "callbacks"},
{"cursor", T_OBJECT_EX, offsetof(Screen, cursor), READONLY, "cursor"},
{"vt_parser", T_OBJECT_EX, offsetof(Screen, vt_parser), READONLY, "vt_parser"},
{"last_reported_cwd", T_OBJECT, offsetof(Screen, last_reported_cwd), READONLY, "last_reported_cwd"},
{"grman", T_OBJECT_EX, offsetof(Screen, grman), READONLY, "grman"},
{"color_profile", T_OBJECT_EX, offsetof(Screen, color_profile), READONLY, "color_profile"},

View file

@ -6,6 +6,7 @@
#pragma once
#include "vt-parser.h"
#include "graphics.h"
#include "monotonic.h"
#define MAX_PARAMS 256
@ -17,7 +18,6 @@ typedef struct {
mBRACKETED_PASTE, mFOCUS_TRACKING, mDECSACE, mHANDLE_TERMIOS_SIGNALS;
MouseTrackingMode mouse_tracking_mode;
MouseTrackingProtocol mouse_tracking_protocol;
bool eight_bit_controls; // S8C1T
} ScreenModes;
typedef struct {
@ -58,9 +58,6 @@ typedef struct {
#define SAVEPOINTS_SZ 256
typedef struct {
uint32_t utf8_state, utf8_codepoint, *g0_charset, *g1_charset;
unsigned int current_charset;
bool use_latin1;
Cursor cursor;
bool mDECOM, mDECAWM, mDECSCNM;
bool is_valid;
@ -92,15 +89,12 @@ typedef struct {
CellPixelSize cell_size;
OverlayLine overlay_line;
id_type window_id;
uint32_t utf8_codepoint, *g0_charset, *g1_charset, *g_charset;
UTF8State utf8_state;
unsigned int current_charset;
Selections selections, url_ranges;
struct {
unsigned int cursor_x, cursor_y, scrolled_by;
index_type lines, columns;
} last_rendered;
bool use_latin1, is_dirty, scroll_changed, reload_all_gpu_data;
bool is_dirty, scroll_changed, reload_all_gpu_data;
Cursor *cursor;
Savepoint main_savepoint, alt_savepoint;
PyObject *callbacks, *test_child;
@ -113,9 +107,6 @@ typedef struct {
ColorProfile *color_profile;
monotonic_t start_visual_bell_at;
uint32_t parser_buf[8*1024];
unsigned int parser_state, parser_text_start, parser_buf_pos;
bool parser_has_pending_text;
uint8_t read_buf[READ_BUF_SZ], *write_buf;
monotonic_t new_input_at;
size_t read_buf_sz, write_buf_sz, write_buf_used;
@ -167,6 +158,7 @@ typedef struct {
struct {
uint8_t stack[16], count;
} main_pointer_shape_stack, alternate_pointer_shape_stack;
Parser *vt_parser;
} Screen;
@ -221,26 +213,21 @@ void screen_repeat_character(Screen *self, unsigned int count);
void screen_delete_characters(Screen *self, unsigned int count);
void screen_erase_characters(Screen *self, unsigned int count);
void screen_set_margins(Screen *self, unsigned int top, unsigned int bottom);
void screen_change_charset(Screen *, uint32_t to);
void screen_handle_cmd(Screen *, PyObject *cmd);
void screen_push_colors(Screen *, unsigned int);
void screen_pop_colors(Screen *, unsigned int);
void screen_report_color_stack(Screen *);
void screen_handle_kitty_dcs(Screen *, const char *callback_name, PyObject *cmd);
void screen_designate_charset(Screen *, uint32_t which, uint32_t as);
void screen_use_latin1(Screen *, bool);
void set_title(Screen *self, PyObject*);
void desktop_notify(Screen *self, unsigned int, PyObject*);
void set_icon(Screen *self, PyObject*);
void set_dynamic_color(Screen *self, unsigned int code, PyObject*);
void clipboard_control(Screen *self, int code, PyObject*);
void shell_prompt_marking(Screen *self, PyObject*);
void shell_prompt_marking(Screen *self, char *buf);
void file_transmission(Screen *self, PyObject*);
void set_color_table_color(Screen *self, unsigned int code, PyObject*);
void process_cwd_notification(Screen *self, unsigned int code, PyObject*);
uint32_t* translation_table(uint32_t which);
void screen_request_capabilities(Screen *, char, PyObject *);
void screen_set_8bit_controls(Screen *, bool);
void screen_request_capabilities(Screen *, char, const char *);
void report_device_attributes(Screen *self, unsigned int UNUSED mode, char start_modifier);
void select_graphic_rendition(Screen *self, int *params, unsigned int count, Region*);
void report_device_status(Screen *self, unsigned int which, bool UNUSED);
@ -285,6 +272,7 @@ int screen_cursor_at_a_shell_prompt(const Screen *);
bool screen_fake_move_cursor_to_position(Screen *, index_type x, index_type y);
bool screen_send_signal_for_key(Screen *, char key);
bool get_line_edge_colors(Screen *self, color_type *left, color_type *right);
void parse_sgr(Screen *screen, const uint8_t *buf, unsigned int num, int *params, const char *report_name, Region *region);
#define DECLARE_CH_SCREEN_HANDLER(name) void screen_##name(Screen *screen);
DECLARE_CH_SCREEN_HANDLER(bell)
DECLARE_CH_SCREEN_HANDLER(backspace)

File diff suppressed because it is too large Load diff

View file

@ -17,6 +17,6 @@ typedef struct Parser {
} Parser;
Parser* alloc_parser(id_type window_id);
void parse_vte(Parser*);
void parse_vte_dump(Parser*);
Parser* alloc_vt_parser(id_type window_id);
void free_vt_parser(Parser*);
void reset_vt_parser(Parser*);

View file

@ -44,13 +44,13 @@
CURSOR_BEAM,
CURSOR_BLOCK,
CURSOR_UNDERLINE,
DCS,
ESC_DCS,
ESC_OSC,
GLFW_MOD_CONTROL,
GLFW_PRESS,
GLFW_RELEASE,
GLFW_REPEAT,
NO_CURSOR_SHAPE,
OSC,
SCROLL_FULL,
SCROLL_LINE,
SCROLL_PAGE,
@ -190,14 +190,16 @@ def modify_argv_for_launch_with_cwd(self, argv: List[str], env: Optional[Dict[st
return window.get_cwd_of_child(oldest=self.request_type is CwdRequestType.oldest) or ''
def process_title_from_child(title: str, is_base64: bool) -> str:
def process_title_from_child(title: memoryview, is_base64: bool, default_title: str) -> str:
if is_base64:
from base64 import standard_b64decode
try:
title = standard_b64decode(title).decode('utf-8', 'replace')
stitle = standard_b64decode(title).decode('utf-8', 'replace')
except Exception:
title = 'undecodeable title'
return sanitize_title(title)
stitle = 'undecodeable title'
else:
stitle = str(title, 'utf-8', 'replace')
return sanitize_title(stitle or default_title)
@lru_cache(maxsize=64)
@ -446,7 +448,7 @@ def cmd_output(screen: Screen, which: CommandOutput = CommandOutput.last_run, as
return ''.join(lines)
def process_remote_print(msg: str) -> str:
def process_remote_print(msg: memoryview) -> str:
from base64 import standard_b64decode
from .cli import green
@ -1004,7 +1006,8 @@ def osc_1337(self, raw_data: str) -> None:
ukey, has_equal, uval = val.partition('=')
self.set_user_var(ukey, (base64_decode(uval) if uval else b'') if has_equal == '=' else None)
def desktop_notify(self, osc_code: int, raw_data: str) -> None:
def desktop_notify(self, osc_code: int, raw_datab: memoryview) -> None:
raw_data = str(raw_datab, 'utf-8', 'replace')
if osc_code == 1337:
self.osc_1337(raw_data)
if osc_code == 777:
@ -1016,9 +1019,6 @@ def desktop_notify(self, osc_code: int, raw_data: str) -> None:
if cmd is not None and osc_code == 99:
self.prev_osc99_cmd = cmd
def use_utf8(self, on: bool) -> None:
get_boss().child_monitor.set_iutf8_winid(self.id, on)
def on_mouse_event(self, event: Dict[str, Any]) -> bool:
event['mods'] = event.get('mods', 0) & mod_mask
ev = MouseEvent(**event)
@ -1119,13 +1119,13 @@ def focus_changed(self, focused: bool) -> None:
# Cancel IME composition after loses focus
update_ime_position_for_window(self.id, False, -1)
def title_changed(self, new_title: Optional[str], is_base64: bool = False) -> None:
self.child_title = process_title_from_child(new_title or self.default_title, is_base64)
def title_changed(self, new_title: Optional[memoryview], is_base64: bool = False) -> None:
self.child_title = process_title_from_child(new_title or memoryview(b''), is_base64, self.default_title)
self.call_watchers(self.watchers.on_title_change, {'title': self.child_title, 'from_child': True})
if self.override_title is None:
self.title_updated()
def icon_changed(self, new_icon: object) -> None:
def icon_changed(self, new_icon: memoryview) -> None:
pass # TODO: Implement this
@property
@ -1190,20 +1190,20 @@ def report_color(self, code: str, r: int, g: int, b: int) -> None:
r |= r << 8
g |= g << 8
b |= b << 8
self.screen.send_escape_code_to_child(OSC, f'{code};rgb:{r:04x}/{g:04x}/{b:04x}')
self.screen.send_escape_code_to_child(ESC_OSC, f'{code};rgb:{r:04x}/{g:04x}/{b:04x}')
def report_notification_activated(self, identifier: str) -> None:
identifier = sanitize_identifier_pat().sub('', identifier)
self.screen.send_escape_code_to_child(OSC, f'99;i={identifier};')
self.screen.send_escape_code_to_child(ESC_OSC, f'99;i={identifier};')
def set_dynamic_color(self, code: int, value: Union[str, bytes]) -> None:
if isinstance(value, bytes):
value = value.decode('utf-8')
def set_dynamic_color(self, code: int, value: Union[str, bytes, memoryview] = '') -> None:
if isinstance(value, (bytes, memoryview)):
value = str(value, 'utf-8', 'replace')
if code == 22:
ret = set_pointer_shape(self.screen, value, self.os_window_id)
if ret:
self.screen.send_escape_code_to_child(OSC, '22:' + ret)
self.screen.send_escape_code_to_child(ESC_OSC, '22:' + ret)
color_changes: Dict[DynamicColor, Optional[str]] = {}
for val in value.split(';'):
w = DYNAMIC_COLOR_CODES.get(code)
@ -1218,7 +1218,8 @@ def set_dynamic_color(self, code: int, value: Union[str, bytes]) -> None:
if color_changes:
self.change_colors(color_changes)
def set_color_table_color(self, code: int, value: str) -> None:
def set_color_table_color(self, code: int, bvalue: Optional[memoryview] = None) -> None:
value = str(bvalue or b'', 'utf-8', 'replace')
cp = self.screen.color_profile
if code == 4:
changed = False
@ -1247,12 +1248,12 @@ def set_color_table_color(self, code: int, value: str) -> None:
def request_capabilities(self, q: str) -> None:
for result in get_capabilities(q, get_options()):
self.screen.send_escape_code_to_child(DCS, result)
self.screen.send_escape_code_to_child(ESC_DCS, result)
def handle_remote_cmd(self, cmd: str) -> None:
def handle_remote_cmd(self, cmd: memoryview) -> None:
get_boss().handle_remote_cmd(cmd, self)
def handle_remote_echo(self, msg: str) -> None:
def handle_remote_echo(self, msg: memoryview) -> None:
from base64 import standard_b64decode
data = standard_b64decode(msg)
# ensure we are not writing any control char back as this can lead to command injection on shell prompts
@ -1260,12 +1261,12 @@ def handle_remote_echo(self, msg: str) -> None:
data = re.sub(rb'[^ -~]', b'', data)
self.write_to_child(data)
def handle_remote_ssh(self, msg: str) -> None:
def handle_remote_ssh(self, msg: memoryview) -> None:
from kittens.ssh.utils import get_ssh_data
for line in get_ssh_data(msg, f'{os.getpid()}-{self.id}'):
self.write_to_child(line)
def handle_kitten_result(self, msg: str) -> None:
def handle_kitten_result(self, msg: memoryview) -> None:
import base64
self.kitten_result = json.loads(base64.b85decode(msg))
for processor in self.kitten_result_processors:
@ -1278,17 +1279,18 @@ def handle_kitten_result(self, msg: str) -> None:
def add_kitten_result_processor(self, callback: Callable[['Window', Any], None]) -> None:
self.kitten_result_processors.append(callback)
def handle_overlay_ready(self, msg: str) -> None:
def handle_overlay_ready(self, msg: memoryview) -> None:
boss = get_boss()
tab = boss.tab_for_window(self)
if tab is not None:
tab.move_window_to_top_of_group(self)
def append_remote_data(self, msg: str) -> str:
if not msg:
def append_remote_data(self, msgb: memoryview) -> str:
if not msgb:
cdata = ''.join(self.current_remote_data)
self.current_remote_data = []
return cdata
msg = str(msgb, 'utf-8', 'replace')
num, rest = msg.split(':', 1)
max_size = get_options().clipboard_max_size * 1024 * 1024
if num == '0' or sum(map(len, self.current_remote_data)) > max_size:
@ -1296,13 +1298,13 @@ def append_remote_data(self, msg: str) -> str:
self.current_remote_data.append(rest)
return ''
def handle_remote_edit(self, msg: str) -> None:
def handle_remote_edit(self, msg: memoryview) -> None:
cdata = self.append_remote_data(msg)
if cdata:
from .launch import remote_edit
remote_edit(cdata, self)
def handle_remote_clone(self, msg: str) -> None:
def handle_remote_clone(self, msg: memoryview) -> None:
cdata = self.append_remote_data(msg)
if cdata:
ac = get_options().allow_cloning
@ -1321,8 +1323,9 @@ def handle_remote_clone_confirmation(self, cdata: str, confirmed: bool) -> None:
from .launch import clone_and_launch
clone_and_launch(cdata, self)
def handle_remote_askpass(self, msg: str) -> None:
def handle_remote_askpass(self, msgb: memoryview) -> None:
from .shm import SharedMemory
msg = str(msgb, 'utf-8')
with SharedMemory(name=msg, readonly=True) as shm:
shm.seek(1)
data = json.loads(shm.read_data_with_size())
@ -1350,17 +1353,17 @@ def callback(ans: Any) -> None:
else:
log_error(f'Ignoring ask request with unknown type: {data["type"]}')
def handle_remote_print(self, msg: str) -> None:
def handle_remote_print(self, msg: memoryview) -> None:
text = process_remote_print(msg)
print(text, end='', flush=True)
def send_cmd_response(self, response: Any) -> None:
self.screen.send_escape_code_to_child(DCS, '@kitty-cmd' + json.dumps(response))
self.screen.send_escape_code_to_child(ESC_DCS, '@kitty-cmd' + json.dumps(response))
def file_transmission(self, data: str) -> None:
def file_transmission(self, data: memoryview) -> None:
self.file_transmission_control.handle_serialized_command(data)
def clipboard_control(self, data: str, is_partial: Optional[bool] = False) -> None:
def clipboard_control(self, data: memoryview, is_partial: Optional[bool] = False) -> None:
if is_partial is None:
self.clipboard_request_manager.parse_osc_5522(data)
else:

View file

@ -16,7 +16,7 @@
from unittest import TestCase
from kitty.config import finalize_keys, finalize_mouse_mappings
from kitty.fast_data_types import Cursor, HistoryBuf, LineBuf, Screen, get_options, parse_bytes, set_options
from kitty.fast_data_types import Cursor, HistoryBuf, LineBuf, Screen, get_options, set_options
from kitty.options.parse import merge_result_dicts
from kitty.options.types import Options, defaults
from kitty.types import MouseEvent
@ -323,7 +323,7 @@ def process_input_from_child(self, timeout=10):
break
bytes_read += len(data)
self.received_bytes += data
parse_bytes(self.screen, data)
self.screen.vt_parser.parse_bytes(self.screen, data)
return bytes_read
def wait_till(self, q, timeout=10):

View file

@ -614,8 +614,6 @@ def get_vcs_rev() -> str:
def get_source_specific_defines(env: Env, src: str) -> Tuple[str, Optional[List[str]]]:
if src == 'kitty/parser_dump.c':
return 'kitty/parser.c', ['DUMP_COMMANDS']
if src == 'kitty/vt-parser-dump.c':
return 'kitty/vt-parser.c', ['DUMP_COMMANDS']
if src == 'kitty/data-types.c':
@ -770,7 +768,6 @@ def find_c_files() -> Tuple[List[str], List[str]]:
ans.append(os.path.join('kitty', x))
elif ext == '.h':
headers.append(os.path.join('kitty', x))
ans.append('kitty/parser_dump.c')
ans.append('kitty/vt-parser-dump.c')
return ans, headers