From ba32e481cac8267d8bd10b3e6b1246b95e13fad5 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 22 Mar 2021 10:46:41 +0530 Subject: [PATCH 01/15] ... --- kitty/fonts/box_drawing.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/kitty/fonts/box_drawing.py b/kitty/fonts/box_drawing.py index a6d9b841d..2ba7f5719 100644 --- a/kitty/fonts/box_drawing.py +++ b/kitty/fonts/box_drawing.py @@ -464,7 +464,8 @@ def rectircle_equations( xexp = radius / cell_width pow = math.pow left_quadrants, lower_quadrants = {'╭': (True, False), '╮': (False, False), '╰': (True, True), '╯': (False, True)}[which] - adjust_left_quadrant = (cell_width // supersample_factor % 2) * supersample_factor + cell_width_is_odd = (cell_width // supersample_factor) % 2 + adjust_x = cell_width_is_odd * supersample_factor if lower_quadrants: def y(t: float) -> float: # 0 -> top of cell, 1 -> middle of cell @@ -478,7 +479,7 @@ def rectircle_equations( if left_quadrants: def x(t: float) -> float: xterm = 1 - pow(t, yexp) - return cell_width - abs(a * pow(xterm, xexp)) - adjust_left_quadrant + return cell_width - abs(a * pow(xterm, xexp)) - adjust_x else: def x(t: float) -> float: xterm = 1 - pow(t, yexp) From 83bbcf0aa156b42eacd8503094d9d50a5638e898 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 22 Mar 2021 22:16:40 +0530 Subject: [PATCH 02/15] Graphics protocol: Add a control to allow clients to specify that the cursor should not move when displaying an image Fixes #3411 --- docs/changelog.rst | 3 +++ docs/graphics-protocol.rst | 8 ++++++ gen-apc-parsers.py | 1 + kitty/graphics.c | 4 ++- kitty/graphics.h | 2 +- kitty/parse-graphics-command.h | 46 +++++++++++++++++++--------------- kitty_tests/graphics.py | 14 ++++++++--- kitty_tests/parser.py | 2 +- 8 files changed, 54 insertions(+), 26 deletions(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index 13b1a3f6d..c22197e47 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -102,6 +102,9 @@ To update |kitty|, :doc:`follow the instructions `. - Improve rendering of rounded corners by using a rectircle equation rather than a cubic bezier (:iss:`3409`) +- Graphics protocol: Add a control to allow clients to specify that the cursor + should not move when displaying an image (:iss:`3411`) + 0.19.3 [2020-12-19] ------------------- diff --git a/docs/graphics-protocol.rst b/docs/graphics-protocol.rst index f8bb826e8..5e3f160a8 100644 --- a/docs/graphics-protocol.rst +++ b/docs/graphics-protocol.rst @@ -401,6 +401,12 @@ colors. number of rows in the image placement rectangle. If either of these cause the cursor to leave either the screen or the scroll area, the exact positioning of the cursor is undefined, and up to implementations. + The client can ask the terminal emulator to not move the cursor at all + by specifying ``C=1`` in the command, which sets the cursor movement policy + to no movement for placing the current image. + +.. versionadded:: 0.20.0 + Support for the C=1 cursor movement policy Deleting images @@ -680,6 +686,8 @@ Key Value Default Description ``Y`` Positive integer ``0`` The y-offset within the first cell at which to start displaying the image ``c`` Positive integer ``0`` The number of columns to display the image over ``r`` Positive integer ``0`` The number of rows to display the image over +``C`` Positive integer ``0`` Cursor movement policy. ``0`` is the default, to move the cursor after the image. + ``1`` is to not move the cursor at all when placing images. ``z`` 32-bit integer ``0`` The *z-index* vertical stacking order of the image **Keys for animation frame loading** diff --git a/gen-apc-parsers.py b/gen-apc-parsers.py index 6e5d6bccb..83b0b6cd5 100755 --- a/gen-apc-parsers.py +++ b/gen-apc-parsers.py @@ -273,6 +273,7 @@ def graphics_parser() -> None: 'X': ('cell_x_offset', 'uint'), 'Y': ('cell_y_offset', 'uint'), 'z': ('z_index', 'int'), + 'C': ('cursor_movement', 'uint'), } text = generate('parse_graphics_code', 'screen_handle_graphics_command', 'graphics_command', keymap, 'GraphicsCommand') write_header(text, 'kitty/parse-graphics-command.h') diff --git a/kitty/graphics.c b/kitty/graphics.c index b3a9d687a..46694e0a5 100644 --- a/kitty/graphics.c +++ b/kitty/graphics.c @@ -699,7 +699,9 @@ handle_put_command(GraphicsManager *self, const GraphicsCommand *g, Cursor *c, b update_src_rect(ref, img); update_dest_rect(ref, g->num_cells, g->num_lines, cell); // Move the cursor, the screen will take care of ensuring it is in bounds - c->x += ref->effective_num_cols; c->y += ref->effective_num_rows - 1; + if (g->cursor_movement != 1) { + c->x += ref->effective_num_cols; c->y += ref->effective_num_rows - 1; + } return img->client_id; } diff --git a/kitty/graphics.h b/kitty/graphics.h index 852b439a1..7343a80d6 100644 --- a/kitty/graphics.h +++ b/kitty/graphics.h @@ -10,7 +10,7 @@ typedef struct { unsigned char action, transmission_type, compressed, delete_action; - uint32_t format, more, id, image_number, data_sz, data_offset, placement_id, quiet; + uint32_t format, more, id, image_number, data_sz, data_offset, placement_id, quiet, cursor_movement; uint32_t width, height, x_offset, y_offset, data_height, data_width, num_cells, num_lines, cell_x_offset, cell_y_offset; int32_t z_index; size_t payload_sz; diff --git a/kitty/parse-graphics-command.h b/kitty/parse-graphics-command.h index 484bb9231..ca64b5f6e 100644 --- a/kitty/parse-graphics-command.h +++ b/kitty/parse-graphics-command.h @@ -39,7 +39,8 @@ static inline void parse_graphics_code(Screen *screen, num_lines = 'r', cell_x_offset = 'X', cell_y_offset = 'Y', - z_index = 'z' + z_index = 'z', + cursor_movement = 'C' }; enum KEYS key = 'a'; @@ -121,6 +122,9 @@ static inline void parse_graphics_code(Screen *screen, case z_index: value_state = INT; break; + case cursor_movement: + value_state = UINT; + break; default: REPORT_ERROR("Malformed GraphicsCommand control block, invalid key " "character: 0x%x", @@ -144,9 +148,9 @@ static inline void parse_graphics_code(Screen *screen, case action: { g.action = screen->parser_buf[pos++] & 0xff; - if (g.action != 'q' && g.action != 'd' && g.action != 'p' && - g.action != 't' && g.action != 'T' && g.action != 'f' && - g.action != 'a') { + if (g.action != 't' && g.action != 'a' && g.action != 'T' && + g.action != 'f' && g.action != 'd' && g.action != 'p' && + g.action != 'q') { REPORT_ERROR("Malformed GraphicsCommand control block, unknown flag " "value for action: 0x%x", g.action); @@ -156,16 +160,16 @@ static inline void parse_graphics_code(Screen *screen, case delete_action: { g.delete_action = screen->parser_buf[pos++] & 0xff; - if (g.delete_action != 'F' && g.delete_action != 'n' && - g.delete_action != 'x' && g.delete_action != 'I' && - g.delete_action != 'p' && g.delete_action != 'i' && - g.delete_action != 'C' && g.delete_action != 'Q' && - g.delete_action != 'A' && g.delete_action != 'z' && - g.delete_action != 'y' && g.delete_action != 'c' && - g.delete_action != 'Z' && g.delete_action != 'N' && - g.delete_action != 'X' && g.delete_action != 'q' && - g.delete_action != 'P' && g.delete_action != 'f' && - g.delete_action != 'Y' && g.delete_action != 'a') { + if (g.delete_action != 'Q' && g.delete_action != 'N' && + g.delete_action != 'z' && g.delete_action != 'p' && + g.delete_action != 'X' && g.delete_action != 'a' && + g.delete_action != 'y' && g.delete_action != 'f' && + g.delete_action != 'Z' && g.delete_action != 'F' && + g.delete_action != 'Y' && g.delete_action != 'n' && + g.delete_action != 'C' && g.delete_action != 'q' && + g.delete_action != 'c' && g.delete_action != 'i' && + g.delete_action != 'A' && g.delete_action != 'P' && + g.delete_action != 'I' && g.delete_action != 'x') { REPORT_ERROR("Malformed GraphicsCommand control block, unknown flag " "value for delete_action: 0x%x", g.delete_action); @@ -175,8 +179,8 @@ static inline void parse_graphics_code(Screen *screen, case transmission_type: { g.transmission_type = screen->parser_buf[pos++] & 0xff; - if (g.transmission_type != 's' && g.transmission_type != 'f' && - g.transmission_type != 'd' && g.transmission_type != 't') { + if (g.transmission_type != 's' && g.transmission_type != 't' && + g.transmission_type != 'd' && g.transmission_type != 'f') { REPORT_ERROR("Malformed GraphicsCommand control block, unknown flag " "value for transmission_type: 0x%x", g.transmission_type); @@ -264,6 +268,7 @@ static inline void parse_graphics_code(Screen *screen, U(num_lines); U(cell_x_offset); U(cell_y_offset); + U(cursor_movement); default: break; } @@ -322,8 +327,8 @@ static inline void parse_graphics_code(Screen *screen, } 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 sI si " - "sI} y#", + "s {sc sc sc sc sI sI sI sI 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", @@ -336,8 +341,9 @@ static inline void parse_graphics_code(Screen *screen, (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); + "cell_y_offset", (unsigned int)g.cell_y_offset, "cursor_movement", + (unsigned int)g.cursor_movement, "z_index", (int)g.z_index, "payload_sz", + g.payload_sz, payload, g.payload_sz); screen_handle_graphics_command(screen, &g, payload); } diff --git a/kitty_tests/graphics.py b/kitty_tests/graphics.py index 5b258a18c..e39c655a0 100644 --- a/kitty_tests/graphics.py +++ b/kitty_tests/graphics.py @@ -124,9 +124,15 @@ def put_helpers(self, cw, ch): s = self.create_screen(10, 5, cell_width=cw, cell_height=ch) return s, 2 / s.columns, 2 / s.lines - def put_cmd(z=0, num_cols=0, num_lines=0, x_off=0, y_off=0, width=0, height=0, cell_x_off=0, cell_y_off=0, placement_id=0): - return 'z=%d,c=%d,r=%d,x=%d,y=%d,w=%d,h=%d,X=%d,Y=%d,p=%d' % ( - z, num_cols, num_lines, x_off, y_off, width, height, cell_x_off, cell_y_off, placement_id) + def put_cmd( + z=0, num_cols=0, num_lines=0, x_off=0, y_off=0, width=0, + height=0, cell_x_off=0, cell_y_off=0, placement_id=0, + cursor_movement=0 + ): + return 'z=%d,c=%d,r=%d,x=%d,y=%d,w=%d,h=%d,X=%d,Y=%d,p=%d,C=%d' % ( + z, num_cols, num_lines, x_off, y_off, width, height, cell_x_off, + cell_y_off, placement_id, cursor_movement + ) def put_image(screen, w, h, **kw): nonlocal iid @@ -510,6 +516,8 @@ class TestGraphics(BaseTest): rect_eq(l2[1]['dest_rect'], -1, 1, -1 + dx, 1 - dy) self.ae(l2[0]['group_count'], 1), self.ae(l2[1]['group_count'], 1) self.ae(s.cursor.x, 0), self.ae(s.cursor.y, 1) + self.ae(put_image(s, 10, 20, cursor_movement=1)[1], 'OK') + self.ae(s.cursor.x, 0), self.ae(s.cursor.y, 1) s.reset() self.assertEqual(s.grman.disk_cache.total_size, 0) diff --git a/kitty_tests/parser.py b/kitty_tests/parser.py index 96bc5d2b5..70474844d 100644 --- a/kitty_tests/parser.py +++ b/kitty_tests/parser.py @@ -381,7 +381,7 @@ class TestParser(BaseTest): k[p] = v.encode('ascii') for f in 'action delete_action transmission_type compressed'.split(): k.setdefault(f, b'\0') - for f in ('format more id data_sz data_offset width height x_offset y_offset data_height data_width' + for f in ('format more id data_sz data_offset width height x_offset y_offset data_height data_width cursor_movement' ' num_cells num_lines cell_x_offset cell_y_offset z_index placement_id image_number quiet').split(): k.setdefault(f, 0) p = k.pop('payload', '').encode('utf-8') From 33ecfc6f84618adc56783553fb73d57ccdb301ca Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 22 Mar 2021 22:33:30 +0530 Subject: [PATCH 03/15] ... --- docs/graphics-protocol.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/graphics-protocol.rst b/docs/graphics-protocol.rst index 5e3f160a8..0bef2396a 100644 --- a/docs/graphics-protocol.rst +++ b/docs/graphics-protocol.rst @@ -686,8 +686,8 @@ Key Value Default Description ``Y`` Positive integer ``0`` The y-offset within the first cell at which to start displaying the image ``c`` Positive integer ``0`` The number of columns to display the image over ``r`` Positive integer ``0`` The number of rows to display the image over -``C`` Positive integer ``0`` Cursor movement policy. ``0`` is the default, to move the cursor after the image. - ``1`` is to not move the cursor at all when placing images. +``C`` Positive integer ``0`` Cursor movement policy. ``0`` is the default, to move the cursor to after the image. + ``1`` is to not move the cursor at all when placing the image. ``z`` 32-bit integer ``0`` The *z-index* vertical stacking order of the image **Keys for animation frame loading** From ca7587c08492347136593774991a11a0cf08520b Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 23 Mar 2021 09:53:22 +0530 Subject: [PATCH 04/15] Allow skipping zero cells when converting a line to unicode --- kitty/line-buf.c | 2 +- kitty/line.c | 13 +++++++------ kitty/lineops.h | 4 ++-- kitty/screen.c | 2 +- 4 files changed, 11 insertions(+), 10 deletions(-) diff --git a/kitty/line-buf.c b/kitty/line-buf.c index e31d2d111..beaf4ceda 100644 --- a/kitty/line-buf.c +++ b/kitty/line-buf.c @@ -470,7 +470,7 @@ __str__(LineBuf *self) { if (lines == NULL) return PyErr_NoMemory(); for (index_type i = 0; i < self->ynum; i++) { init_line(self, self->line, self->line_map[i]); - PyObject *t = line_as_unicode(self->line); + PyObject *t = line_as_unicode(self->line, false); if (t == NULL) { Py_CLEAR(lines); return NULL; } PyTuple_SET_ITEM(lines, i, t); } diff --git a/kitty/line.c b/kitty/line.c index 86858c98b..82cd3eabe 100644 --- a/kitty/line.c +++ b/kitty/line.c @@ -229,7 +229,7 @@ cell_as_utf8_for_fallback(CPUCell *cell, char *buf) { PyObject* -unicode_in_range(Line *self, index_type start, index_type limit, bool include_cc, char leading_char) { +unicode_in_range(const Line *self, const index_type start, const index_type limit, const bool include_cc, const char leading_char, const bool skip_zero_cells) { size_t n = 0; static Py_UCS4 buf[4096]; if (leading_char) buf[n++] = leading_char; @@ -238,6 +238,7 @@ unicode_in_range(Line *self, index_type start, index_type limit, bool include_cc char_type ch = self->cpu_cells[i].ch; if (ch == 0) { if (previous_width == 2) { previous_width = 0; continue; }; + if (skip_zero_cells) continue; } if (ch == '\t') { buf[n++] = '\t'; @@ -255,8 +256,8 @@ unicode_in_range(Line *self, index_type start, index_type limit, bool include_cc } PyObject * -line_as_unicode(Line* self) { - return unicode_in_range(self, 0, xlimit_for_line(self), true, 0); +line_as_unicode(Line* self, bool skip_zero_cells) { + return unicode_in_range(self, 0, xlimit_for_line(self), true, 0, skip_zero_cells); } static PyObject* @@ -380,7 +381,7 @@ is_continued(Line* self, PyObject *a UNUSED) { static PyObject* __repr__(Line* self) { - PyObject *s = line_as_unicode(self); + PyObject *s = line_as_unicode(self, false); if (s == NULL) return NULL; PyObject *ans = PyObject_Repr(s); Py_CLEAR(s); @@ -784,7 +785,7 @@ mark_text_in_line(PyObject *marker, Line *line) { for (index_type i = 0; i < line->xnum; i++) line->gpu_cells[i].attrs &= ATTRS_MASK_WITHOUT_MARK; return; } - PyObject *text = line_as_unicode(line); + PyObject *text = line_as_unicode(line, false); if (PyUnicode_GET_LENGTH(text) > 0) { apply_marker(marker, line, text); } else { @@ -827,7 +828,7 @@ as_text_generic(PyObject *args, void *container, get_line_func get_line, index_t Py_CLEAR(ret); } } else { - t = line_as_unicode(line); + t = line_as_unicode(line, false); } if (t == NULL) goto end; ret = PyObject_CallFunctionObjArgs(callback, t, NULL); diff --git a/kitty/lineops.h b/kitty/lineops.h index 2481f1cb4..77bde43eb 100644 --- a/kitty/lineops.h +++ b/kitty/lineops.h @@ -83,8 +83,8 @@ size_t cell_as_unicode(CPUCell *cell, bool include_cc, Py_UCS4 *buf, char_type); size_t cell_as_unicode_for_fallback(CPUCell *cell, Py_UCS4 *buf); size_t cell_as_utf8(CPUCell *cell, bool include_cc, char *buf, char_type); size_t cell_as_utf8_for_fallback(CPUCell *cell, char *buf); -PyObject* unicode_in_range(Line *self, index_type start, index_type limit, bool include_cc, char leading_char); -PyObject* line_as_unicode(Line *); +PyObject* unicode_in_range(const Line *self, const index_type start, const index_type limit, const bool include_cc, const char leading_char, const bool skip_zero_cells); +PyObject* line_as_unicode(Line *, bool); void linebuf_init_line(LineBuf *, index_type); void linebuf_clear(LineBuf *, char_type ch); diff --git a/kitty/screen.c b/kitty/screen.c index 094cdbcfe..5ffedf9ac 100644 --- a/kitty/screen.c +++ b/kitty/screen.c @@ -1978,7 +1978,7 @@ text_for_range(Screen *self, const Selection *sel, bool insert_newlines) { Line *line = range_line_(self, y); XRange xr = xrange_for_iteration(&idata, y, line); char leading_char = (i > 0 && insert_newlines && !line->continued) ? '\n' : 0; - PyObject *text = unicode_in_range(line, xr.x, xr.x_limit, true, leading_char); + PyObject *text = unicode_in_range(line, xr.x, xr.x_limit, true, leading_char, false); if (text == NULL) { Py_DECREF(ans); return PyErr_NoMemory(); } PyTuple_SET_ITEM(ans, i, text); } From c0ec60c11314fe34808641578b1ca73cf6c15066 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 23 Mar 2021 09:56:58 +0530 Subject: [PATCH 05/15] Forgot to change a couple of usages of line_as_unicode --- kitty/history.c | 2 +- kitty/line.c | 8 +++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/kitty/history.c b/kitty/history.c index 3a4ae5baf..bba7bb863 100644 --- a/kitty/history.c +++ b/kitty/history.c @@ -278,7 +278,7 @@ __str__(HistoryBuf *self) { if (lines == NULL) return PyErr_NoMemory(); for (index_type i = 0; i < self->count; i++) { init_line(self, index_of(self, i), self->line); - PyObject *t = line_as_unicode(self->line); + PyObject *t = line_as_unicode(self->line, false); if (t == NULL) { Py_CLEAR(lines); return NULL; } PyTuple_SET_ITEM(lines, i, t); } diff --git a/kitty/line.c b/kitty/line.c index 82cd3eabe..9dc8b3305 100644 --- a/kitty/line.c +++ b/kitty/line.c @@ -388,6 +388,12 @@ __repr__(Line* self) { return ans; } +static PyObject* +__str__(Line* self) { + return line_as_unicode(self, false); +} + + static PyObject* width(Line *self, PyObject *val) { #define width_doc "width(x) -> the width of the character at x" @@ -907,7 +913,7 @@ PyTypeObject Line_Type = { .tp_basicsize = sizeof(Line), .tp_dealloc = (destructor)dealloc, .tp_repr = (reprfunc)__repr__, - .tp_str = (reprfunc)line_as_unicode, + .tp_str = (reprfunc)__str__, .tp_as_sequence = &sequence_methods, .tp_flags = Py_TPFLAGS_DEFAULT, .tp_richcompare = richcmp, From 11268ffa166d7ca40df18bbea699aae667bb0727 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 23 Mar 2021 10:04:58 +0530 Subject: [PATCH 06/15] Fix marking of text not working on lines that contain zero cells Zero cells are passed to the regex engine as spaces, so they must increment the match_pos counter. Fixes #3403 --- docs/changelog.rst | 3 +++ kitty/line.c | 2 +- kitty_tests/screen.py | 7 +++++++ 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index c22197e47..a75930980 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -105,6 +105,9 @@ To update |kitty|, :doc:`follow the instructions `. - Graphics protocol: Add a control to allow clients to specify that the cursor should not move when displaying an image (:iss:`3411`) +- Fix marking of text not working on lines that contain zero cells + (:iss:`3403`) + 0.19.3 [2020-12-19] ------------------- diff --git a/kitty/line.c b/kitty/line.c index 9dc8b3305..5b84a8e14 100644 --- a/kitty/line.c +++ b/kitty/line.c @@ -736,8 +736,8 @@ apply_mark(Line *line, const attrs_type mark, index_type *cell_pos, unsigned int #define MARK { line->gpu_cells[x].attrs &= ATTRS_MASK_WITHOUT_MARK; line->gpu_cells[x].attrs |= mark; } index_type x = *cell_pos; MARK; + (*match_pos)++; if (line->cpu_cells[x].ch) { - (*match_pos)++; if (line->cpu_cells[x].ch == '\t') { unsigned num_cells_to_skip_for_tab = line->cpu_cells[x].cc_idx[0]; while (num_cells_to_skip_for_tab && x + 1 < line->xnum && line->cpu_cells[x+1].ch == ' ') { diff --git a/kitty_tests/screen.py b/kitty_tests/screen.py index 9784dad83..aba670cc9 100644 --- a/kitty_tests/screen.py +++ b/kitty_tests/screen.py @@ -647,6 +647,13 @@ class TestScreen(BaseTest): self.ae(s.marked_cells(), cells(8)) s.set_marker(marker_from_regex('\t', 3)) self.ae(s.marked_cells(), cells(*range(8))) + s = self.create_screen() + s.cursor.x = 2 + s.draw('x') + s.cursor.x += 1 + s.draw('x') + s.set_marker(marker_from_function(mark_x)) + self.ae(s.marked_cells(), [(2, 0, 1), (4, 0, 2)]) def test_hyperlinks(self): s = self.create_screen() From 84dcf8fd272f438c104449c474dcb4b0a042f410 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 23 Mar 2021 10:42:07 +0530 Subject: [PATCH 07/15] Use an enum for ime_state --- glfw/cocoa_window.m | 12 ++++++------ glfw/glfw3.h | 11 ++++++++--- glfw/ibus_glfw.c | 6 +++--- glfw/xkb_glfw.c | 4 ++-- kitty/glfw-wrapper.h | 11 ++++++++--- kitty/keys.c | 6 +++--- 6 files changed, 30 insertions(+), 20 deletions(-) diff --git a/glfw/cocoa_window.m b/glfw/cocoa_window.m index b319e5060..45734e747 100644 --- a/glfw/cocoa_window.m +++ b/glfw/cocoa_window.m @@ -1073,7 +1073,7 @@ is_ascii_control_char(char x) { if (input_source_changed) { debug_key(@"Input source changed, clearing pre-edit text and resetting deadkey state\n"); glfw_keyevent.text = NULL; - glfw_keyevent.ime_state = 1; + glfw_keyevent.ime_state = GLFW_IME_PREEDIT_CHANGED; window->ns.deadKeyState = 0; _glfwInputKeyboard(window, &glfw_keyevent); // clear pre-edit text } @@ -1110,14 +1110,14 @@ is_ascii_control_char(char x) { // 0x75 is the delete key which needs to be ignored during a compose sequence debug_key(@"Sending pre-edit text for dead key (text: %@ markedText: %@).\n", @(format_text(_glfw.ns.text)), markedText); glfw_keyevent.text = [[markedText string] UTF8String]; - glfw_keyevent.ime_state = 1; + glfw_keyevent.ime_state = GLFW_IME_PREEDIT_CHANGED; _glfwInputKeyboard(window, &glfw_keyevent); // update pre-edit text return; } if (in_compose_sequence) { debug_key(@"Clearing pre-edit text at end of compose sequence\n"); glfw_keyevent.text = NULL; - glfw_keyevent.ime_state = 1; + glfw_keyevent.ime_state = GLFW_IME_PREEDIT_CHANGED; _glfwInputKeyboard(window, &glfw_keyevent); // clear pre-edit text } } @@ -1127,11 +1127,11 @@ is_ascii_control_char(char x) { if (!window->ns.deadKeyState) { if ([self hasMarkedText]) { glfw_keyevent.text = [[markedText string] UTF8String]; - glfw_keyevent.ime_state = 1; + glfw_keyevent.ime_state = GLFW_IME_PREEDIT_CHANGED; _glfwInputKeyboard(window, &glfw_keyevent); // update pre-edit text } else if (previous_has_marked_text) { glfw_keyevent.text = NULL; - glfw_keyevent.ime_state = 1; + glfw_keyevent.ime_state = GLFW_IME_PREEDIT_CHANGED; _glfwInputKeyboard(window, &glfw_keyevent); // clear pre-edit text } if (([self hasMarkedText] || previous_has_marked_text) && !_glfw.ns.text[0]) { @@ -1140,7 +1140,7 @@ is_ascii_control_char(char x) { } } glfw_keyevent.text = _glfw.ns.text; - glfw_keyevent.ime_state = 0; + glfw_keyevent.ime_state = GLFW_IME_NONE; add_alternate_keys(&glfw_keyevent, event); _glfwInputKeyboard(window, &glfw_keyevent); } diff --git a/glfw/glfw3.h b/glfw/glfw3.h index e7ee09fda..cbfa60830 100644 --- a/glfw/glfw3.h +++ b/glfw/glfw3.h @@ -1184,6 +1184,11 @@ typedef enum { GLFW_PRESS = 1, GLFW_REPEAT = 2 } GLFWKeyAction; +typedef enum { + GLFW_IME_NONE, + GLFW_IME_PREEDIT_CHANGED, + GLFW_IME_COMMIT_TEXT +} GLFWIMEState; typedef struct GLFWkeyevent { @@ -1203,9 +1208,9 @@ typedef struct GLFWkeyevent const char *text; // Used for Input Method events. Zero for normal key events. - // A value of 1 means the pre-edit text for the input event has been changed. - // A value of 2 means the text should be committed. - int ime_state; + // A value of GLFW_IME_PREEDIT_CHANGED means the pre-edit text for the input event has been changed. + // A value of GLFW_IME_COMMIT_TEXT means the text should be committed. + GLFWIMEState ime_state; } GLFWkeyevent; /*! @brief The function pointer type for error callbacks. diff --git a/glfw/ibus_glfw.c b/glfw/ibus_glfw.c index 83b0f4ac8..0e36fcd5d 100644 --- a/glfw/ibus_glfw.c +++ b/glfw/ibus_glfw.c @@ -107,7 +107,7 @@ get_ibus_text_from_message(DBusMessage *msg) { } static inline void -send_text(const char *text, int ime_state) { +send_text(const char *text, GLFWIMEState ime_state) { _GLFWwindow *w = _glfwFocusedWindow(); if (w && w->callbacks.keyboard) { GLFWkeyevent fake_ev = {.action = GLFW_PRESS}; @@ -130,11 +130,11 @@ message_handler(DBusConnection *conn UNUSED, DBusMessage *msg, void *user_data) case 0: text = get_ibus_text_from_message(msg); debug("IBUS: CommitText: '%s'\n", text ? text : "(nil)"); - send_text(text, 2); + send_text(text, GLFW_IME_COMMIT_TEXT); break; case 1: text = get_ibus_text_from_message(msg); - send_text(text, 1); + send_text(text, GLFW_IME_PREEDIT_CHANGED); debug("IBUS: UpdatePreeditText: '%s'\n", text ? text : "(nil)"); break; case 2: diff --git a/glfw/xkb_glfw.c b/glfw/xkb_glfw.c index 21a2018a5..314f10eb0 100644 --- a/glfw/xkb_glfw.c +++ b/glfw/xkb_glfw.c @@ -575,7 +575,7 @@ glfw_xkb_key_from_ime(_GLFWIBUSKeyEvent *ev, bool handled_by_ime, bool failed) { if (failed && window && window->callbacks.keyboard) { // notify application to remove any existing pre-edit text GLFWkeyevent fake_ev = {.action = GLFW_PRESS}; - fake_ev.ime_state = 1; + fake_ev.ime_state = GLFW_IME_PREEDIT_CHANGED; window->callbacks.keyboard((GLFWwindow*) window, &fake_ev); } static xkb_keycode_t last_handled_press_keycode = 0; @@ -594,7 +594,7 @@ glfw_xkb_key_from_ime(_GLFWIBUSKeyEvent *ev, bool handled_by_ime, bool failed) { format_mods(ev->glfw_ev.mods), ev->glfw_ev.text ); - ev->glfw_ev.ime_state = 0; + ev->glfw_ev.ime_state = GLFW_IME_NONE; _glfwInputKeyboard(window, &ev->glfw_ev); } else debug("↳ discarded\n"); if (!is_release && handled_by_ime) diff --git a/kitty/glfw-wrapper.h b/kitty/glfw-wrapper.h index 797085bc2..bcb40a0bc 100644 --- a/kitty/glfw-wrapper.h +++ b/kitty/glfw-wrapper.h @@ -922,6 +922,11 @@ typedef enum { GLFW_PRESS = 1, GLFW_REPEAT = 2 } GLFWKeyAction; +typedef enum { + GLFW_IME_NONE, + GLFW_IME_PREEDIT_CHANGED, + GLFW_IME_COMMIT_TEXT +} GLFWIMEState; typedef struct GLFWkeyevent { @@ -941,9 +946,9 @@ typedef struct GLFWkeyevent const char *text; // Used for Input Method events. Zero for normal key events. - // A value of 1 means the pre-edit text for the input event has been changed. - // A value of 2 means the text should be committed. - int ime_state; + // A value of GLFW_IME_PREEDIT_CHANGED means the pre-edit text for the input event has been changed. + // A value of GLFW_IME_COMMIT_TEXT means the text should be committed. + GLFWIMEState ime_state; } GLFWkeyevent; /*! @brief The function pointer type for error callbacks. diff --git a/kitty/keys.c b/kitty/keys.c index 11be2dcb6..f958b51f4 100644 --- a/kitty/keys.c +++ b/kitty/keys.c @@ -105,19 +105,19 @@ on_key_input(GLFWkeyevent *ev) { id_type active_window_id = w->id; switch(ev->ime_state) { - case 1: // update pre-edit text + case GLFW_IME_PREEDIT_CHANGED: update_ime_position(global_state.callback_os_window, w, screen); screen_draw_overlay_text(screen, text); debug("updated pre-edit text: '%s'\n", text); return; - case 2: // commit text + case GLFW_IME_COMMIT_TEXT: if (*text) { schedule_write_to_child(w->id, 1, text, strlen(text)); debug("committed pre-edit text: %s\n", text); } else debug("committed pre-edit text: (null)\n"); screen_draw_overlay_text(screen, NULL); return; - case 0: + case GLFW_IME_NONE: // for macOS, update ime position on every key input // because the position is required before next input #if defined(__APPLE__) From a981b46ec99d8fd4c4b16dd697a583cb5c508813 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 23 Mar 2021 10:52:11 +0530 Subject: [PATCH 08/15] Use an enum for updateimestate as well --- glfw/cocoa_window.m | 4 ++-- glfw/glfw3.h | 15 +++++++++++---- glfw/input.c | 2 +- glfw/internal.h | 2 +- glfw/wl_window.c | 2 +- glfw/x11_window.c | 2 +- glfw/xkb_glfw.c | 6 +++--- glfw/xkb_glfw.h | 2 +- kitty/glfw-wrapper.h | 9 ++++++++- 9 files changed, 29 insertions(+), 15 deletions(-) diff --git a/glfw/cocoa_window.m b/glfw/cocoa_window.m index 45734e747..afed53938 100644 --- a/glfw/cocoa_window.m +++ b/glfw/cocoa_window.m @@ -1295,11 +1295,11 @@ is_ascii_control_char(char x) { [[markedText mutableString] setString:@""]; } -void _glfwPlatformUpdateIMEState(_GLFWwindow *w, int which, int a, int b, int c, int d) { +void _glfwPlatformUpdateIMEState(_GLFWwindow *w, GLFWIMEUpdateState which, int a, int b, int c, int d) { [w->ns.view updateIMEStateFor: which left:(CGFloat)a top:(CGFloat)b cellWidth:(CGFloat)c cellHeight:(CGFloat)d]; } -- (void)updateIMEStateFor:(int)which +- (void)updateIMEStateFor:(GLFWIMEUpdateState)which left:(CGFloat)left top:(CGFloat)top cellWidth:(CGFloat)cellWidth diff --git a/glfw/glfw3.h b/glfw/glfw3.h index cbfa60830..18518e85a 100644 --- a/glfw/glfw3.h +++ b/glfw/glfw3.h @@ -1179,17 +1179,24 @@ typedef struct GLFWwindow GLFWwindow; * @ingroup input */ typedef struct GLFWcursor GLFWcursor; + typedef enum { GLFW_RELEASE = 0, GLFW_PRESS = 1, GLFW_REPEAT = 2 } GLFWKeyAction; + typedef enum { GLFW_IME_NONE, GLFW_IME_PREEDIT_CHANGED, GLFW_IME_COMMIT_TEXT } GLFWIMEState; +typedef enum { + GLFW_IME_UPDATE_FOCUS = 1, + GLFW_IME_UPDATE_CURSOR_POSITION = 2 +} GLFWIMEUpdateState; + typedef struct GLFWkeyevent { // The [keyboard key](@ref keys) that was pressed or released. @@ -4538,16 +4545,16 @@ GLFWAPI GLFWkeyboardfun glfwSetKeyboardCallback(GLFWwindow* window, GLFWkeyboard * Used to notify the IME system of changes in state such as focus gained/lost * and text cursor position. * - * @param which: What data to notify. 1 means focus and 2 means cursor position. - * @param a, b, c, d: Interpreted based on the value of which. When which is 1 - * a is interpreted as a boolean indicating focus gained/lost. When which is 2 + * @param which: What data to notify. + * @param a, b, c, d: Interpreted based on the value of which. When which is GLFW_IME_UPDATE_FOCUS + * a is interpreted as a boolean indicating focus gained/lost. When which is GLFW_IME_UPDATE_CURSOR_POSITION * a, b, c, d are the cursor x, y, width and height values (in the window co-ordinate * system). * * @ingroup input * @since Added in version 4.0 */ -GLFWAPI void glfwUpdateIMEState(GLFWwindow* window, int which, int a, int b, int c, int d); +GLFWAPI void glfwUpdateIMEState(GLFWwindow* window, GLFWIMEUpdateState which, int a, int b, int c, int d); /*! @brief Sets the mouse button callback. diff --git a/glfw/input.c b/glfw/input.c index c6a7569e6..7ba7922df 100644 --- a/glfw/input.c +++ b/glfw/input.c @@ -1010,7 +1010,7 @@ GLFWAPI GLFWkeyboardfun glfwSetKeyboardCallback(GLFWwindow* handle, GLFWkeyboard return cbfun; } -GLFWAPI void glfwUpdateIMEState(GLFWwindow* handle, int which, int a, int b, int c, int d) { +GLFWAPI void glfwUpdateIMEState(GLFWwindow* handle, GLFWIMEUpdateState which, int a, int b, int c, int d) { _GLFWwindow* window = (_GLFWwindow*) handle; assert(window != NULL); diff --git a/glfw/internal.h b/glfw/internal.h index 58edb4ce9..88076bed1 100644 --- a/glfw/internal.h +++ b/glfw/internal.h @@ -728,7 +728,7 @@ void _glfwPlatformSetWindowDecorated(_GLFWwindow* window, bool enabled); void _glfwPlatformSetWindowFloating(_GLFWwindow* window, bool enabled); void _glfwPlatformSetWindowMousePassthrough(_GLFWwindow* window, bool enabled); void _glfwPlatformSetWindowOpacity(_GLFWwindow* window, float opacity); -void _glfwPlatformUpdateIMEState(_GLFWwindow *w, int which, int a, int b, int c, int d); +void _glfwPlatformUpdateIMEState(_GLFWwindow *w, GLFWIMEUpdateState which, int a, int b, int c, int d); void _glfwPlatformPollEvents(void); void _glfwPlatformWaitEvents(void); diff --git a/glfw/wl_window.c b/glfw/wl_window.c index c3b897602..b3eb96a59 100644 --- a/glfw/wl_window.c +++ b/glfw/wl_window.c @@ -2105,7 +2105,7 @@ VkResult _glfwPlatformCreateWindowSurface(VkInstance instance, } void -_glfwPlatformUpdateIMEState(_GLFWwindow *w, int which, int a, int b, int c, int d) { +_glfwPlatformUpdateIMEState(_GLFWwindow *w, GLFWIMEUpdateState which, int a, int b, int c, int d) { glfw_xkb_update_ime_state(w, &_glfw.wl.xkb, which, a, b, c, d); } diff --git a/glfw/x11_window.c b/glfw/x11_window.c index 1389e171a..e7d6149df 100644 --- a/glfw/x11_window.c +++ b/glfw/x11_window.c @@ -3085,7 +3085,7 @@ VkResult _glfwPlatformCreateWindowSurface(VkInstance instance, } void -_glfwPlatformUpdateIMEState(_GLFWwindow *w, int which, int a, int b, int c, int d) { +_glfwPlatformUpdateIMEState(_GLFWwindow *w, GLFWIMEUpdateState which, int a, int b, int c, int d) { glfw_xkb_update_ime_state(w, &_glfw.x11.xkb, which, a, b, c, d); } diff --git a/glfw/xkb_glfw.c b/glfw/xkb_glfw.c index 314f10eb0..47b16d5a0 100644 --- a/glfw/xkb_glfw.c +++ b/glfw/xkb_glfw.c @@ -555,13 +555,13 @@ format_xkb_mods(_GLFWXKBData *xkb, const char* name, xkb_mod_mask_t mods) { } void -glfw_xkb_update_ime_state(_GLFWwindow *w, _GLFWXKBData *xkb, int which, int a, int b, int c, int d) { +glfw_xkb_update_ime_state(_GLFWwindow *w, _GLFWXKBData *xkb, GLFWIMEUpdateState which, int a, int b, int c, int d) { int x = 0, y = 0; switch(which) { - case 1: + case GLFW_IME_UPDATE_FOCUS: glfw_ibus_set_focused(&xkb->ibus, a ? true : false); break; - case 2: + case GLFW_IME_UPDATE_CURSOR_POSITION: _glfwPlatformGetWindowPos(w, &x, &y); x += a; y += b; glfw_ibus_set_cursor_geometry(&xkb->ibus, x, y, c, d); diff --git a/glfw/xkb_glfw.h b/glfw/xkb_glfw.h index 39b74c851..68d7ccd18 100644 --- a/glfw/xkb_glfw.h +++ b/glfw/xkb_glfw.h @@ -92,5 +92,5 @@ const char* glfw_xkb_keysym_name(xkb_keysym_t sym); xkb_keysym_t glfw_xkb_sym_for_key(uint32_t key); void glfw_xkb_handle_key_event(_GLFWwindow *window, _GLFWXKBData *xkb, xkb_keycode_t keycode, int action); int glfw_xkb_keysym_from_name(const char *name, bool case_sensitive); -void glfw_xkb_update_ime_state(_GLFWwindow *w, _GLFWXKBData *xkb, int which, int a, int b, int c, int d); +void glfw_xkb_update_ime_state(_GLFWwindow *w, _GLFWXKBData *xkb, GLFWIMEUpdateState which, int a, int b, int c, int d); void glfw_xkb_key_from_ime(_GLFWIBUSKeyEvent *ev, bool handled_by_ime, bool failed); diff --git a/kitty/glfw-wrapper.h b/kitty/glfw-wrapper.h index bcb40a0bc..a5856b1ec 100644 --- a/kitty/glfw-wrapper.h +++ b/kitty/glfw-wrapper.h @@ -917,17 +917,24 @@ typedef struct GLFWwindow GLFWwindow; * @ingroup input */ typedef struct GLFWcursor GLFWcursor; + typedef enum { GLFW_RELEASE = 0, GLFW_PRESS = 1, GLFW_REPEAT = 2 } GLFWKeyAction; + typedef enum { GLFW_IME_NONE, GLFW_IME_PREEDIT_CHANGED, GLFW_IME_COMMIT_TEXT } GLFWIMEState; +typedef enum { + GLFW_IME_UPDATE_FOCUS = 1, + GLFW_IME_UPDATE_CURSOR_POSITION = 2 +} GLFWIMEUpdateState; + typedef struct GLFWkeyevent { // The [keyboard key](@ref keys) that was pressed or released. @@ -1925,7 +1932,7 @@ typedef GLFWkeyboardfun (*glfwSetKeyboardCallback_func)(GLFWwindow*, GLFWkeyboar GFW_EXTERN glfwSetKeyboardCallback_func glfwSetKeyboardCallback_impl; #define glfwSetKeyboardCallback glfwSetKeyboardCallback_impl -typedef void (*glfwUpdateIMEState_func)(GLFWwindow*, int, int, int, int, int); +typedef void (*glfwUpdateIMEState_func)(GLFWwindow*, GLFWIMEUpdateState, int, int, int, int); GFW_EXTERN glfwUpdateIMEState_func glfwUpdateIMEState_impl; #define glfwUpdateIMEState glfwUpdateIMEState_impl From 34d06fa3e9de402049fb4aca5dc74f96221b36cf Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 23 Mar 2021 16:05:22 +0530 Subject: [PATCH 09/15] Use a struct for IME update events Allows for easier extension in the future --- glfw/cocoa_window.m | 4 ++-- glfw/glfw3.h | 20 +++++++++++++------- glfw/input.c | 4 ++-- glfw/internal.h | 2 +- glfw/wl_window.c | 4 ++-- glfw/x11_window.c | 4 ++-- glfw/xkb_glfw.c | 10 +++++----- glfw/xkb_glfw.h | 2 +- kitty/glfw-wrapper.h | 14 ++++++++++++-- kitty/glfw.c | 3 ++- kitty/keys.c | 4 +++- 11 files changed, 45 insertions(+), 26 deletions(-) diff --git a/glfw/cocoa_window.m b/glfw/cocoa_window.m index afed53938..1ad25a46c 100644 --- a/glfw/cocoa_window.m +++ b/glfw/cocoa_window.m @@ -1295,8 +1295,8 @@ is_ascii_control_char(char x) { [[markedText mutableString] setString:@""]; } -void _glfwPlatformUpdateIMEState(_GLFWwindow *w, GLFWIMEUpdateState which, int a, int b, int c, int d) { - [w->ns.view updateIMEStateFor: which left:(CGFloat)a top:(CGFloat)b cellWidth:(CGFloat)c cellHeight:(CGFloat)d]; +void _glfwPlatformUpdateIMEState(_GLFWwindow *w, const GLFWIMEUpdateEvent *ev) { + [w->ns.view updateIMEStateFor: which left:(CGFloat)ev->cursor.left top:(CGFloat)ev->cursor.top cellWidth:(CGFloat)ev->cursor.width cellHeight:(CGFloat)ev->cursor.height]; } - (void)updateIMEStateFor:(GLFWIMEUpdateState)which diff --git a/glfw/glfw3.h b/glfw/glfw3.h index 18518e85a..14fce1227 100644 --- a/glfw/glfw3.h +++ b/glfw/glfw3.h @@ -1195,7 +1195,17 @@ typedef enum { typedef enum { GLFW_IME_UPDATE_FOCUS = 1, GLFW_IME_UPDATE_CURSOR_POSITION = 2 -} GLFWIMEUpdateState; +} GLFWIMEUpdateType; + +typedef struct GLFWIMEUpdateEvent { + GLFWIMEUpdateType type; + const char *before_text, *at_text, *after_text; + bool focused; + struct { + int left, top, width, height; + } cursor; +} GLFWIMEUpdateEvent; + typedef struct GLFWkeyevent { @@ -4545,16 +4555,12 @@ GLFWAPI GLFWkeyboardfun glfwSetKeyboardCallback(GLFWwindow* window, GLFWkeyboard * Used to notify the IME system of changes in state such as focus gained/lost * and text cursor position. * - * @param which: What data to notify. - * @param a, b, c, d: Interpreted based on the value of which. When which is GLFW_IME_UPDATE_FOCUS - * a is interpreted as a boolean indicating focus gained/lost. When which is GLFW_IME_UPDATE_CURSOR_POSITION - * a, b, c, d are the cursor x, y, width and height values (in the window co-ordinate - * system). + * @param ev: What data to notify. * * @ingroup input * @since Added in version 4.0 */ -GLFWAPI void glfwUpdateIMEState(GLFWwindow* window, GLFWIMEUpdateState which, int a, int b, int c, int d); +GLFWAPI void glfwUpdateIMEState(GLFWwindow* window, const GLFWIMEUpdateEvent *ev); /*! @brief Sets the mouse button callback. diff --git a/glfw/input.c b/glfw/input.c index 7ba7922df..f07bccfed 100644 --- a/glfw/input.c +++ b/glfw/input.c @@ -1010,13 +1010,13 @@ GLFWAPI GLFWkeyboardfun glfwSetKeyboardCallback(GLFWwindow* handle, GLFWkeyboard return cbfun; } -GLFWAPI void glfwUpdateIMEState(GLFWwindow* handle, GLFWIMEUpdateState which, int a, int b, int c, int d) { +GLFWAPI void glfwUpdateIMEState(GLFWwindow* handle, const GLFWIMEUpdateEvent *ev) { _GLFWwindow* window = (_GLFWwindow*) handle; assert(window != NULL); _GLFW_REQUIRE_INIT(); #if defined(_GLFW_X11) || defined(_GLFW_WAYLAND) || defined(_GLFW_COCOA) - _glfwPlatformUpdateIMEState(window, which, a, b, c, d); + _glfwPlatformUpdateIMEState(window, ev); #else (void)window; (void)which; (void)a; (void)b; (void)c; (void)d; #endif diff --git a/glfw/internal.h b/glfw/internal.h index 88076bed1..85e6da567 100644 --- a/glfw/internal.h +++ b/glfw/internal.h @@ -728,7 +728,7 @@ void _glfwPlatformSetWindowDecorated(_GLFWwindow* window, bool enabled); void _glfwPlatformSetWindowFloating(_GLFWwindow* window, bool enabled); void _glfwPlatformSetWindowMousePassthrough(_GLFWwindow* window, bool enabled); void _glfwPlatformSetWindowOpacity(_GLFWwindow* window, float opacity); -void _glfwPlatformUpdateIMEState(_GLFWwindow *w, GLFWIMEUpdateState which, int a, int b, int c, int d); +void _glfwPlatformUpdateIMEState(_GLFWwindow *w, const GLFWIMEUpdateEvent *ev); void _glfwPlatformPollEvents(void); void _glfwPlatformWaitEvents(void); diff --git a/glfw/wl_window.c b/glfw/wl_window.c index b3eb96a59..4c7bf5dc5 100644 --- a/glfw/wl_window.c +++ b/glfw/wl_window.c @@ -2105,8 +2105,8 @@ VkResult _glfwPlatformCreateWindowSurface(VkInstance instance, } void -_glfwPlatformUpdateIMEState(_GLFWwindow *w, GLFWIMEUpdateState which, int a, int b, int c, int d) { - glfw_xkb_update_ime_state(w, &_glfw.wl.xkb, which, a, b, c, d); +_glfwPlatformUpdateIMEState(_GLFWwindow *w, const GLFWIMEUpdateEvent *ev) { + glfw_xkb_update_ime_state(w, &_glfw.wl.xkb, ev); } static void diff --git a/glfw/x11_window.c b/glfw/x11_window.c index e7d6149df..e00545c6c 100644 --- a/glfw/x11_window.c +++ b/glfw/x11_window.c @@ -3085,8 +3085,8 @@ VkResult _glfwPlatformCreateWindowSurface(VkInstance instance, } void -_glfwPlatformUpdateIMEState(_GLFWwindow *w, GLFWIMEUpdateState which, int a, int b, int c, int d) { - glfw_xkb_update_ime_state(w, &_glfw.x11.xkb, which, a, b, c, d); +_glfwPlatformUpdateIMEState(_GLFWwindow *w, const GLFWIMEUpdateEvent *ev) { + glfw_xkb_update_ime_state(w, &_glfw.x11.xkb, ev); } ////////////////////////////////////////////////////////////////////////// diff --git a/glfw/xkb_glfw.c b/glfw/xkb_glfw.c index 47b16d5a0..6c92e7e87 100644 --- a/glfw/xkb_glfw.c +++ b/glfw/xkb_glfw.c @@ -555,16 +555,16 @@ format_xkb_mods(_GLFWXKBData *xkb, const char* name, xkb_mod_mask_t mods) { } void -glfw_xkb_update_ime_state(_GLFWwindow *w, _GLFWXKBData *xkb, GLFWIMEUpdateState which, int a, int b, int c, int d) { +glfw_xkb_update_ime_state(_GLFWwindow *w, _GLFWXKBData *xkb, const GLFWIMEUpdateEvent *ev) { int x = 0, y = 0; - switch(which) { + switch(ev->type) { case GLFW_IME_UPDATE_FOCUS: - glfw_ibus_set_focused(&xkb->ibus, a ? true : false); + glfw_ibus_set_focused(&xkb->ibus, ev->focused); break; case GLFW_IME_UPDATE_CURSOR_POSITION: _glfwPlatformGetWindowPos(w, &x, &y); - x += a; y += b; - glfw_ibus_set_cursor_geometry(&xkb->ibus, x, y, c, d); + x += ev->cursor.left; y += ev->cursor.top; + glfw_ibus_set_cursor_geometry(&xkb->ibus, x, y, ev->cursor.width, ev->cursor.height); break; } } diff --git a/glfw/xkb_glfw.h b/glfw/xkb_glfw.h index 68d7ccd18..d2c086a3f 100644 --- a/glfw/xkb_glfw.h +++ b/glfw/xkb_glfw.h @@ -92,5 +92,5 @@ const char* glfw_xkb_keysym_name(xkb_keysym_t sym); xkb_keysym_t glfw_xkb_sym_for_key(uint32_t key); void glfw_xkb_handle_key_event(_GLFWwindow *window, _GLFWXKBData *xkb, xkb_keycode_t keycode, int action); int glfw_xkb_keysym_from_name(const char *name, bool case_sensitive); -void glfw_xkb_update_ime_state(_GLFWwindow *w, _GLFWXKBData *xkb, GLFWIMEUpdateState which, int a, int b, int c, int d); +void glfw_xkb_update_ime_state(_GLFWwindow *w, _GLFWXKBData *xkb, const GLFWIMEUpdateEvent *ev); void glfw_xkb_key_from_ime(_GLFWIBUSKeyEvent *ev, bool handled_by_ime, bool failed); diff --git a/kitty/glfw-wrapper.h b/kitty/glfw-wrapper.h index a5856b1ec..101cde393 100644 --- a/kitty/glfw-wrapper.h +++ b/kitty/glfw-wrapper.h @@ -933,7 +933,17 @@ typedef enum { typedef enum { GLFW_IME_UPDATE_FOCUS = 1, GLFW_IME_UPDATE_CURSOR_POSITION = 2 -} GLFWIMEUpdateState; +} GLFWIMEUpdateType; + +typedef struct GLFWIMEUpdateEvent { + GLFWIMEUpdateType type; + const char *before_text, *at_text, *after_text; + bool focused; + struct { + int left, top, width, height; + } cursor; +} GLFWIMEUpdateEvent; + typedef struct GLFWkeyevent { @@ -1932,7 +1942,7 @@ typedef GLFWkeyboardfun (*glfwSetKeyboardCallback_func)(GLFWwindow*, GLFWkeyboar GFW_EXTERN glfwSetKeyboardCallback_func glfwSetKeyboardCallback_impl; #define glfwSetKeyboardCallback glfwSetKeyboardCallback_impl -typedef void (*glfwUpdateIMEState_func)(GLFWwindow*, GLFWIMEUpdateState, int, int, int, int); +typedef void (*glfwUpdateIMEState_func)(GLFWwindow*, const GLFWIMEUpdateEvent*); GFW_EXTERN glfwUpdateIMEState_func glfwUpdateIMEState_impl; #define glfwUpdateIMEState glfwUpdateIMEState_impl diff --git a/kitty/glfw.c b/kitty/glfw.c index b0ff141b3..6e8b87a1f 100644 --- a/kitty/glfw.c +++ b/kitty/glfw.c @@ -343,7 +343,8 @@ window_focus_callback(GLFWwindow *w, int focused) { global_state.callback_os_window->cursor_blink_zero_time = now; if (is_window_ready_for_callbacks()) { WINDOW_CALLBACK(on_focus, "O", focused ? Py_True : Py_False); - glfwUpdateIMEState(global_state.callback_os_window->handle, 1, focused, 0, 0, 0); + GLFWIMEUpdateEvent ev = { .type = GLFW_IME_UPDATE_FOCUS, .focused = focused }; + glfwUpdateIMEState(global_state.callback_os_window->handle, &ev); } request_tick_callback(); global_state.callback_os_window = NULL; diff --git a/kitty/keys.c b/kitty/keys.c index f958b51f4..cde7bf4e3 100644 --- a/kitty/keys.c +++ b/kitty/keys.c @@ -85,7 +85,9 @@ update_ime_position(OSWindow *os_window, Window* w, Screen *screen) { unsigned int left = w->geometry.left, top = w->geometry.top; left += screen->cursor->x * cell_width; top += screen->cursor->y * cell_height; - glfwUpdateIMEState(global_state.callback_os_window->handle, 2, left, top, cell_width, cell_height); + GLFWIMEUpdateEvent ev = { .type = GLFW_IME_UPDATE_CURSOR_POSITION }; + ev.cursor.left = left; ev.cursor.top = top; ev.cursor.width = cell_width; ev.cursor.height = cell_height; + glfwUpdateIMEState(global_state.callback_os_window->handle, &ev); } void From df7790fdfeb0a82d870fd027fce91aedf40fd9d5 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 23 Mar 2021 16:31:25 +0530 Subject: [PATCH 10/15] ... --- glfw/cocoa_window.m | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/glfw/cocoa_window.m b/glfw/cocoa_window.m index 1ad25a46c..0b69f103d 100644 --- a/glfw/cocoa_window.m +++ b/glfw/cocoa_window.m @@ -1296,10 +1296,10 @@ is_ascii_control_char(char x) { } void _glfwPlatformUpdateIMEState(_GLFWwindow *w, const GLFWIMEUpdateEvent *ev) { - [w->ns.view updateIMEStateFor: which left:(CGFloat)ev->cursor.left top:(CGFloat)ev->cursor.top cellWidth:(CGFloat)ev->cursor.width cellHeight:(CGFloat)ev->cursor.height]; + [w->ns.view updateIMEStateFor: ev->type left:(CGFloat)ev->cursor.left top:(CGFloat)ev->cursor.top cellWidth:(CGFloat)ev->cursor.width cellHeight:(CGFloat)ev->cursor.height]; } -- (void)updateIMEStateFor:(GLFWIMEUpdateState)which +- (void)updateIMEStateFor:(GLFWIMEUpdateType)which left:(CGFloat)left top:(CGFloat)top cellWidth:(CGFloat)cellWidth From 62997956a097c90841fe1827e88842005c383564 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 23 Mar 2021 15:07:40 +0530 Subject: [PATCH 11/15] Start work on Wayland text input protocol support --- glfw/source-info.json | 5 ++- glfw/wl_init.c | 7 ++++ glfw/wl_platform.h | 1 + glfw/wl_text_input.c | 85 +++++++++++++++++++++++++++++++++++++++++++ glfw/wl_text_input.h | 14 +++++++ glfw/xkb_glfw.c | 2 + 6 files changed, 113 insertions(+), 1 deletion(-) create mode 100644 glfw/wl_text_input.c create mode 100644 glfw/wl_text_input.h diff --git a/glfw/source-info.json b/glfw/source-info.json index ee7c666f3..c333e8508 100644 --- a/glfw/source-info.json +++ b/glfw/source-info.json @@ -55,6 +55,7 @@ "wl_platform.h", "posix_thread.h", "wl_cursors.h", + "wl_text_input.h", "xkb_glfw.h", "dbus_glfw.h", "ibus_glfw.h", @@ -73,13 +74,15 @@ "unstable/pointer-constraints/pointer-constraints-unstable-v1.xml", "unstable/idle-inhibit/idle-inhibit-unstable-v1.xml", "unstable/xdg-decoration/xdg-decoration-unstable-v1.xml", - "unstable/primary-selection/primary-selection-unstable-v1.xml" + "unstable/primary-selection/primary-selection-unstable-v1.xml", + "unstable/text-input/text-input-unstable-v3.xml" ], "sources": [ "wl_init.c", "wl_monitor.c", "wl_window.c", "wl_cursors.c", + "wl_text_input.c", "posix_thread.c", "xkb_glfw.c", "dbus_glfw.c", diff --git a/glfw/wl_init.c b/glfw/wl_init.c index ff9a35cd6..3903c6496 100644 --- a/glfw/wl_init.c +++ b/glfw/wl_init.c @@ -584,6 +584,7 @@ static void registryHandleGlobal(void* data UNUSED, if (_glfw.wl.primarySelectionDeviceManager && !_glfw.wl.primarySelectionDevice) { _glfwSetupWaylandPrimarySelectionDevice(); } + _glfwWaylandInitTextInput(); } } else if (strcmp(interface, "xdg_wm_base") == 0) @@ -617,6 +618,11 @@ static void registryHandleGlobal(void* data UNUSED, &zwp_pointer_constraints_v1_interface, 1); } + else if (strcmp(interface, GLFW_WAYLAND_TEXT_INPUT_INTERFACE_NAME) == 0) + { + _glfwWaylandBindTextInput(registry, name); + _glfwWaylandInitTextInput(); + } else if (strcmp(interface, "zwp_idle_inhibit_manager_v1") == 0) { _glfw.wl.idleInhibitManager = @@ -847,6 +853,7 @@ void _glfwPlatformTerminate(void) zwp_relative_pointer_manager_v1_destroy(_glfw.wl.relativePointerManager); if (_glfw.wl.pointerConstraints) zwp_pointer_constraints_v1_destroy(_glfw.wl.pointerConstraints); + _glfwWaylandDestroyTextInput(); if (_glfw.wl.idleInhibitManager) zwp_idle_inhibit_manager_v1_destroy(_glfw.wl.idleInhibitManager); if (_glfw.wl.dataSourceForClipboard) diff --git a/glfw/wl_platform.h b/glfw/wl_platform.h index 7aaed8f1e..5b6ee79d4 100644 --- a/glfw/wl_platform.h +++ b/glfw/wl_platform.h @@ -59,6 +59,7 @@ typedef VkBool32 (APIENTRY *PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR #include "wayland-pointer-constraints-unstable-v1-client-protocol.h" #include "wayland-idle-inhibit-unstable-v1-client-protocol.h" #include "wayland-primary-selection-unstable-v1-client-protocol.h" +#include "wl_text_input.h" #define _glfw_dlopen(name) dlopen(name, RTLD_LAZY | RTLD_LOCAL) #define _glfw_dlclose(handle) dlclose(handle) diff --git a/glfw/wl_text_input.c b/glfw/wl_text_input.c new file mode 100644 index 000000000..ae6a3d411 --- /dev/null +++ b/glfw/wl_text_input.c @@ -0,0 +1,85 @@ +/* + * wl_text_input.c + * Copyright (C) 2021 Kovid Goyal + * + * Distributed under terms of the GPL3 license. + */ + +#include "wl_text_input.h" +#include "internal.h" +#include "wayland-text-input-unstable-v3-client-protocol.h" + +static struct zwp_text_input_v3* text_input; +static struct zwp_text_input_manager_v3* text_input_manager; + +static void +text_input_enter(void *data UNUSED, struct zwp_text_input_v3 *text_input UNUSED, struct wl_surface *surface UNUSED) { + printf("enter text input\n"); +} + +static void +text_input_leave(void *data UNUSED, struct zwp_text_input_v3 *text_input UNUSED, struct wl_surface *surface UNUSED) { + printf("leave text input\n"); +} + +static void +text_input_preedit_string( + void *data UNUSED, + struct zwp_text_input_v3 *text_input UNUSED, + const char *text UNUSED, + int32_t cursor_begin UNUSED, + int32_t cursor_end UNUSED +) { +} + +static void +text_input_commit_string(void *data UNUSED, struct zwp_text_input_v3 *text_input UNUSED, const char *text UNUSED) { +} + +static void +text_input_delete_surrounding_text( + void *data UNUSED, + struct zwp_text_input_v3 *zwp_text_input_v3 UNUSED, + uint32_t before_length UNUSED, + uint32_t after_length UNUSED) { +} + +static void +text_input_done(void *data UNUSED, struct zwp_text_input_v3 *zwp_text_input_v3 UNUSED, uint32_t serial UNUSED) { +} + +void +_glfwWaylandBindTextInput(struct wl_registry* registry, uint32_t name) { + if (!text_input_manager) { + text_input_manager = + wl_registry_bind(registry, name, + &zwp_text_input_manager_v3_interface, + 1); + } +} + +void +_glfwWaylandInitTextInput(void) { + static const struct zwp_text_input_v3_listener text_input_listener = { + .enter = text_input_enter, + .leave = text_input_leave, + .preedit_string = text_input_preedit_string, + .commit_string = text_input_commit_string, + .delete_surrounding_text = text_input_delete_surrounding_text, + .done = text_input_done, + }; + if (!text_input) { + if (text_input_manager && _glfw.wl.seat) { + text_input = zwp_text_input_manager_v3_get_text_input( + text_input_manager, _glfw.wl.seat); + if (text_input) zwp_text_input_v3_add_listener(text_input, &text_input_listener, NULL); + } + } +} + +void +_glfwWaylandDestroyTextInput(void) { + if (text_input) zwp_text_input_v3_destroy(text_input); + if (text_input_manager) zwp_text_input_manager_v3_destroy(text_input_manager); + text_input = NULL; text_input_manager = NULL; +} diff --git a/glfw/wl_text_input.h b/glfw/wl_text_input.h new file mode 100644 index 000000000..a01934758 --- /dev/null +++ b/glfw/wl_text_input.h @@ -0,0 +1,14 @@ +/* + * Copyright (C) 2021 Kovid Goyal + * + * Distributed under terms of the GPL3 license. + */ + +#pragma once +#include + +#define GLFW_WAYLAND_TEXT_INPUT_INTERFACE_NAME "zwp_text_input_manager_v3" + +void _glfwWaylandBindTextInput(struct wl_registry* registry, uint32_t name); +void _glfwWaylandInitTextInput(void); +void _glfwWaylandDestroyTextInput(void); diff --git a/glfw/xkb_glfw.c b/glfw/xkb_glfw.c index 6c92e7e87..ca3d148b1 100644 --- a/glfw/xkb_glfw.c +++ b/glfw/xkb_glfw.c @@ -349,7 +349,9 @@ glfw_xkb_create_context(_GLFWXKBData *xkb) { "Failed to initialize XKB context"); return false; } +#ifndef _GLFW_WAYLAND glfw_connect_to_ibus(&xkb->ibus); +#endif return true; } From 67445a22d320c2d580931f2730e694fdc856cd30 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 23 Mar 2021 15:53:55 +0530 Subject: [PATCH 12/15] ... --- glfw/wl_text_input.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/glfw/wl_text_input.c b/glfw/wl_text_input.c index ae6a3d411..51dfd39e3 100644 --- a/glfw/wl_text_input.c +++ b/glfw/wl_text_input.c @@ -8,18 +8,19 @@ #include "wl_text_input.h" #include "internal.h" #include "wayland-text-input-unstable-v3-client-protocol.h" +#define debug(...) if (_glfw.hints.init.debugKeyboard) printf(__VA_ARGS__); static struct zwp_text_input_v3* text_input; static struct zwp_text_input_manager_v3* text_input_manager; static void text_input_enter(void *data UNUSED, struct zwp_text_input_v3 *text_input UNUSED, struct wl_surface *surface UNUSED) { - printf("enter text input\n"); + debug("text-input: enter event\n"); } static void text_input_leave(void *data UNUSED, struct zwp_text_input_v3 *text_input UNUSED, struct wl_surface *surface UNUSED) { - printf("leave text input\n"); + debug("text-input: leave event\n"); } static void From 317ecbc9faa4b88d172e635de7692401b06e1e77 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 24 Mar 2021 11:14:14 +0530 Subject: [PATCH 13/15] Wayland: Add support for the text input protocol Fixes #3410 --- docs/changelog.rst | 2 ++ glfw/wl_text_input.c | 67 ++++++++++++++++++++++++++++++++++++-------- glfw/wl_window.c | 5 ---- 3 files changed, 57 insertions(+), 17 deletions(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index a75930980..675f8a39b 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -144,6 +144,8 @@ To update |kitty|, :doc:`follow the instructions `. - Wayland: Fix key repeat being stopped by the release of an unrelated key (:iss:`2191`) +- Wayland: Add support for the text input protocol (:iss:`3410`) + - Add an option, :opt:`detect_urls` to control whether kitty will detect URLs when the mouse moves over them (:pull:`3118`) diff --git a/glfw/wl_text_input.c b/glfw/wl_text_input.c index 51dfd39e3..e8a6c1673 100644 --- a/glfw/wl_text_input.c +++ b/glfw/wl_text_input.c @@ -13,50 +13,73 @@ static struct zwp_text_input_v3* text_input; static struct zwp_text_input_manager_v3* text_input_manager; +static void commit(void) { if (text_input) zwp_text_input_v3_commit (text_input); } + static void text_input_enter(void *data UNUSED, struct zwp_text_input_v3 *text_input UNUSED, struct wl_surface *surface UNUSED) { debug("text-input: enter event\n"); + if (text_input) { + zwp_text_input_v3_enable(text_input); + zwp_text_input_v3_set_content_type(text_input, ZWP_TEXT_INPUT_V3_CONTENT_HINT_NONE, ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_TERMINAL); + commit(); + } } static void text_input_leave(void *data UNUSED, struct zwp_text_input_v3 *text_input UNUSED, struct wl_surface *surface UNUSED) { debug("text-input: leave event\n"); + if (text_input) { + zwp_text_input_v3_disable(text_input); + commit(); + } +} + +static inline void +send_text(const char *text, GLFWIMEState ime_state) { + _GLFWwindow *w = _glfwFocusedWindow(); + if (w && w->callbacks.keyboard) { + GLFWkeyevent fake_ev = {.action = GLFW_PRESS}; + fake_ev.text = text; + fake_ev.ime_state = ime_state; + w->callbacks.keyboard((GLFWwindow*) w, &fake_ev); + } } static void text_input_preedit_string( void *data UNUSED, struct zwp_text_input_v3 *text_input UNUSED, - const char *text UNUSED, - int32_t cursor_begin UNUSED, - int32_t cursor_end UNUSED + const char *text, + int32_t cursor_begin, + int32_t cursor_end ) { + debug("text-input: preedit_string event: text: %s cursor_begin: %d cursor_end: %d\n", text, cursor_begin, cursor_end); + send_text(text, GLFW_IME_PREEDIT_CHANGED); } static void -text_input_commit_string(void *data UNUSED, struct zwp_text_input_v3 *text_input UNUSED, const char *text UNUSED) { +text_input_commit_string(void *data UNUSED, struct zwp_text_input_v3 *text_input UNUSED, const char *text) { + debug("text-input: commit_string event: text: %s\n", text); + send_text(text, GLFW_IME_COMMIT_TEXT); } static void text_input_delete_surrounding_text( void *data UNUSED, struct zwp_text_input_v3 *zwp_text_input_v3 UNUSED, - uint32_t before_length UNUSED, - uint32_t after_length UNUSED) { + uint32_t before_length, + uint32_t after_length) { + debug("text-input: delete_surrounding_text event: before_length: %u after_length: %u\n", before_length, after_length); } static void text_input_done(void *data UNUSED, struct zwp_text_input_v3 *zwp_text_input_v3 UNUSED, uint32_t serial UNUSED) { + debug("text-input: done event: serial: %u\n", serial); } void _glfwWaylandBindTextInput(struct wl_registry* registry, uint32_t name) { - if (!text_input_manager) { - text_input_manager = - wl_registry_bind(registry, name, - &zwp_text_input_manager_v3_interface, - 1); - } + if (!text_input_manager) text_input_manager = wl_registry_bind(registry, name, &zwp_text_input_manager_v3_interface, 1); } void @@ -84,3 +107,23 @@ _glfwWaylandDestroyTextInput(void) { if (text_input_manager) zwp_text_input_manager_v3_destroy(text_input_manager); text_input = NULL; text_input_manager = NULL; } + +void +_glfwPlatformUpdateIMEState(_GLFWwindow *w, const GLFWIMEUpdateEvent *ev) { + if (!text_input) return; + switch(ev->type) { + case GLFW_IME_UPDATE_FOCUS: + debug("\ntext-input: updating IME focus state, focused: %d\n", ev->focused); + if (ev->focused) zwp_text_input_v3_enable(text_input); else zwp_text_input_v3_disable(text_input); + commit(); + break; + case GLFW_IME_UPDATE_CURSOR_POSITION: { + const int scale = w->wl.scale; + const int left = ev->cursor.left / scale, top = ev->cursor.top / scale, width = ev->cursor.width / scale, height = ev->cursor.height / scale; + debug("\ntext-input: updating cursor position: left=%d top=%d width=%d height=%d\n", left, top, width, height); + zwp_text_input_v3_set_cursor_rectangle(text_input, left, top, width, height); + commit(); + } + break; + } +} diff --git a/glfw/wl_window.c b/glfw/wl_window.c index 4c7bf5dc5..dffcb4beb 100644 --- a/glfw/wl_window.c +++ b/glfw/wl_window.c @@ -2104,11 +2104,6 @@ VkResult _glfwPlatformCreateWindowSurface(VkInstance instance, return err; } -void -_glfwPlatformUpdateIMEState(_GLFWwindow *w, const GLFWIMEUpdateEvent *ev) { - glfw_xkb_update_ime_state(w, &_glfw.wl.xkb, ev); -} - static void frame_handle_redraw(void *data, struct wl_callback *callback, uint32_t time UNUSED) { _GLFWwindow* window = (_GLFWwindow*) data; From 7c7933efa957373b69624c371e3727e9589bf111 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 24 Mar 2021 13:17:38 +0530 Subject: [PATCH 14/15] Fix #3416 --- kitty/fonts.c | 5 +++-- kitty_tests/fonts.py | 1 + 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/kitty/fonts.c b/kitty/fonts.c index 1d9a3a236..b6f508282 100644 --- a/kitty/fonts.c +++ b/kitty/fonts.c @@ -961,7 +961,7 @@ shape_run(CPUCell *first_cpu_cell, GPUCell *first_gpu_cell, index_type num_cells bool is_special = is_special_glyph(glyph_id, font, &G(current_cell_data)); bool is_empty = is_special && is_empty_glyph(glyph_id, font); uint32_t num_codepoints_used_by_glyph = 0; - bool is_last_glyph = G(glyph_idx) == G(num_glyphs) - 1; + bool is_last_glyph = G(glyph_idx) == G(num_glyphs) - 1, is_first_glyph = G(glyph_idx) == 0; Group *current_group = G(groups) + G(group_idx); if (is_last_glyph) { num_codepoints_used_by_glyph = UINT32_MAX; @@ -977,7 +977,8 @@ shape_run(CPUCell *first_cpu_cell, GPUCell *first_gpu_cell, index_type num_cells else add_to_current_group = ligature_type == INFINITE_LIGATURE_MIDDLE || ligature_type == INFINITE_LIGATURE_END || is_empty; } else { if (is_special) { - if (font->spacer_strategy == SPACERS_BEFORE) add_to_current_group = G(prev_was_empty); + if (!is_first_glyph && !current_group->num_cells) add_to_current_group = true; + else if (font->spacer_strategy == SPACERS_BEFORE) add_to_current_group = G(prev_was_empty); else add_to_current_group = is_empty; } else { add_to_current_group = !G(prev_was_special); diff --git a/kitty_tests/fonts.py b/kitty_tests/fonts.py index b565e53e2..c8461ac93 100644 --- a/kitty_tests/fonts.py +++ b/kitty_tests/fonts.py @@ -116,6 +116,7 @@ class Rendering(BaseTest): self.ae(groups('|\U0001F601|\U0001F64f|\U0001F63a|'), [(1, 1), (2, 1), (1, 1), (2, 1), (1, 1), (2, 1), (1, 1)]) self.ae(groups('He\u0347\u0305llo\u0337,', font='LiberationMono-Regular.ttf'), [(1, 1), (1, 3), (1, 1), (1, 1), (1, 2), (1, 1)]) + self.ae(groups('i\u0332\u0308', font='LiberationMono-Regular.ttf'), [(1, 2)]) def test_emoji_presentation(self): s = self.create_screen() From fc8e147e4a86c133633945c48f26a455e47a3324 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Thu, 25 Mar 2021 09:27:25 +0530 Subject: [PATCH 15/15] Fix mouse handling when using client side decorations The mouse co-ordinates used by glfw were all wrong. --- docs/changelog.rst | 2 ++ glfw/wl_init.c | 24 ++++++++++++------------ glfw/wl_platform.h | 2 +- 3 files changed, 15 insertions(+), 13 deletions(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index 675f8a39b..fc9be69d1 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -146,6 +146,8 @@ To update |kitty|, :doc:`follow the instructions `. - Wayland: Add support for the text input protocol (:iss:`3410`) +- Wayland: Fix mouse handling when using client side decorations + - Add an option, :opt:`detect_urls` to control whether kitty will detect URLs when the mouse moves over them (:pull:`3118`) diff --git a/glfw/wl_init.c b/glfw/wl_init.c index 3903c6496..c978dd58a 100644 --- a/glfw/wl_init.c +++ b/glfw/wl_init.c @@ -173,6 +173,8 @@ static void setCursor(GLFWCursorShape shape, _GLFWwindow* window) _glfw.wl.cursorPreviousShape = shape; } +#define x window->wl.allCursorPosX +#define y window->wl.allCursorPosY static void pointerHandleMotion(void* data UNUSED, struct wl_pointer* pointer UNUSED, uint32_t time UNUSED, @@ -181,15 +183,14 @@ static void pointerHandleMotion(void* data UNUSED, { _GLFWwindow* window = _glfw.wl.pointerFocus; GLFWCursorShape cursorShape = GLFW_ARROW_CURSOR; - double x, y; if (!window) return; if (window->cursorMode == GLFW_CURSOR_DISABLED) return; - x = wl_fixed_to_double(sx); - y = wl_fixed_to_double(sy); + window->wl.allCursorPosX = wl_fixed_to_double(sx); + window->wl.allCursorPosY = wl_fixed_to_double(sy); switch (window->wl.decorations.focus) { @@ -252,7 +253,7 @@ static void pointerHandleButton(void* data UNUSED, case mainWindow: break; case topDecoration: - if (window->wl.cursorPosY < _GLFW_DECORATION_WIDTH) + if (y < _GLFW_DECORATION_WIDTH) edges = XDG_TOPLEVEL_RESIZE_EDGE_TOP; else { @@ -261,21 +262,21 @@ static void pointerHandleButton(void* data UNUSED, } break; case leftDecoration: - if (window->wl.cursorPosY < _GLFW_DECORATION_WIDTH) + if (y < _GLFW_DECORATION_WIDTH) edges = XDG_TOPLEVEL_RESIZE_EDGE_TOP_LEFT; else edges = XDG_TOPLEVEL_RESIZE_EDGE_LEFT; break; case rightDecoration: - if (window->wl.cursorPosY < _GLFW_DECORATION_WIDTH) + if (y < _GLFW_DECORATION_WIDTH) edges = XDG_TOPLEVEL_RESIZE_EDGE_TOP_RIGHT; else edges = XDG_TOPLEVEL_RESIZE_EDGE_RIGHT; break; case bottomDecoration: - if (window->wl.cursorPosX < _GLFW_DECORATION_WIDTH) + if (x < _GLFW_DECORATION_WIDTH) edges = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_LEFT; - else if (window->wl.cursorPosX > window->wl.width + _GLFW_DECORATION_WIDTH) + else if (x > window->wl.width + _GLFW_DECORATION_WIDTH) edges = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_RIGHT; else edges = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM; @@ -293,10 +294,7 @@ static void pointerHandleButton(void* data UNUSED, { if (window->wl.decorations.focus != mainWindow && window->wl.xdg.toplevel) { - xdg_toplevel_show_window_menu(window->wl.xdg.toplevel, - _glfw.wl.seat, serial, - (int32_t)window->wl.cursorPosX, - (int32_t)window->wl.cursorPosY); + xdg_toplevel_show_window_menu(window->wl.xdg.toplevel, _glfw.wl.seat, serial, (int32_t)x, (int32_t)y - _GLFW_DECORATION_TOP); return; } } @@ -318,6 +316,8 @@ static void pointerHandleButton(void* data UNUSED, : GLFW_RELEASE, _glfw.wl.xkb.states.modifiers); } +#undef x +#undef y static void pointerHandleAxis(void* data UNUSED, struct wl_pointer* pointer UNUSED, diff --git a/glfw/wl_platform.h b/glfw/wl_platform.h index 5b6ee79d4..f29da2564 100644 --- a/glfw/wl_platform.h +++ b/glfw/wl_platform.h @@ -132,7 +132,7 @@ typedef struct _GLFWwindowWayland } xdg; _GLFWcursor* currentCursor; - double cursorPosX, cursorPosY; + double cursorPosX, cursorPosY, allCursorPosX, allCursorPosY; char* title; char appId[256];