From 62dbc1129c65b2b3b1107fefedc76195ff30ccf0 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 8 Dec 2021 16:04:23 +0530 Subject: [PATCH] When setting the OS Window title strip out CSI escape codes Fixes #4325 --- kitty/glfw.c | 41 +++++++++++++++++++++++++++++++++++++++- kitty_tests/datatypes.py | 10 +++++++++- 2 files changed, 49 insertions(+), 2 deletions(-) diff --git a/kitty/glfw.c b/kitty/glfw.c index df0187067..7d79b65f3 100644 --- a/kitty/glfw.c +++ b/kitty/glfw.c @@ -1275,9 +1275,36 @@ request_window_attention(id_type kitty_window_id, bool audio_bell) { } } +static void +strip_csi_(const char *title, char *buf, size_t bufsz) { + enum { NORMAL, IN_ESC, IN_CSI} state = NORMAL; + char *dest = buf, *last = &buf[bufsz-1]; + *dest = 0; *last = 0; + + for (; *title && dest < last; title++) { + const char ch = *title; + switch (state) { + case NORMAL: { + if (ch == 0x1b) { state = IN_ESC; } + else *(dest++) = ch; + } break; + case IN_ESC: { + if (ch == '[') { state = IN_CSI; } + else { state = NORMAL; } + } break; + case IN_CSI: { + if (!(('0' <= ch && ch <= '9') || ch == ';' || ch == ':')) state = NORMAL; + } break; + } + } + *dest = 0; +} + void set_os_window_title(OSWindow *w, const char *title) { - glfwSetWindowTitle(w->handle, title); + static char buf[2048]; + strip_csi_(title, buf, arraysz(buf)); + glfwSetWindowTitle(w->handle, buf); } void @@ -1514,6 +1541,17 @@ stop_main_loop(void) { glfwStopMainLoop(); } +static PyObject* +strip_csi(PyObject *self UNUSED, PyObject *src) { + if (!PyUnicode_Check(src)) { PyErr_SetString(PyExc_TypeError, "Unicode string expected"); return NULL; } + Py_ssize_t sz; + const char *title = PyUnicode_AsUTF8AndSize(src, &sz); + if (!title) return NULL; + FREE_AFTER_FUNCTION char *buf = malloc(sz + 1); + if (!buf) { return PyErr_NoMemory(); } + strip_csi_(title, buf, sz + 1); + return PyUnicode_FromString(buf); +} // Boilerplate {{{ @@ -1534,6 +1572,7 @@ static PyMethodDef module_methods[] = { METHODB(get_click_interval, METH_NOARGS), METHODB(x11_window_id, METH_O), METHODB(set_primary_selection, METH_VARARGS), + METHODB(strip_csi, METH_O), #ifndef __APPLE__ METHODB(dbus_send_notification, METH_VARARGS), #endif diff --git a/kitty_tests/datatypes.py b/kitty_tests/datatypes.py index f41c659cf..17e407b8c 100644 --- a/kitty_tests/datatypes.py +++ b/kitty_tests/datatypes.py @@ -7,7 +7,7 @@ import tempfile from kitty.config import build_ansi_color_table, defaults from kitty.fast_data_types import ( ColorProfile, Cursor as C, HistoryBuf, LineBuf, Color, - parse_input_from_terminal, truncate_point_for_length, wcswidth, wcwidth + parse_input_from_terminal, truncate_point_for_length, wcswidth, wcwidth, strip_csi ) from kitty.rgb import to_color from kitty.utils import is_path_in_temp_dir, sanitize_title @@ -520,3 +520,11 @@ class TestDataTypes(BaseTest): a = [] hb.as_ansi(a.append) self.ae(a, [str(hb.line(i)) + '\n' for i in range(hb.count - 1, -1, -1)]) + + def test_strip_csi(self): + def q(x, y=''): + self.ae(y or x, strip_csi(x)) + q('test') + q('a\x1bbc', 'ac') + q('a\x1b[bc', 'ac') + q('a\x1b[12;34:43mbc', 'abc')