Generate the code to parse APC escape codes
Allows it to be re-used for other APC codes if needed.
This commit is contained in:
parent
0b7abf5690
commit
af9f7bb0d2
@ -372,9 +372,9 @@ Key Value Default Description
|
|||||||
**Keys for deleting images**
|
**Keys for deleting images**
|
||||||
-----------------------------------------------------------
|
-----------------------------------------------------------
|
||||||
``d`` Single character. ``a`` What to delete.
|
``d`` Single character. ``a`` What to delete.
|
||||||
``(a, A, c, C, p,
|
``(a, A, c, C, i,
|
||||||
P, q, Q, x, X, y,
|
I, p, P, q, Q, x, X,
|
||||||
Y, z, Z)``.
|
y, Y, z, Z)``.
|
||||||
======= ==================== ========= =================
|
======= ==================== ========= =================
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
258
gen-apc-parsers.py
Executable file
258
gen-apc-parsers.py
Executable file
@ -0,0 +1,258 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# vim:fileencoding=utf-8
|
||||||
|
# License: GPLv3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>
|
||||||
|
|
||||||
|
from collections import defaultdict
|
||||||
|
|
||||||
|
|
||||||
|
def resolve_keys(keymap):
|
||||||
|
ans = defaultdict(list)
|
||||||
|
for ch, (attr, atype) in keymap.items():
|
||||||
|
if atype not in ('int', 'uint'):
|
||||||
|
atype = 'flag'
|
||||||
|
ans[atype].append(ch)
|
||||||
|
return ans
|
||||||
|
|
||||||
|
|
||||||
|
def enum(keymap):
|
||||||
|
lines = []
|
||||||
|
for ch, (attr, atype) in keymap.items():
|
||||||
|
lines.append(f"{attr}='{ch}'")
|
||||||
|
return '''
|
||||||
|
enum KEYS {{
|
||||||
|
{}
|
||||||
|
}};
|
||||||
|
'''.format(',\n'.join(lines))
|
||||||
|
|
||||||
|
|
||||||
|
def parse_key(keymap):
|
||||||
|
lines = []
|
||||||
|
for attr, atype in keymap.values():
|
||||||
|
vs = atype.upper() if atype in ('uint', 'int') else 'FLAG'
|
||||||
|
lines.append(f'case {attr}: value_state = {vs}; break;')
|
||||||
|
return ' \n'.join(lines)
|
||||||
|
|
||||||
|
|
||||||
|
def parse_flag(keymap, type_map, command_class):
|
||||||
|
lines = []
|
||||||
|
for ch in type_map['flag']:
|
||||||
|
attr, allowed_values = keymap[ch]
|
||||||
|
q = ' && '.join(f"g.{attr} != '{x}'" for x in allowed_values)
|
||||||
|
lines.append(f'''
|
||||||
|
case {attr}: {{
|
||||||
|
g.{attr} = screen->parser_buf[pos++] & 0xff;
|
||||||
|
if ({q}) {{
|
||||||
|
REPORT_ERROR("Malformed {command_class} control block, unknown flag value for {attr}: 0x%x", g.{attr});
|
||||||
|
return;
|
||||||
|
}};
|
||||||
|
}}
|
||||||
|
break;
|
||||||
|
''')
|
||||||
|
return ' \n'.join(lines)
|
||||||
|
|
||||||
|
|
||||||
|
def parse_number(keymap):
|
||||||
|
int_keys = [f'I({attr})' for attr, atype in keymap.values() if atype == 'int']
|
||||||
|
uint_keys = [f'U({attr})' for attr, atype in keymap.values() if atype == 'uint']
|
||||||
|
return '; '.join(int_keys), '; '.join(uint_keys)
|
||||||
|
|
||||||
|
|
||||||
|
def cmd_for_report(report_name, keymap, type_map, payload_allowed):
|
||||||
|
def group(atype, conv):
|
||||||
|
flag_fmt, flag_attrs = [], []
|
||||||
|
cv = {'flag': 'c', 'int': 'i', 'uint': 'I'}[atype]
|
||||||
|
for ch in type_map[atype]:
|
||||||
|
flag_fmt.append('s' + cv)
|
||||||
|
attr = keymap[ch][0]
|
||||||
|
flag_attrs.append(f'"{attr}", {conv}g.{attr}')
|
||||||
|
return ' '.join(flag_fmt), ', '.join(flag_attrs)
|
||||||
|
|
||||||
|
flag_fmt, flag_attrs = group('flag', '')
|
||||||
|
int_fmt, int_attrs = group('int', '(int)')
|
||||||
|
uint_fmt, uint_attrs = group('uint', '(unsigned int)')
|
||||||
|
|
||||||
|
fmt = f'{flag_fmt} {uint_fmt} {int_fmt}'
|
||||||
|
if payload_allowed:
|
||||||
|
ans = [f'REPORT_VA_COMMAND("s {{{fmt} sI}} y#", "{report_name}",']
|
||||||
|
else:
|
||||||
|
ans = [f'REPORT_VA_COMMAND("s {{{fmt}}}", "{report_name}",']
|
||||||
|
ans.append(',\n '.join((flag_attrs, uint_attrs, int_attrs)))
|
||||||
|
if payload_allowed:
|
||||||
|
ans.append(', "payload_sz", g.payload_sz, payload, g.payload_sz')
|
||||||
|
ans.append(');')
|
||||||
|
return '\n'.join(ans)
|
||||||
|
|
||||||
|
|
||||||
|
def generate(function_name, callback_name, report_name, keymap, command_class, initial_key='a', payload_allowed=True):
|
||||||
|
type_map = resolve_keys(keymap)
|
||||||
|
keys_enum = enum(keymap)
|
||||||
|
handle_key = parse_key(keymap)
|
||||||
|
flag_keys = parse_flag(keymap, type_map, command_class)
|
||||||
|
int_keys, uint_keys = parse_number(keymap)
|
||||||
|
report_cmd = cmd_for_report(report_name, keymap, type_map, payload_allowed)
|
||||||
|
if payload_allowed:
|
||||||
|
payload_after_value = "case ';': state = PAYLOAD; break;"
|
||||||
|
payload = payload = ', PAYLOAD'
|
||||||
|
parr = 'static uint8_t payload[4096];'
|
||||||
|
payload_case = f'''
|
||||||
|
case PAYLOAD: {{
|
||||||
|
sz = screen->parser_buf_pos - pos;
|
||||||
|
const char *err = base64_decode(screen->parser_buf + pos, sz, payload, sizeof(payload), &g.payload_sz);
|
||||||
|
if (err != NULL) {{ REPORT_ERROR("Failed to parse {command_class} command payload with error: %s", err); return; }}
|
||||||
|
pos = screen->parser_buf_pos;
|
||||||
|
}}
|
||||||
|
break;
|
||||||
|
'''
|
||||||
|
callback = f'{callback_name}(screen, &g, payload)'
|
||||||
|
else:
|
||||||
|
payload_after_value = payload = parr = payload_case = ''
|
||||||
|
callback = f'{callback_name}(screen, &g)'
|
||||||
|
|
||||||
|
return f'''
|
||||||
|
static inline void
|
||||||
|
{function_name}(Screen *screen, PyObject UNUSED *dump_callback) {{
|
||||||
|
unsigned int pos = 1;
|
||||||
|
enum PARSER_STATES {{ KEY, EQUAL, UINT, INT, FLAG, AFTER_VALUE {payload} }};
|
||||||
|
enum PARSER_STATES state = KEY, value_state = FLAG;
|
||||||
|
static {command_class} g;
|
||||||
|
unsigned int i, code;
|
||||||
|
uint64_t lcode;
|
||||||
|
bool is_negative;
|
||||||
|
memset(&g, 0, sizeof(g));
|
||||||
|
size_t sz;
|
||||||
|
{parr}
|
||||||
|
{keys_enum}
|
||||||
|
enum KEYS key = '{initial_key}';
|
||||||
|
|
||||||
|
while (pos < screen->parser_buf_pos) {{
|
||||||
|
switch(state) {{
|
||||||
|
case KEY:
|
||||||
|
key = screen->parser_buf[pos++];
|
||||||
|
state = EQUAL;
|
||||||
|
switch(key) {{
|
||||||
|
{handle_key}
|
||||||
|
default:
|
||||||
|
REPORT_ERROR("Malformed {command_class} control block, invalid key character: 0x%x", key);
|
||||||
|
return;
|
||||||
|
}}
|
||||||
|
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]);
|
||||||
|
return;
|
||||||
|
}}
|
||||||
|
state = value_state;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FLAG:
|
||||||
|
switch(key) {{
|
||||||
|
{flag_keys}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}}
|
||||||
|
state = AFTER_VALUE;
|
||||||
|
break;
|
||||||
|
|
||||||
|
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; \\
|
||||||
|
}} \\
|
||||||
|
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; \\
|
||||||
|
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++; }}
|
||||||
|
#define I(x) case x: g.x = is_negative ? 0 - (int32_t)code : (int32_t)code; break
|
||||||
|
READ_UINT;
|
||||||
|
switch(key) {{
|
||||||
|
{int_keys};
|
||||||
|
default: break;
|
||||||
|
}}
|
||||||
|
state = AFTER_VALUE;
|
||||||
|
break;
|
||||||
|
#undef I
|
||||||
|
case UINT:
|
||||||
|
READ_UINT;
|
||||||
|
#define U(x) case x: g.x = code; break
|
||||||
|
switch(key) {{
|
||||||
|
{uint_keys};
|
||||||
|
default: break;
|
||||||
|
}}
|
||||||
|
state = AFTER_VALUE;
|
||||||
|
break;
|
||||||
|
#undef U
|
||||||
|
#undef READ_UINT
|
||||||
|
|
||||||
|
case AFTER_VALUE:
|
||||||
|
switch (screen->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]);
|
||||||
|
return;
|
||||||
|
case ',':
|
||||||
|
state = KEY;
|
||||||
|
break;
|
||||||
|
{payload_after_value}
|
||||||
|
}}
|
||||||
|
break;
|
||||||
|
|
||||||
|
{payload_case}
|
||||||
|
|
||||||
|
}} // end switch
|
||||||
|
}} // end while
|
||||||
|
|
||||||
|
switch(state) {{
|
||||||
|
case EQUAL:
|
||||||
|
REPORT_ERROR("Malformed {command_class} control block, no = after key"); return;
|
||||||
|
case INT:
|
||||||
|
case UINT:
|
||||||
|
REPORT_ERROR("Malformed {command_class} control block, expecting an integer value"); return;
|
||||||
|
case FLAG:
|
||||||
|
REPORT_ERROR("Malformed {command_class} control block, expecting a flag value"); return;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}}
|
||||||
|
|
||||||
|
{report_cmd}
|
||||||
|
|
||||||
|
{callback};
|
||||||
|
}}
|
||||||
|
'''
|
||||||
|
|
||||||
|
|
||||||
|
def graphics_parser():
|
||||||
|
flag = frozenset
|
||||||
|
keymap = {
|
||||||
|
'a': ('action', flag('tTqpd')),
|
||||||
|
'd': ('delete_action', flag('aAiIcCpPqQxXyYzZ')),
|
||||||
|
't': ('transmission_type', flag('dfts')),
|
||||||
|
'o': ('compressed', flag('z')),
|
||||||
|
'f': ('format', 'uint'),
|
||||||
|
'm': ('more', 'uint'),
|
||||||
|
'i': ('id', 'uint'),
|
||||||
|
'w': ('width', 'uint'),
|
||||||
|
'h': ('height', 'uint'),
|
||||||
|
'x': ('x_offset', 'uint'),
|
||||||
|
'y': ('y_offset', 'uint'),
|
||||||
|
'v': ('data_height', 'uint'),
|
||||||
|
's': ('data_width', 'uint'),
|
||||||
|
'S': ('data_sz', 'uint'),
|
||||||
|
'O': ('data_offset', 'uint'),
|
||||||
|
'c': ('num_cells', 'uint'),
|
||||||
|
'r': ('num_lines', 'uint'),
|
||||||
|
'X': ('cell_x_offset', 'uint'),
|
||||||
|
'Y': ('cell_y_offset', 'uint'),
|
||||||
|
'z': ('z_index', 'int'),
|
||||||
|
}
|
||||||
|
text = generate('parse_graphics_code', 'screen_handle_graphics_command', 'graphics_command', keymap, 'GraphicsCommand')
|
||||||
|
|
||||||
|
with open('kitty/parse-graphics-command.h', 'w') as f:
|
||||||
|
print('#pragma once', file=f)
|
||||||
|
print(text, file=f)
|
||||||
|
|
||||||
|
|
||||||
|
graphics_parser()
|
||||||
209
kitty/parse-graphics-command.h
Normal file
209
kitty/parse-graphics-command.h
Normal file
@ -0,0 +1,209 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
parse_graphics_code(Screen *screen, PyObject UNUSED *dump_callback) {
|
||||||
|
unsigned int pos = 1;
|
||||||
|
enum PARSER_STATES { KEY, EQUAL, UINT, INT, FLAG, AFTER_VALUE , PAYLOAD };
|
||||||
|
enum PARSER_STATES state = KEY, value_state = FLAG;
|
||||||
|
static GraphicsCommand g;
|
||||||
|
unsigned int i, code;
|
||||||
|
uint64_t lcode;
|
||||||
|
bool is_negative;
|
||||||
|
memset(&g, 0, sizeof(g));
|
||||||
|
size_t sz;
|
||||||
|
static uint8_t payload[4096];
|
||||||
|
|
||||||
|
enum KEYS {
|
||||||
|
action='a',
|
||||||
|
delete_action='d',
|
||||||
|
transmission_type='t',
|
||||||
|
compressed='o',
|
||||||
|
format='f',
|
||||||
|
more='m',
|
||||||
|
id='i',
|
||||||
|
width='w',
|
||||||
|
height='h',
|
||||||
|
x_offset='x',
|
||||||
|
y_offset='y',
|
||||||
|
data_height='v',
|
||||||
|
data_width='s',
|
||||||
|
data_sz='S',
|
||||||
|
data_offset='O',
|
||||||
|
num_cells='c',
|
||||||
|
num_lines='r',
|
||||||
|
cell_x_offset='X',
|
||||||
|
cell_y_offset='Y',
|
||||||
|
z_index='z'
|
||||||
|
};
|
||||||
|
|
||||||
|
enum KEYS key = 'a';
|
||||||
|
|
||||||
|
while (pos < screen->parser_buf_pos) {
|
||||||
|
switch(state) {
|
||||||
|
case KEY:
|
||||||
|
key = screen->parser_buf[pos++];
|
||||||
|
state = EQUAL;
|
||||||
|
switch(key) {
|
||||||
|
case action: value_state = FLAG; break;
|
||||||
|
case delete_action: value_state = FLAG; break;
|
||||||
|
case transmission_type: value_state = FLAG; break;
|
||||||
|
case compressed: value_state = FLAG; break;
|
||||||
|
case format: value_state = UINT; break;
|
||||||
|
case more: value_state = UINT; break;
|
||||||
|
case id: value_state = UINT; break;
|
||||||
|
case width: value_state = UINT; break;
|
||||||
|
case height: value_state = UINT; break;
|
||||||
|
case x_offset: value_state = UINT; break;
|
||||||
|
case y_offset: value_state = UINT; break;
|
||||||
|
case data_height: value_state = UINT; break;
|
||||||
|
case data_width: value_state = UINT; break;
|
||||||
|
case data_sz: value_state = UINT; break;
|
||||||
|
case data_offset: value_state = UINT; break;
|
||||||
|
case num_cells: value_state = UINT; break;
|
||||||
|
case num_lines: value_state = UINT; break;
|
||||||
|
case cell_x_offset: value_state = UINT; break;
|
||||||
|
case cell_y_offset: value_state = UINT; break;
|
||||||
|
case z_index: value_state = INT; break;
|
||||||
|
default:
|
||||||
|
REPORT_ERROR("Malformed GraphicsCommand control block, invalid key character: 0x%x", key);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EQUAL:
|
||||||
|
if (screen->parser_buf[pos++] != '=') {
|
||||||
|
REPORT_ERROR("Malformed GraphicsCommand control block, no = after key, found: 0x%x instead", screen->parser_buf[pos-1]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
state = value_state;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FLAG:
|
||||||
|
switch(key) {
|
||||||
|
|
||||||
|
case action: {
|
||||||
|
g.action = screen->parser_buf[pos++] & 0xff;
|
||||||
|
if (g.action != 'q' && g.action != 't' && g.action != 'p' && g.action != 'T' && g.action != 'd') {
|
||||||
|
REPORT_ERROR("Malformed GraphicsCommand control block, unknown flag value for action: 0x%x", g.action);
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
|
||||||
|
case delete_action: {
|
||||||
|
g.delete_action = screen->parser_buf[pos++] & 0xff;
|
||||||
|
if (g.delete_action != 'A' && g.delete_action != 'q' && g.delete_action != 'x' && g.delete_action != 'Y' && g.delete_action != 'z' && g.delete_action != 'a' && g.delete_action != 'Z' && g.delete_action != 'p' && g.delete_action != 'Q' && g.delete_action != 'c' && g.delete_action != 'P' && g.delete_action != 'y' && g.delete_action != 'C' && g.delete_action != 'i' && g.delete_action != 'X' && g.delete_action != 'I') {
|
||||||
|
REPORT_ERROR("Malformed GraphicsCommand control block, unknown flag value for delete_action: 0x%x", g.delete_action);
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
|
||||||
|
case transmission_type: {
|
||||||
|
g.transmission_type = screen->parser_buf[pos++] & 0xff;
|
||||||
|
if (g.transmission_type != 't' && g.transmission_type != 'f' && g.transmission_type != 's' && g.transmission_type != 'd') {
|
||||||
|
REPORT_ERROR("Malformed GraphicsCommand control block, unknown flag value for transmission_type: 0x%x", g.transmission_type);
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
|
||||||
|
case compressed: {
|
||||||
|
g.compressed = screen->parser_buf[pos++] & 0xff;
|
||||||
|
if (g.compressed != 'z') {
|
||||||
|
REPORT_ERROR("Malformed GraphicsCommand control block, unknown flag value for compressed: 0x%x", g.compressed);
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
state = AFTER_VALUE;
|
||||||
|
break;
|
||||||
|
|
||||||
|
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; \
|
||||||
|
} \
|
||||||
|
if (i == pos) { REPORT_ERROR("Malformed GraphicsCommand control block, expecting an integer value for key: %c", key & 0xFF); return; } \
|
||||||
|
lcode = utoi(screen->parser_buf + pos, i - pos); pos = i; \
|
||||||
|
if (lcode > UINT32_MAX) { REPORT_ERROR("Malformed GraphicsCommand control block, number is too large"); return; } \
|
||||||
|
code = lcode;
|
||||||
|
|
||||||
|
is_negative = false;
|
||||||
|
if(screen->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) {
|
||||||
|
I(z_index);
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
state = AFTER_VALUE;
|
||||||
|
break;
|
||||||
|
#undef I
|
||||||
|
case UINT:
|
||||||
|
READ_UINT;
|
||||||
|
#define U(x) case x: g.x = code; break
|
||||||
|
switch(key) {
|
||||||
|
U(format); U(more); U(id); U(width); U(height); U(x_offset); U(y_offset); U(data_height); U(data_width); U(data_sz); U(data_offset); U(num_cells); U(num_lines); U(cell_x_offset); U(cell_y_offset);
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
state = AFTER_VALUE;
|
||||||
|
break;
|
||||||
|
#undef U
|
||||||
|
#undef READ_UINT
|
||||||
|
|
||||||
|
case AFTER_VALUE:
|
||||||
|
switch (screen->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]);
|
||||||
|
return;
|
||||||
|
case ',':
|
||||||
|
state = KEY;
|
||||||
|
break;
|
||||||
|
case ';': state = PAYLOAD; break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
|
||||||
|
case PAYLOAD: {
|
||||||
|
sz = screen->parser_buf_pos - pos;
|
||||||
|
const char *err = base64_decode(screen->parser_buf + pos, sz, payload, sizeof(payload), &g.payload_sz);
|
||||||
|
if (err != NULL) { REPORT_ERROR("Failed to parse GraphicsCommand command payload with error: %s", err); return; }
|
||||||
|
pos = screen->parser_buf_pos;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
|
||||||
|
} // end switch
|
||||||
|
} // end while
|
||||||
|
|
||||||
|
switch(state) {
|
||||||
|
case EQUAL:
|
||||||
|
REPORT_ERROR("Malformed GraphicsCommand control block, no = after key"); return;
|
||||||
|
case INT:
|
||||||
|
case UINT:
|
||||||
|
REPORT_ERROR("Malformed GraphicsCommand control block, expecting an integer value"); return;
|
||||||
|
case FLAG:
|
||||||
|
REPORT_ERROR("Malformed GraphicsCommand control block, expecting a flag value"); return;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
REPORT_VA_COMMAND("s {sc sc sc sc sI sI sI sI sI sI sI sI sI sI sI sI sI sI sI si sI} y#", "graphics_command",
|
||||||
|
"action", g.action, "delete_action", g.delete_action, "transmission_type", g.transmission_type, "compressed", g.compressed,
|
||||||
|
"format", (unsigned int)g.format, "more", (unsigned int)g.more, "id", (unsigned int)g.id, "width", (unsigned int)g.width, "height", (unsigned int)g.height, "x_offset", (unsigned int)g.x_offset, "y_offset", (unsigned int)g.y_offset, "data_height", (unsigned int)g.data_height, "data_width", (unsigned int)g.data_width, "data_sz", (unsigned int)g.data_sz, "data_offset", (unsigned int)g.data_offset, "num_cells", (unsigned int)g.num_cells, "num_lines", (unsigned int)g.num_lines, "cell_x_offset", (unsigned int)g.cell_x_offset, "cell_y_offset", (unsigned int)g.cell_y_offset,
|
||||||
|
"z_index", (int)g.z_index
|
||||||
|
, "payload_sz", g.payload_sz, payload, g.payload_sz
|
||||||
|
);
|
||||||
|
|
||||||
|
screen_handle_graphics_command(screen, &g, payload);
|
||||||
|
}
|
||||||
|
|
||||||
156
kitty/parser.c
156
kitty/parser.c
@ -824,161 +824,7 @@ dispatch_dcs(Screen *screen, PyObject DUMP_UNUSED *dump_callback) {
|
|||||||
|
|
||||||
// APC mode {{{
|
// APC mode {{{
|
||||||
|
|
||||||
static inline void
|
#include "parse-graphics-command.h"
|
||||||
parse_graphics_code(Screen *screen, PyObject UNUSED *dump_callback) {
|
|
||||||
unsigned int pos = 1;
|
|
||||||
enum GR_STATES { KEY, EQUAL, UINT, INT, FLAG, AFTER_VALUE, PAYLOAD };
|
|
||||||
enum GR_STATES state = KEY, value_state = FLAG;
|
|
||||||
enum KEYS {
|
|
||||||
action='a',
|
|
||||||
delete_action='d',
|
|
||||||
transmission_type='t',
|
|
||||||
compressed='o',
|
|
||||||
format = 'f',
|
|
||||||
more = 'm',
|
|
||||||
id = 'i',
|
|
||||||
width = 'w',
|
|
||||||
height = 'h',
|
|
||||||
x_offset = 'x',
|
|
||||||
y_offset = 'y',
|
|
||||||
data_height = 'v',
|
|
||||||
data_width = 's',
|
|
||||||
data_sz = 'S',
|
|
||||||
data_offset = 'O',
|
|
||||||
num_cells = 'c',
|
|
||||||
num_lines = 'r',
|
|
||||||
cell_x_offset = 'X',
|
|
||||||
cell_y_offset = 'Y',
|
|
||||||
z_index = 'z'
|
|
||||||
};
|
|
||||||
enum KEYS key = 'a';
|
|
||||||
static GraphicsCommand g;
|
|
||||||
unsigned int i, code;
|
|
||||||
uint64_t lcode;
|
|
||||||
bool is_negative;
|
|
||||||
memset(&g, 0, sizeof(g));
|
|
||||||
static uint8_t payload[4096];
|
|
||||||
size_t sz;
|
|
||||||
const char *err;
|
|
||||||
|
|
||||||
while (pos < screen->parser_buf_pos) {
|
|
||||||
switch(state) {
|
|
||||||
|
|
||||||
case KEY:
|
|
||||||
key = screen->parser_buf[pos++];
|
|
||||||
switch(key) {
|
|
||||||
#define KS(n, vs) case n: state = EQUAL; value_state = vs; break
|
|
||||||
#define U(x) KS(x, UINT)
|
|
||||||
KS(action, FLAG); KS(delete_action, FLAG); KS(transmission_type, FLAG); KS(compressed, FLAG); KS(z_index, INT);
|
|
||||||
U(format); U(more); U(id); U(data_sz); U(data_offset); U(width); U(height); U(x_offset); U(y_offset); U(data_height); U(data_width); U(num_cells); U(num_lines); U(cell_x_offset); U(cell_y_offset);
|
|
||||||
#undef U
|
|
||||||
#undef KS
|
|
||||||
default:
|
|
||||||
REPORT_ERROR("Malformed graphics control block, invalid key character: 0x%x", key);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case EQUAL:
|
|
||||||
if (screen->parser_buf[pos++] != '=') {
|
|
||||||
REPORT_ERROR("Malformed graphics control block, no = after key, found: 0x%x instead", screen->parser_buf[pos-1]);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
state = value_state;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case FLAG:
|
|
||||||
switch(key) {
|
|
||||||
#define F(a) case a: g.a = screen->parser_buf[pos++] & 0xff; break
|
|
||||||
F(action); F(delete_action); F(transmission_type); F(compressed);
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
state = AFTER_VALUE;
|
|
||||||
break;
|
|
||||||
#undef F
|
|
||||||
|
|
||||||
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; \
|
|
||||||
} \
|
|
||||||
if (i == pos) { REPORT_ERROR("Malformed graphics control block, expecting an integer value for key: %c", key & 0xFF); return; } \
|
|
||||||
lcode = utoi(screen->parser_buf + pos, i - pos); pos = i; \
|
|
||||||
if (lcode > UINT32_MAX) { REPORT_ERROR("id is too large"); return; } \
|
|
||||||
code = lcode;
|
|
||||||
|
|
||||||
is_negative = false;
|
|
||||||
if(screen->parser_buf[pos] == '-') { is_negative = true; pos++; }
|
|
||||||
#define U(x) case x: g.x = is_negative ? 0 - (int32_t)code : (int32_t)code; break
|
|
||||||
READ_UINT;
|
|
||||||
switch(key) {
|
|
||||||
U(z_index);
|
|
||||||
default: break;
|
|
||||||
}
|
|
||||||
state = AFTER_VALUE;
|
|
||||||
break;
|
|
||||||
#undef U
|
|
||||||
case UINT:
|
|
||||||
READ_UINT;
|
|
||||||
#define U(x) case x: g.x = code; break
|
|
||||||
switch(key) {
|
|
||||||
U(format); U(more); U(id); U(data_sz); U(data_offset); U(width); U(height); U(x_offset); U(y_offset); U(data_height); U(data_width); U(num_cells); U(num_lines); U(cell_x_offset); U(cell_y_offset);
|
|
||||||
default: break;
|
|
||||||
}
|
|
||||||
state = AFTER_VALUE;
|
|
||||||
break;
|
|
||||||
#undef U
|
|
||||||
#undef SET_ATTR
|
|
||||||
#undef READ_UINT
|
|
||||||
case AFTER_VALUE:
|
|
||||||
switch (screen->parser_buf[pos++]) {
|
|
||||||
case ',':
|
|
||||||
state = KEY;
|
|
||||||
break;
|
|
||||||
case ';':
|
|
||||||
state = PAYLOAD;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
REPORT_ERROR("Malformed graphics control block, expecting a comma or semi-colon after a value, found: 0x%x", screen->parser_buf[pos - 1]);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case PAYLOAD:
|
|
||||||
sz = screen->parser_buf_pos - pos;
|
|
||||||
err = base64_decode(screen->parser_buf + pos, sz, payload, sizeof(payload), &g.payload_sz);
|
|
||||||
if (err != NULL) { REPORT_ERROR("Failed to parse graphics command payload with error: %s", err); return; }
|
|
||||||
pos = screen->parser_buf_pos;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
switch(state) {
|
|
||||||
case EQUAL:
|
|
||||||
REPORT_ERROR("Malformed graphics control block, no = after key"); return;
|
|
||||||
case INT:
|
|
||||||
case UINT:
|
|
||||||
REPORT_ERROR("Malformed graphics control block, expecting an integer value"); return;
|
|
||||||
case FLAG:
|
|
||||||
REPORT_ERROR("Malformed graphics control block, expecting a flag value"); return;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
#define A(x) #x, g.x
|
|
||||||
#define U(x) #x, (unsigned int)(g.x)
|
|
||||||
#define I(x) #x, (int)(g.x)
|
|
||||||
REPORT_VA_COMMAND("s {sc sc sc sc sI sI sI sI sI sI sI sI sI sI sI sI sI sI sI sI si} y#", "graphics_command",
|
|
||||||
A(action), A(delete_action), A(transmission_type), A(compressed),
|
|
||||||
U(format), U(more), U(id), U(data_sz), U(data_offset),
|
|
||||||
U(width), U(height), U(x_offset), U(y_offset), U(data_height), U(data_width), U(num_cells), U(num_lines), U(cell_x_offset), U(cell_y_offset),
|
|
||||||
U(payload_sz), I(z_index),
|
|
||||||
payload, g.payload_sz
|
|
||||||
);
|
|
||||||
#undef U
|
|
||||||
#undef A
|
|
||||||
#undef I
|
|
||||||
screen_handle_graphics_command(screen, &g, payload);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void
|
static inline void
|
||||||
dispatch_apc(Screen *screen, PyObject DUMP_UNUSED *dump_callback) {
|
dispatch_apc(Screen *screen, PyObject DUMP_UNUSED *dump_callback) {
|
||||||
|
|||||||
@ -265,19 +265,19 @@ class TestParser(BaseTest):
|
|||||||
pb = partial(self.parse_bytes_dump, s)
|
pb = partial(self.parse_bytes_dump, s)
|
||||||
uint32_max = 2**32 - 1
|
uint32_max = 2**32 - 1
|
||||||
t('i=%d' % uint32_max, id=uint32_max)
|
t('i=%d' % uint32_max, id=uint32_max)
|
||||||
e('i=%d' % (uint32_max + 1), 'id is too large')
|
e('i=%d' % (uint32_max + 1), 'Malformed GraphicsCommand control block, number is too large')
|
||||||
pb('\033_Gi=12\033\\', c(id=12))
|
pb('\033_Gi=12\033\\', c(id=12))
|
||||||
t('a=t,t=d,s=100,z=-9', payload='X', action='t', transmission_type='d', data_width=100, z_index=-9, payload_sz=1)
|
t('a=t,t=d,s=100,z=-9', payload='X', action='t', transmission_type='d', data_width=100, z_index=-9, payload_sz=1)
|
||||||
t('a=t,t=d,s=100,z=9', payload='payload', action='t', transmission_type='d', data_width=100, z_index=9, payload_sz=7)
|
t('a=t,t=d,s=100,z=9', payload='payload', action='t', transmission_type='d', data_width=100, z_index=9, payload_sz=7)
|
||||||
t('a=t,t=d,s=100,z=9', action='t', transmission_type='d', data_width=100, z_index=9)
|
t('a=t,t=d,s=100,z=9', action='t', transmission_type='d', data_width=100, z_index=9)
|
||||||
e(',s=1', 'Malformed graphics control block, invalid key character: 0x2c')
|
e(',s=1', 'Malformed GraphicsCommand control block, invalid key character: 0x2c')
|
||||||
e('W=1', 'Malformed graphics control block, invalid key character: 0x57')
|
e('W=1', 'Malformed GraphicsCommand control block, invalid key character: 0x57')
|
||||||
e('1=1', 'Malformed graphics control block, invalid key character: 0x31')
|
e('1=1', 'Malformed GraphicsCommand control block, invalid key character: 0x31')
|
||||||
e('a=t,,w=2', 'Malformed graphics control block, invalid key character: 0x2c')
|
e('a=t,,w=2', 'Malformed GraphicsCommand control block, invalid key character: 0x2c')
|
||||||
e('s', 'Malformed graphics control block, no = after key')
|
e('s', 'Malformed GraphicsCommand control block, no = after key')
|
||||||
e('s=', 'Malformed graphics control block, expecting an integer value')
|
e('s=', 'Malformed GraphicsCommand control block, expecting an integer value')
|
||||||
e('s==', 'Malformed graphics control block, expecting an integer value for key: s')
|
e('s==', 'Malformed GraphicsCommand control block, expecting an integer value for key: s')
|
||||||
e('s=1=', 'Malformed graphics control block, expecting a comma or semi-colon after a value, found: 0x3d')
|
e('s=1=', 'Malformed GraphicsCommand control block, expecting a comma or semi-colon after a value, found: 0x3d')
|
||||||
|
|
||||||
def test_deccara(self):
|
def test_deccara(self):
|
||||||
s = self.create_screen()
|
s = self.create_screen()
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user