From ce8db7415468db55967a244bb869a01bea340447 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 23 Nov 2016 17:25:38 +0530 Subject: [PATCH] Port OSC parsing --- kitty/data-types.h | 6 ++-- kitty/parser.c | 69 +++++++++++++++++++++++++++++++++++++++++-- kitty/screen.c | 17 ++++++----- kitty_tests/parser.py | 23 ++++++++------- 4 files changed, 90 insertions(+), 25 deletions(-) diff --git a/kitty/data-types.h b/kitty/data-types.h index 6d8c2d4e4..758a7956a 100644 --- a/kitty/data-types.h +++ b/kitty/data-types.h @@ -358,9 +358,9 @@ void screen_delete_lines(Screen *self, unsigned int count/*=1*/); 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 set_title(Screen *self, const char *buf, unsigned int sz); -void set_icon(Screen *self, const char *buf, unsigned int sz); -void set_dynamic_color(Screen *self, unsigned int code, const char *buf, unsigned int sz); +void set_title(Screen *self, PyObject*); +void set_icon(Screen *self, PyObject*); +void set_dynamic_color(Screen *self, unsigned int code, PyObject*); void report_device_attributes(Screen *self, unsigned int UNUSED mode, bool UNUSED secondary); void select_graphic_rendition(Screen *self, unsigned int *params, unsigned int count); void report_device_status(Screen *self, unsigned int which, bool UNUSED); diff --git a/kitty/parser.c b/kitty/parser.c index 74ff67016..41afc1f16 100644 --- a/kitty/parser.c +++ b/kitty/parser.c @@ -7,7 +7,33 @@ #include "data-types.h" #include "control-codes.h" +static unsigned int pow10[10] = { + 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 +}; + +static inline unsigned int +utoi(uint32_t *buf, unsigned int sz) { + unsigned int ans = 0; + for (int i = sz-1, j=0; i >=0; i--, j++) { + ans += (buf[i] - '0') * pow10[j]; + } + return ans; +} + // Macros {{{ +#define IS_DIGIT \ + case '0': \ + case '1': \ + case '2': \ + case '3': \ + case '4': \ + case '5': \ + case '6': \ + case '7': \ + case '8': \ + case '9': + + #ifdef DUMP_COMMANDS static void _report_error(PyObject *dump_callback, const char *fmt, ...) { va_list argptr; @@ -39,6 +65,9 @@ static void _report_error(PyObject *dump_callback, const char *fmt, ...) { #define REPORT_DRAW(ch) \ Py_XDECREF(PyObject_CallFunction(dump_callback, "sC", "draw", ch)); PyErr_Clear(); +#define REPORT_OSC(name, string) \ + Py_XDECREF(PyObject_CallFunction(dump_callback, "sO", #name, string)); PyErr_Clear(); + #else #define DUMP_UNUSED UNUSED @@ -47,6 +76,7 @@ static void _report_error(PyObject *dump_callback, const char *fmt, ...) { #define REPORT_COMMAND(...) #define REPORT_DRAW(ch) +#define REPORT_OSC(name, string) #endif @@ -157,8 +187,41 @@ handle_esc_mode_char(Screen *screen, uint32_t ch, PyObject DUMP_UNUSED *dump_cal // OSC mode {{{ static inline void -dispatch_osc(Screen *screen) { - screen->parser_buf_pos++; +dispatch_osc(Screen *screen, PyObject DUMP_UNUSED *dump_callback) { +#define DISPATCH_OSC(name) REPORT_OSC(name, string); name(screen, string); + const unsigned int limit = screen->parser_buf_pos; + unsigned int code=0, i; + for (i = 0; i < MIN(limit, 5); i++) { + if (screen->parser_buf[i] < '0' || screen->parser_buf[i] > '9') break; + } + if (i > 0) { + code = utoi(screen->parser_buf, i); + if (i < limit - 1 && screen->parser_buf[i] == ';') i++; + } + PyObject *string = PyUnicode_FromKindAndData(PyUnicode_4BYTE_KIND, screen->parser_buf + i, limit - i); + switch(code) { + case 0: + DISPATCH_OSC(set_title); + DISPATCH_OSC(set_icon); + break; + case 1: + DISPATCH_OSC(set_icon); + break; + case 2: + DISPATCH_OSC(set_title); + break; + case 10: + case 11: + case 110: + case 111: + REPORT_OSC(set_dynamic_color, string); + set_dynamic_color(screen, code, string); + break; + default: + REPORT_ERROR("Unknown OSC code: %u", code); + } + Py_CLEAR(string); +#undef DISPATCH_OSC } // }}} @@ -239,7 +302,7 @@ _parse_bytes(Screen *screen, uint8_t *buf, Py_ssize_t len, PyObject DUMP_UNUSED /* case CSI_STATE: */ /* CALL_HANDLER(csi); */ case OSC: - if (accumulate_osc(screen, codepoint, dump_callback)) { dispatch_osc(screen); SET_STATE(0); } + if (accumulate_osc(screen, codepoint, dump_callback)) { dispatch_osc(screen, dump_callback); SET_STATE(0); } break; case DCS: if (accumulate_dcs(screen, codepoint, dump_callback)) { dispatch_dcs(screen); SET_STATE(0); } diff --git a/kitty/screen.c b/kitty/screen.c index cf8622413..040d19116 100644 --- a/kitty/screen.c +++ b/kitty/screen.c @@ -795,18 +795,19 @@ void screen_set_cursor(Screen *self, unsigned int mode, uint8_t secondary) { } } -void set_title(Screen *self, const char *buf, unsigned int sz) { - callback("title_changed", self, buf, sz); +void set_title(Screen *self, PyObject *title) { + PyObject_CallMethod(self->callbacks, "title_changed", "O", title); + if (PyErr_Occurred()) { PyErr_Print(); PyErr_Clear(); } } -void set_icon(Screen *self, const char *buf, unsigned int sz) { - callback("icon_changed", self, buf, sz); +void set_icon(Screen *self, PyObject *icon) { + PyObject_CallMethod(self->callbacks, "icon_changed", "O", icon); + if (PyErr_Occurred()) { PyErr_Print(); PyErr_Clear(); } } -void set_dynamic_color(Screen *self, unsigned int code, const char *buf, unsigned int sz) { - PyObject_CallMethod(self->callbacks, "set_dynamic_color", "Iy#", code, buf, sz); - if (PyErr_Occurred()) PyErr_Print(); - PyErr_Clear(); +void set_dynamic_color(Screen *self, unsigned int code, PyObject *color) { + PyObject_CallMethod(self->callbacks, "set_dynamic_color", "IO", code, color); + if (PyErr_Occurred()) { PyErr_Print(); PyErr_Clear(); } } // }}} diff --git a/kitty_tests/parser.py b/kitty_tests/parser.py index ccf244789..27312c59e 100644 --- a/kitty_tests/parser.py +++ b/kitty_tests/parser.py @@ -33,7 +33,8 @@ def set_dynamic_color(self, code, data): self.colorbuf += data def clear(self): - self.wtcbuf = self.iconbuf = self.titlebuf = self.colorbuf = b'' + self.wtcbuf = b'' + self.iconbuf = self.titlebuf = self.colorbuf = '' class TestParser(BaseTest): @@ -150,19 +151,19 @@ def test_osc_codes(self): pb = partial(self.parse_bytes_dump, s) c = Callbacks() s.callbacks = c - pb(b'a\033]2;xyz\x07bcde', 'a', ('set_title', 3), 'bcde') + pb(b'a\033]2;xyz\x07bcde', 'a', ('set_title', 'xyz'), 'bcde') self.ae(str(s.line(0)), 'abcde') - self.ae(c.titlebuf, b'xyz') + self.ae(c.titlebuf, 'xyz') c.clear() - pb('\033]\x07', ('set_title', 0), ('set_icon', 0)) - self.ae(c.titlebuf, b''), self.ae(c.iconbuf, b'') - pb('\033]ab\x07', ('set_title', 2), ('set_icon', 2)) - self.ae(c.titlebuf, b'ab'), self.ae(c.iconbuf, b'ab') + pb('\033]\x07', ('set_title', ''), ('set_icon', '')) + self.ae(c.titlebuf, ''), self.ae(c.iconbuf, '') + pb('\033]ab\x07', ('set_title', 'ab'), ('set_icon', 'ab')) + self.ae(c.titlebuf, 'ab'), self.ae(c.iconbuf, 'ab') c.clear() - pb('\033]2;;;;\x07', ('set_title', 3)) - self.ae(c.titlebuf, b';;;') - pb('\033]110\x07', ('set_dynamic_color', 110, 0)) - self.ae(c.colorbuf, b'') + pb('\033]2;;;;\x07', ('set_title', ';;;')) + self.ae(c.titlebuf, ';;;') + pb('\033]110\x07', ('set_dynamic_color', '')) + self.ae(c.colorbuf, '') # def test_dcs_codes(self): # s = self.create_screen()