From 1a5a0952d540933ff8eca9b743aea0e67aeee211 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 2 Dec 2021 20:36:09 +0100 Subject: [PATCH 01/24] =?UTF-8?q?changelog:=20add=20new=20=E2=80=98unrelea?= =?UTF-8?q?sed=E2=80=99=20section?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2d187ab8..cc9e0e95 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ # Changelog +* [Unreleased](#unreleased) * [1.10.2](#1-10-2) * [1.10.1](#1-10-1) * [1.10.0](#1-10-0) @@ -34,6 +35,16 @@ * [1.2.0](#1-2-0) +## Unreleased +### Added +### Changed +### Deprecated +### Removed +### Fixed +### Security +### Contributors + + ## 1.10.2 ### Added From d73c683ab1984e34d6d7e62b43dd57e6c95d836a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Fri, 3 Dec 2021 22:44:35 +0100 Subject: [PATCH 02/24] install: update instructions on how to manually compile the terminfo --- INSTALL.md | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/INSTALL.md b/INSTALL.md index 1e350f6f..3f5e40d8 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -449,19 +449,14 @@ sed 's/@default_terminfo@/foot/g' foot.info | \ ``` Where _”output-directory”_ **must** match the value passed to -`-Dcustom-terminfo-install-location` in the foot build. - -To compile and install directly (assuming the default -`-Dcustom-terminfo-install-location`): - -```sh -sudo tic -o /usr/share/foot/terminfo ... -``` +`-Dcustom-terminfo-install-location` in the foot build. If +`-Dcustom-terminfo-install-location` has not been set, `-o +` can simply be omitted. Or, if packaging: ```sh -tic -o ${DESTDIR}/usr/share/foot/terminfo ... +tic -o ${DESTDIR}/usr/share/terminfo ... ``` From 6f6af910a59e5cae0892a629a732d506d393b578 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Fri, 3 Dec 2021 22:46:09 +0100 Subject: [PATCH 03/24] install: add UTF-8 locate to runtime deps --- INSTALL.md | 1 + 1 file changed, 1 insertion(+) diff --git a/INSTALL.md b/INSTALL.md index 3f5e40d8..eb7caefb 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -39,6 +39,7 @@ subprojects. ### Running +* UTF-8 locale * fontconfig * freetype * pixman From 71606009fda541132de0f12c2b68b9e69c5bc7f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 28 Nov 2021 16:36:14 +0100 Subject: [PATCH 04/24] =?UTF-8?q?input:=20don=E2=80=99t=20ignore=20key=20r?= =?UTF-8?q?elease=20events?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Before this, key release events stopped the repeat timer, and then returned. Now, we run through the entire function. Most things are still only done on key press events. But, the goal here is to get to the keyboard protocol functions (and the kitty protocol in particular), and call them on release events too. This is in preparation for the kitty protocol mode 0b10, report event types. --- input.c | 107 +++++++++++++++++++++++++++++++++----------------------- 1 file changed, 63 insertions(+), 44 deletions(-) diff --git a/input.c b/input.c index 055c0fc8..117f4e08 100644 --- a/input.c +++ b/input.c @@ -1026,6 +1026,9 @@ static bool legacy_kbd_protocol(struct seat *seat, struct terminal *term, const struct kbd_ctx *ctx) { + if (ctx->key_state != WL_KEYBOARD_KEY_STATE_PRESSED) + return; + enum modifier keymap_mods = MOD_NONE; keymap_mods |= seat->kbd.shift ? MOD_SHIFT : MOD_NONE; keymap_mods |= seat->kbd.alt ? MOD_ALT : MOD_NONE; @@ -1418,15 +1421,20 @@ key_press_release(struct seat *seat, struct terminal *term, uint32_t serial, return; } - if (state == XKB_KEY_UP) { - stop_repeater(seat, key); - return; - } + const bool pressed = state == WL_KEYBOARD_KEY_STATE_PRESSED; + //const bool repeated = pressed && seat->kbd.repeat.dont_re_repeat; + const bool released = state == WL_KEYBOARD_KEY_STATE_RELEASED; + + if (released) + stop_repeater(seat, key); + + bool should_repeat = + pressed && xkb_keymap_key_repeats(seat->kbd.xkb_keymap, key); - bool should_repeat = xkb_keymap_key_repeats(seat->kbd.xkb_keymap, key); xkb_keysym_t sym = xkb_state_key_get_one_sym(seat->kbd.xkb_state, key); - if (state == XKB_KEY_DOWN && term->conf->mouse.hide_when_typing && + if (pressed && term->conf->mouse.hide_when_typing && + /* TODO: better way to detect modifiers */ sym != XKB_KEY_Shift_L && sym != XKB_KEY_Shift_R && sym != XKB_KEY_Control_L && sym != XKB_KEY_Control_R && @@ -1443,7 +1451,8 @@ key_press_release(struct seat *seat, struct terminal *term, uint32_t serial, enum xkb_compose_status compose_status = XKB_COMPOSE_NOTHING; if (seat->kbd.xkb_compose_state != NULL) { - xkb_compose_state_feed(seat->kbd.xkb_compose_state, sym); + if (pressed) + xkb_compose_state_feed(seat->kbd.xkb_compose_state, sym); compose_status = xkb_compose_state_get_status( seat->kbd.xkb_compose_state); } @@ -1464,18 +1473,26 @@ key_press_release(struct seat *seat, struct terminal *term, uint32_t serial, size_t raw_count = xkb_keymap_key_get_syms_by_level( seat->kbd.xkb_keymap, key, layout_idx, 0, &raw_syms); - if (term->is_searching) { - if (should_repeat) - start_repeater(seat, key); - search_input( - seat, term, key, sym, bind_mods, bind_consumed, raw_syms, raw_count, serial); - return; - } else if (urls_mode_is_active(term)) { - if (should_repeat) - start_repeater(seat, key); - urls_input( - seat, term, key, sym, bind_mods, bind_consumed, raw_syms, raw_count, serial); - return; + if (pressed) { + if (term->is_searching) { + if (should_repeat) + start_repeater(seat, key); + + search_input( + seat, term, key, sym, bind_mods, bind_consumed, + raw_syms, raw_count, serial); + return; + } + + else if (urls_mode_is_active(term)) { + if (should_repeat) + start_repeater(seat, key); + + urls_input( + seat, term, key, sym, bind_mods, bind_consumed, + raw_syms, raw_count, serial); + return; + } } #if 0 @@ -1499,36 +1516,38 @@ key_press_release(struct seat *seat, struct terminal *term, uint32_t serial, /* * User configurable bindings */ - tll_foreach(seat->kbd.bindings.key, it) { - const struct key_binding *bind = &it->item; + if (pressed) { + tll_foreach(seat->kbd.bindings.key, it) { + const struct key_binding *bind = &it->item; - /* Match translated symbol */ - if (bind->sym == sym && - bind->mods == (bind_mods & ~bind_consumed) && - execute_binding( - seat, term, bind->action, bind->pipe_argv, serial)) - { - goto maybe_repeat; - } - - if (bind->mods != bind_mods) - continue; - - /* Match untranslated symbols */ - for (size_t i = 0; i < raw_count; i++) { - if (bind->sym == raw_syms[i] && execute_binding( + /* Match translated symbol */ + if (bind->sym == sym && + bind->mods == (bind_mods & ~bind_consumed) && + execute_binding( seat, term, bind->action, bind->pipe_argv, serial)) { goto maybe_repeat; } - } - /* Match raw key code */ - tll_foreach(bind->key_codes, code) { - if (code->item == key && execute_binding( - seat, term, bind->action, bind->pipe_argv, serial)) - { - goto maybe_repeat; + if (bind->mods != bind_mods) + continue; + + /* Match untranslated symbols */ + for (size_t i = 0; i < raw_count; i++) { + if (bind->sym == raw_syms[i] && execute_binding( + seat, term, bind->action, bind->pipe_argv, serial)) + { + goto maybe_repeat; + } + } + + /* Match raw key code */ + tll_foreach(bind->key_codes, code) { + if (code->item == key && execute_binding( + seat, term, bind->action, bind->pipe_argv, serial)) + { + goto maybe_repeat; + } } } } @@ -1590,7 +1609,7 @@ key_press_release(struct seat *seat, struct terminal *term, uint32_t serial, ? kitty_kbd_protocol(seat, term, &ctx) : legacy_kbd_protocol(seat, term, &ctx); - if (seat->kbd.xkb_compose_state != NULL) + if (seat->kbd.xkb_compose_state != NULL && pressed) xkb_compose_state_reset(seat->kbd.xkb_compose_state); if (utf8 != buf) From 30f60259cc13fd13d483b8e6cd36338324735eb9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 28 Nov 2021 18:45:28 +0100 Subject: [PATCH 05/24] =?UTF-8?q?input:=20kitty:=20add=20support=20for=20t?= =?UTF-8?q?he=20=E2=80=9Creport=20event=E2=80=9D=20mode=20(0b10)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- input.c | 51 ++++++++++++++++++++++++++++++++++++++------------- terminal.h | 2 +- 2 files changed, 39 insertions(+), 14 deletions(-) diff --git a/input.c b/input.c index 117f4e08..f7b0e23c 100644 --- a/input.c +++ b/input.c @@ -1027,7 +1027,7 @@ legacy_kbd_protocol(struct seat *seat, struct terminal *term, const struct kbd_ctx *ctx) { if (ctx->key_state != WL_KEYBOARD_KEY_STATE_PRESSED) - return; + return false; enum modifier keymap_mods = MOD_NONE; keymap_mods |= seat->kbd.shift ? MOD_SHIFT : MOD_NONE; @@ -1143,6 +1143,21 @@ static bool kitty_kbd_protocol(struct seat *seat, struct terminal *term, const struct kbd_ctx *ctx) { + const bool repeating = seat->kbd.repeat.dont_re_repeat; + const bool pressed = ctx->key_state == WL_KEYBOARD_KEY_STATE_PRESSED && !repeating; + const bool released = ctx->key_state == WL_KEYBOARD_KEY_STATE_RELEASED; + + const enum kitty_kbd_flags flags = term->grid->kitty_kbd.flags[term->grid->kitty_kbd.idx]; + const bool disambiguate = flags & KITTY_KBD_DISAMBIGUATE; + const bool report_events = flags & KITTY_KBD_REPORT_EVENT; + + if (!report_events && !pressed) + return false; + + /* TODO: should we even bother with this, or just say it’s not supported? */ + if (!disambiguate && pressed) + return legacy_kbd_protocol(seat, term, ctx); + const xkb_mod_mask_t mods = ctx->mods & seat->kbd.kitty_significant; const xkb_mod_mask_t consumed = xkb_state_key_get_consumed_mods2( seat->kbd.xkb_state, ctx->key, XKB_CONSUMED_MODE_GTK) & seat->kbd.kitty_significant; @@ -1155,11 +1170,6 @@ kitty_kbd_protocol(struct seat *seat, struct terminal *term, const uint8_t *const utf8 = ctx->utf8.buf; const size_t count = ctx->utf8.count; - if (ctx->compose_status == XKB_COMPOSE_COMPOSED) { - term_to_slave(term, utf8, count); - return true; - } - if (effective == 0) { switch (sym) { case XKB_KEY_Return: term_to_slave(term, "\r", 1); return true; @@ -1168,6 +1178,11 @@ kitty_kbd_protocol(struct seat *seat, struct terminal *term, } } + if (ctx->compose_status == XKB_COMPOSE_COMPOSED && !released) { + term_to_slave(term, utf8, count); + return true; + } + /* * Printables without any modifiers are printed as is. * @@ -1177,7 +1192,7 @@ kitty_kbd_protocol(struct seat *seat, struct terminal *term, * keys, like Return and Backspace; figure out if there’s some * better magic than filtering out Caps- and Num-Lock here.. */ - if (iswprint(utf32) && (effective & ~caps_num) == 0) { + if (iswprint(utf32) && (effective & ~caps_num) == 0 && !released) { term_to_slave(term, utf8, count); return true; } @@ -1319,7 +1334,7 @@ kitty_kbd_protocol(struct seat *seat, struct terminal *term, default: if (count > 0) { - if (effective == 0) { + if (effective == 0 && !released) { term_to_slave(term, utf8, count); return true; } @@ -1386,6 +1401,16 @@ kitty_kbd_protocol(struct seat *seat, struct terminal *term, xassert(encoded_mods >= 1); + char event[4]; + if (report_events) { + /* Note: this deviates slightly from Kitty, which omits the + * “:1” subparameter for key press events */ + event[0] = ':'; + event[1] = '0' + (pressed ? 1 : repeating ? 2 : 3); + event[2] = '\0'; + } else + event[0] = '\0'; + char buf[16]; int bytes; @@ -1393,14 +1418,14 @@ kitty_kbd_protocol(struct seat *seat, struct terminal *term, return false; if (final == 'u' || final == '~') { - if (encoded_mods > 1) - bytes = snprintf(buf, sizeof(buf), "\x1b[%u;%u%c", - key, encoded_mods, final); + if (encoded_mods > 1 || event[0] != '\0') + bytes = snprintf(buf, sizeof(buf), "\x1b[%u;%u%s%c", + key, encoded_mods, event, final); else bytes = snprintf(buf, sizeof(buf), "\x1b[%u%c", key, final); } else { - if (encoded_mods > 1) - bytes = snprintf(buf, sizeof(buf), "\x1b[1;%u%c", encoded_mods, final); + if (encoded_mods > 1 || event[0] != '\0') + bytes = snprintf(buf, sizeof(buf), "\x1b[1;%u%s%c", encoded_mods, event, final); else bytes = snprintf(buf, sizeof(buf), "\x1b[%c", final); } diff --git a/terminal.h b/terminal.h index 62ccfb2a..6b7b01f8 100644 --- a/terminal.h +++ b/terminal.h @@ -133,7 +133,7 @@ enum kitty_kbd_flags { KITTY_KBD_REPORT_ALTERNATE = 0x04, KITTY_KBD_REPORT_ALL = 0x08, KITTY_KBD_REPORT_ASSOCIATED = 0x10, - KITTY_KBD_SUPPORTED = KITTY_KBD_DISAMBIGUATE, + KITTY_KBD_SUPPORTED = KITTY_KBD_DISAMBIGUATE | KITTY_KBD_REPORT_EVENT, }; struct grid { From a242ba3f979a5656c2b3409a137773a71238152b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 28 Nov 2021 19:03:49 +0100 Subject: [PATCH 06/24] changelog: kitty: report events --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cc9e0e95..e9da6281 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -52,7 +52,8 @@ * New value, `max`, for `[tweak].grapheme-width-method`. * Initial support for the [Kitty keyboard protocol](https://sw.kovidgoyal.net/kitty/keyboard-protocol/). Modes supported: - - [Disambiguate escape codes](https://sw.kovidgoyal.net/kitty/keyboard-protocol/#disambiguate) + - [Disambiguate escape codes](https://sw.kovidgoyal.net/kitty/keyboard-protocol/#disambiguate) (mode `0b1`) + - [Report event types](https://sw.kovidgoyal.net/kitty/keyboard-protocol/#report-events) (mode `0b10`) * “Window menu” (compositor provided) on right clicks on the CSD title bar. From 5d94b02a03877adf85f4e4f721d4f1adaece5397 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 28 Nov 2021 19:20:37 +0100 Subject: [PATCH 07/24] input: kitty: treat repeating == pressed when report-events is off --- input.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/input.c b/input.c index f7b0e23c..1b660741 100644 --- a/input.c +++ b/input.c @@ -1151,7 +1151,7 @@ kitty_kbd_protocol(struct seat *seat, struct terminal *term, const bool disambiguate = flags & KITTY_KBD_DISAMBIGUATE; const bool report_events = flags & KITTY_KBD_REPORT_EVENT; - if (!report_events && !pressed) + if (!report_events && released) return false; /* TODO: should we even bother with this, or just say it’s not supported? */ From 149e978bc4281bd0d7ad1fd345ad2b843458d6d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 29 Nov 2021 20:12:49 +0100 Subject: [PATCH 08/24] input: reset compose state on key *releases*, not presses --- input.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/input.c b/input.c index 1b660741..bfa0bbd5 100644 --- a/input.c +++ b/input.c @@ -1634,7 +1634,7 @@ key_press_release(struct seat *seat, struct terminal *term, uint32_t serial, ? kitty_kbd_protocol(seat, term, &ctx) : legacy_kbd_protocol(seat, term, &ctx); - if (seat->kbd.xkb_compose_state != NULL && pressed) + if (seat->kbd.xkb_compose_state != NULL && released) xkb_compose_state_reset(seat->kbd.xkb_compose_state); if (utf8 != buf) From 85b10675146566f6912ef16690c6318faf3678b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 29 Nov 2021 20:13:23 +0100 Subject: [PATCH 09/24] input: kitty: merge handling of plain-text and composed characters MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All plain-text and composed characters are now printed as-is, in a single place. Also fix handling of “generic” keys when emitted as escapes; don’t use the raw XKB symbol as key in the escape, convert it to a unicode code point first. For many symbols, these are the same. But not all. For now, we fallback to using the symbol as is if XKB fails to convert it to a codepoint. Not sure if we should simply drop the key press instead. Composed characters also need special treatment; we can’t use the symbol as is, since it typically refers to the last key pressed (i.e. not the composed character). And, that key is also (usually) a special “dead” key, which cannot be converted to a unicode codepoint. So, what we do is convert the generated utf8 string, and (try to) convert it to a wchar. If it succeeds, use that. If not, fallback to using the XKB symbol (as above). --- input.c | 185 ++++++++++++++++++++++++++++---------------------------- 1 file changed, 92 insertions(+), 93 deletions(-) diff --git a/input.c b/input.c index bfa0bbd5..fc7a1205 100644 --- a/input.c +++ b/input.c @@ -1146,6 +1146,7 @@ kitty_kbd_protocol(struct seat *seat, struct terminal *term, const bool repeating = seat->kbd.repeat.dont_re_repeat; const bool pressed = ctx->key_state == WL_KEYBOARD_KEY_STATE_PRESSED && !repeating; const bool released = ctx->key_state == WL_KEYBOARD_KEY_STATE_RELEASED; + const bool composed = ctx->compose_status == XKB_COMPOSE_COMPOSED; const enum kitty_kbd_flags flags = term->grid->kitty_kbd.flags[term->grid->kitty_kbd.idx]; const bool disambiguate = flags & KITTY_KBD_DISAMBIGUATE; @@ -1154,6 +1155,9 @@ kitty_kbd_protocol(struct seat *seat, struct terminal *term, if (!report_events && released) return false; + if (composed && released) + return false; + /* TODO: should we even bother with this, or just say it’s not supported? */ if (!disambiguate && pressed) return legacy_kbd_protocol(seat, term, ctx); @@ -1178,21 +1182,10 @@ kitty_kbd_protocol(struct seat *seat, struct terminal *term, } } - if (ctx->compose_status == XKB_COMPOSE_COMPOSED && !released) { - term_to_slave(term, utf8, count); - return true; - } - - /* - * Printables without any modifiers are printed as is. - * - * TODO: plain text keys (a-z, 0-9 etc) are still printed as text, - * even when NumLock is active, despite NumLock being a - * significant modifier, *and* despite NumLock affecting other - * keys, like Return and Backspace; figure out if there’s some - * better magic than filtering out Caps- and Num-Lock here.. - */ - if (iswprint(utf32) && (effective & ~caps_num) == 0 && !released) { + /* Plain-text without modifiers, or commposed text, is emitted as-is */ + if (((iswprint(utf32) && (effective & ~caps_num) == 0) || composed) + && !released) + { term_to_slave(term, utf8, count); return true; } @@ -1314,90 +1307,96 @@ kitty_kbd_protocol(struct seat *seat, struct terminal *term, case XKB_KEY_XF86AudioRaiseVolume: key = 57439; final = 'u'; break; case XKB_KEY_XF86AudioMute: key = 57440; final = 'u'; break; -#if 0 /* TODO: enable when “Report all keys as escape codes” is enabled */ - case XKB_KEY_Caps_Lock: key = 57358; final = 'u'; break; - case XKB_KEY_Num_Lock: key = 57360; final = 'u'; break; + case XKB_KEY_Caps_Lock: if (false) {key = 57358; final = 'u';} break; + case XKB_KEY_Num_Lock: if (false) {key = 57360; final = 'u';} break; - case XKB_KEY_Shift_L: key = 57441; final = 'u'; break; - case XKB_KEY_Control_L: key = 57442; final = 'u'; break; - case XKB_KEY_Alt_L: key = 57443; final = 'u'; break; - case XKB_KEY_Super_L: key = 57444; final = 'u'; break; - case XKB_KEY_Hyper_L: key = 57445; final = 'u'; break; - case XKB_KEY_Meta_L: key = 57446; final = 'u'; break; - case XKB_KEY_Shift_R: key = 57447; final = 'u'; break; - case XKB_KEY_Control_R: key = 57448; final = 'u'; break; - case XKB_KEY_Alt_R: key = 57449; final = 'u'; break; - case XKB_KEY_Super_R: key = 57450; final = 'u'; break; - case XKB_KEY_Hyper_R: key = 57451; final = 'u'; break; - case XKB_KEY_Meta_R: key = 57452; final = 'u'; break; -#endif + case XKB_KEY_Shift_L: if (false) {key = 57441; final = 'u';} break; + case XKB_KEY_Control_L: if (false) {key = 57442; final = 'u';} break; + case XKB_KEY_Alt_L: if (false) {key = 57443; final = 'u';} break; + case XKB_KEY_Super_L: if (false) {key = 57444; final = 'u';} break; + case XKB_KEY_Hyper_L: if (false) {key = 57445; final = 'u';} break; + case XKB_KEY_Meta_L: if (false) {key = 57446; final = 'u';} break; + case XKB_KEY_Shift_R: if (false) {key = 57447; final = 'u';} break; + case XKB_KEY_Control_R: if (false) {key = 57448; final = 'u';} break; + case XKB_KEY_Alt_R: if (false) {key = 57449; final = 'u';} break; + case XKB_KEY_Super_R: if (false) {key = 57450; final = 'u';} break; + case XKB_KEY_Hyper_R: if (false) {key = 57451; final = 'u';} break; + case XKB_KEY_Meta_R: if (false) {key = 57452; final = 'u';} break; - default: - if (count > 0) { - if (effective == 0 && !released) { - term_to_slave(term, utf8, count); - return true; + default: { + /* + * Use keysym (typically its Unicode codepoint value). + * + * If the keysym is shifted, use its unshifted codepoint + * instead. In other words, ctrl+a and ctrl+shift+a should + * both use the same value for ‘key’ (97 - i.a. ‘a’). + * + * However, if a non-significant modifier was used to + * generate the symbol. This is needed since we cannot + * encode non-significant modifiers, and thus the “extra” + * modifier(s) would get lost. + * + * Example: + * + * the Swedish layout has ‘2’, QUOTATION MARK (“double + * quote”), ‘@’, and ‘²’ on the same key. ‘2’ is the base + * symbol. + * + * Shift+2 results in QUOTATION MARK + * AltGr+2 results in ‘@’ + * AltGr+Shift+2 results in ‘²’ + * + * The kitty kbd protocol can’t encode AltGr. So, if we + * always used the base symbol (‘2’), Alt+Shift+2 would + * result in the same escape sequence as + * AltGr+Alt+Shift+2. + * + * (yes, this matches what kitty does, as of 0.23.1) + */ + + /* Get the key’s shift level */ + xkb_level_index_t lvl = xkb_state_key_get_level( + seat->kbd.xkb_state, ctx->key, ctx->layout); + + /* And get all modifier combinations that, combined with + * the pressed key, results in the current shift level */ + xkb_mod_mask_t masks[32]; + size_t mask_count = xkb_keymap_key_get_mods_for_level( + seat->kbd.xkb_keymap, ctx->key, ctx->layout, lvl, + masks, ALEN(masks)); + + /* Check modifier combinations - if a combination has + * modifiers not in our set of ‘significant’ modifiers, + * use key sym as-is */ + bool use_level0_sym = true; + for (size_t i = 0; i < mask_count; i++) { + if ((masks[i] & ~seat->kbd.kitty_significant) > 0) { + use_level0_sym = false; + break; } - - /* - * Use keysym (typically its Unicode codepoint value). - * - * If the keysym is shifted, use its unshifted codepoint - * instead. In other words, ctrl+a and ctrl+shift+a should - * both use the same value for ‘key’ (97 - i.a. ‘a’). - * - * However, if a non-significant modifier was used to - * generate the symbol. This is needed since we cannot - * encode non-significant modifiers, and thus the “extra” - * modifier(s) would get lost. - * - * Example: - * - * the Swedish layout has ‘2’, QUOTATION MARK (“double - * quote”), ‘@’, and ‘²’ on the same key. ‘2’ is the base - * symbol. - * - * Shift+2 results in QUOTATION MARK - * AltGr+2 results in ‘@’ - * AltGr+Shift+2 results in ‘²’ - * - * The kitty kbd protocol can’t encode AltGr. So, if we - * always used the base symbol (‘2’), Alt+Shift+2 would - * result in the same escape sequence as - * AltGr+Alt+Shift+2. - * - * (yes, this matches what kitty does, as of 0.23.1) - */ - - /* Get the key’s shift level */ - xkb_level_index_t lvl = xkb_state_key_get_level( - seat->kbd.xkb_state, ctx->key, ctx->layout); - - /* And get all modifier combinations that, combined with - * the pressed key, results in the current shift level */ - xkb_mod_mask_t masks[32]; - size_t mask_count = xkb_keymap_key_get_mods_for_level( - seat->kbd.xkb_keymap, ctx->key, ctx->layout, lvl, - masks, ALEN(masks)); - - /* Check modifier combinations - if a combination has - * modifiers not in our set of ‘significant’ modifiers, - * use key sym as-is */ - bool use_level0_sym = true; - for (size_t i = 0; i < mask_count; i++) { - if ((masks[i] & ~seat->kbd.kitty_significant) > 0) { - use_level0_sym = false; - break; - } - } - - key = use_level0_sym && ctx->level0_syms.count > 0 - ? ctx->level0_syms.syms[0] - : sym; - final = 'u'; } + + xkb_keysym_t sym_to_use = use_level0_sym && ctx->level0_syms.count > 0 + ? ctx->level0_syms.syms[0] + : sym; + + if (composed) { + wchar_t wc; + if (mbtowc(&wc, (const char *)utf8, count) == count) { + xassert(false); + key = wc; + } + } + + if (key < 0) { + key = xkb_keysym_to_utf32(sym_to_use); + if (key == 0) + key = sym_to_use; + } + final = 'u'; break; } + } xassert(encoded_mods >= 1); From 46cd5dfafc4c1d9265b149eeb55eaee3a92bee60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 2 Dec 2021 20:41:45 +0100 Subject: [PATCH 10/24] =?UTF-8?q?changelog:=20move=20entry=20to=20?= =?UTF-8?q?=E2=80=98unreleased=E2=80=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e9da6281..2a04c6a8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -37,6 +37,12 @@ ## Unreleased ### Added + +* Kitty keyboard protocol: + - [Report event types](https://sw.kovidgoyal.net/kitty/keyboard-protocol/#report-events) + (mode `0b10`) + + ### Changed ### Deprecated ### Removed @@ -53,7 +59,6 @@ * Initial support for the [Kitty keyboard protocol](https://sw.kovidgoyal.net/kitty/keyboard-protocol/). Modes supported: - [Disambiguate escape codes](https://sw.kovidgoyal.net/kitty/keyboard-protocol/#disambiguate) (mode `0b1`) - - [Report event types](https://sw.kovidgoyal.net/kitty/keyboard-protocol/#report-events) (mode `0b10`) * “Window menu” (compositor provided) on right clicks on the CSD title bar. From 8b260568fbf9d1e420b7bdacd971ba0f82a96fd4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 4 Dec 2021 18:41:36 +0100 Subject: [PATCH 11/24] term: ensure cell dimensions are non-zero Closes #830 --- CHANGELOG.md | 5 +++++ terminal.c | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2a04c6a8..41e069d9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -47,6 +47,11 @@ ### Deprecated ### Removed ### Fixed + +* Crash when bitmap fonts are scaled down to very small font sizes + (https://codeberg.org/dnkl/foot/issues/830). + + ### Security ### Contributors diff --git a/terminal.c b/terminal.c index 25310d36..ac887412 100644 --- a/terminal.c +++ b/terminal.c @@ -688,6 +688,11 @@ term_set_fonts(struct terminal *term, struct fcft_font *fonts[static 4]) : max(term->fonts[0]->height, term->fonts[0]->ascent + term->fonts[0]->descent); + if (term->cell_width <= 0) + term->cell_width = 1; + if (term->cell_height <= 0) + term->cell_height = 1; + term->font_x_ofs = term_pt_or_px_as_pixels(term, &conf->horizontal_letter_offset); term->font_y_ofs = term_pt_or_px_as_pixels(term, &conf->vertical_letter_offset); From 4e34fb8fb7006a3d5d9d458e220fffef47c97de6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 29 Nov 2021 21:03:33 +0100 Subject: [PATCH 12/24] =?UTF-8?q?input:=20kitty:=20add=20support=20for=20?= =?UTF-8?q?=E2=80=9Creport=20all=20keys=20as=20escape=20codes=E2=80=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 2 ++ input.c | 80 ++++++++++++++++++++++++++++++++++++---------------- terminal.h | 4 ++- 3 files changed, 60 insertions(+), 26 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 41e069d9..9f28071d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -41,6 +41,8 @@ * Kitty keyboard protocol: - [Report event types](https://sw.kovidgoyal.net/kitty/keyboard-protocol/#report-events) (mode `0b10`) + - [Report all keys as escape codes](https://sw.kovidgoyal.net/kitty/keyboard-protocol/#report-all-keys) + (mode `0b1000`) ### Changed diff --git a/input.c b/input.c index fc7a1205..6e237c44 100644 --- a/input.c +++ b/input.c @@ -1028,6 +1028,8 @@ legacy_kbd_protocol(struct seat *seat, struct terminal *term, { if (ctx->key_state != WL_KEYBOARD_KEY_STATE_PRESSED) return false; + if (ctx->compose_status == XKB_COMPOSE_COMPOSING) + return false; enum modifier keymap_mods = MOD_NONE; keymap_mods |= seat->kbd.shift ? MOD_SHIFT : MOD_NONE; @@ -1146,11 +1148,13 @@ kitty_kbd_protocol(struct seat *seat, struct terminal *term, const bool repeating = seat->kbd.repeat.dont_re_repeat; const bool pressed = ctx->key_state == WL_KEYBOARD_KEY_STATE_PRESSED && !repeating; const bool released = ctx->key_state == WL_KEYBOARD_KEY_STATE_RELEASED; + const bool composing = ctx->compose_status == XKB_COMPOSE_COMPOSING; const bool composed = ctx->compose_status == XKB_COMPOSE_COMPOSED; const enum kitty_kbd_flags flags = term->grid->kitty_kbd.flags[term->grid->kitty_kbd.idx]; const bool disambiguate = flags & KITTY_KBD_DISAMBIGUATE; const bool report_events = flags & KITTY_KBD_REPORT_EVENT; + const bool report_all_as_escapes = flags & KITTY_KBD_REPORT_ALL; if (!report_events && released) return false; @@ -1159,7 +1163,7 @@ kitty_kbd_protocol(struct seat *seat, struct terminal *term, return false; /* TODO: should we even bother with this, or just say it’s not supported? */ - if (!disambiguate && pressed) + if (!disambiguate && !report_all_as_escapes && pressed) return legacy_kbd_protocol(seat, term, ctx); const xkb_mod_mask_t mods = ctx->mods & seat->kbd.kitty_significant; @@ -1174,6 +1178,34 @@ kitty_kbd_protocol(struct seat *seat, struct terminal *term, const uint8_t *const utf8 = ctx->utf8.buf; const size_t count = ctx->utf8.count; + if (composing) { + /* We never emit anything while composing, *except* modifiers + * (and only in report-all-keys-as-escape-codes mode) */ + switch (sym) { + case XKB_KEY_Caps_Lock: + case XKB_KEY_Num_Lock: + case XKB_KEY_Shift_L: + case XKB_KEY_Control_L: + case XKB_KEY_Alt_L: + case XKB_KEY_Super_L: + case XKB_KEY_Hyper_L: + case XKB_KEY_Meta_L: + case XKB_KEY_Shift_R: + case XKB_KEY_Control_R: + case XKB_KEY_Alt_R: + case XKB_KEY_Super_R: + case XKB_KEY_Hyper_R: + case XKB_KEY_Meta_R: + goto emit_escapes; + + default: + return false; + } + } + + if (report_all_as_escapes) + goto emit_escapes; + if (effective == 0) { switch (sym) { case XKB_KEY_Return: term_to_slave(term, "\r", 1); return true; @@ -1190,6 +1222,8 @@ kitty_kbd_protocol(struct seat *seat, struct terminal *term, return true; } +emit_escapes: + ; unsigned int encoded_mods = 0; if (seat->kbd.mod_shift != XKB_MOD_INVALID) encoded_mods |= mods & (1 << seat->kbd.mod_shift) ? (1 << 0) : 0; @@ -1307,21 +1341,21 @@ kitty_kbd_protocol(struct seat *seat, struct terminal *term, case XKB_KEY_XF86AudioRaiseVolume: key = 57439; final = 'u'; break; case XKB_KEY_XF86AudioMute: key = 57440; final = 'u'; break; - case XKB_KEY_Caps_Lock: if (false) {key = 57358; final = 'u';} break; - case XKB_KEY_Num_Lock: if (false) {key = 57360; final = 'u';} break; + case XKB_KEY_Caps_Lock: if (report_all_as_escapes) {key = 57358; final = 'u';} break; + case XKB_KEY_Num_Lock: if (report_all_as_escapes) {key = 57360; final = 'u';} break; - case XKB_KEY_Shift_L: if (false) {key = 57441; final = 'u';} break; - case XKB_KEY_Control_L: if (false) {key = 57442; final = 'u';} break; - case XKB_KEY_Alt_L: if (false) {key = 57443; final = 'u';} break; - case XKB_KEY_Super_L: if (false) {key = 57444; final = 'u';} break; - case XKB_KEY_Hyper_L: if (false) {key = 57445; final = 'u';} break; - case XKB_KEY_Meta_L: if (false) {key = 57446; final = 'u';} break; - case XKB_KEY_Shift_R: if (false) {key = 57447; final = 'u';} break; - case XKB_KEY_Control_R: if (false) {key = 57448; final = 'u';} break; - case XKB_KEY_Alt_R: if (false) {key = 57449; final = 'u';} break; - case XKB_KEY_Super_R: if (false) {key = 57450; final = 'u';} break; - case XKB_KEY_Hyper_R: if (false) {key = 57451; final = 'u';} break; - case XKB_KEY_Meta_R: if (false) {key = 57452; final = 'u';} break; + case XKB_KEY_Shift_L: if (report_all_as_escapes) {key = 57441; final = 'u';} break; + case XKB_KEY_Control_L: if (report_all_as_escapes) {key = 57442; final = 'u';} break; + case XKB_KEY_Alt_L: if (report_all_as_escapes) {key = 57443; final = 'u';} break; + case XKB_KEY_Super_L: if (report_all_as_escapes) {key = 57444; final = 'u';} break; + case XKB_KEY_Hyper_L: if (report_all_as_escapes) {key = 57445; final = 'u';} break; + case XKB_KEY_Meta_L: if (report_all_as_escapes) {key = 57446; final = 'u';} break; + case XKB_KEY_Shift_R: if (report_all_as_escapes) {key = 57447; final = 'u';} break; + case XKB_KEY_Control_R: if (report_all_as_escapes) {key = 57448; final = 'u';} break; + case XKB_KEY_Alt_R: if (report_all_as_escapes) {key = 57449; final = 'u';} break; + case XKB_KEY_Super_R: if (report_all_as_escapes) {key = 57450; final = 'u';} break; + case XKB_KEY_Hyper_R: if (report_all_as_escapes) {key = 57451; final = 'u';} break; + case XKB_KEY_Meta_R: if (report_all_as_escapes) {key = 57452; final = 'u';} break; default: { /* @@ -1382,10 +1416,8 @@ kitty_kbd_protocol(struct seat *seat, struct terminal *term, if (composed) { wchar_t wc; - if (mbtowc(&wc, (const char *)utf8, count) == count) { - xassert(false); + if (mbtowc(&wc, (const char *)utf8, count) == count) key = wc; - } } if (key < 0) { @@ -1481,8 +1513,7 @@ key_press_release(struct seat *seat, struct terminal *term, uint32_t serial, seat->kbd.xkb_compose_state); } - if (compose_status == XKB_COMPOSE_COMPOSING) - goto maybe_repeat; + const bool composed = compose_status == XKB_COMPOSE_COMPOSED; xkb_mod_mask_t mods, consumed; get_current_modifiers(seat, &mods, &consumed, key); @@ -1585,13 +1616,12 @@ key_press_release(struct seat *seat, struct terminal *term, uint32_t serial, * Compose, and maybe emit "normal" character */ - xassert(seat->kbd.xkb_compose_state != NULL || - compose_status != XKB_COMPOSE_COMPOSED); + xassert(seat->kbd.xkb_compose_state != NULL || !composed); if (compose_status == XKB_COMPOSE_CANCELLED) goto maybe_repeat; - int count = compose_status == XKB_COMPOSE_COMPOSED + int count = composed ? xkb_compose_state_get_utf8(seat->kbd.xkb_compose_state, NULL, 0) : xkb_state_key_get_utf8(seat->kbd.xkb_state, key, NULL, 0); @@ -1601,7 +1631,7 @@ key_press_release(struct seat *seat, struct terminal *term, uint32_t serial, uint8_t *utf8 = count < sizeof(buf) ? buf : xmalloc(count + 1); uint32_t utf32 = (uint32_t)-1; - if (compose_status == XKB_COMPOSE_COMPOSED) { + if (composed) { xkb_compose_state_get_utf8( seat->kbd.xkb_compose_state, (char *)utf8, count + 1); } else { @@ -1633,7 +1663,7 @@ key_press_release(struct seat *seat, struct terminal *term, uint32_t serial, ? kitty_kbd_protocol(seat, term, &ctx) : legacy_kbd_protocol(seat, term, &ctx); - if (seat->kbd.xkb_compose_state != NULL && released) + if (composed && released) xkb_compose_state_reset(seat->kbd.xkb_compose_state); if (utf8 != buf) diff --git a/terminal.h b/terminal.h index 6b7b01f8..15d263d5 100644 --- a/terminal.h +++ b/terminal.h @@ -133,7 +133,9 @@ enum kitty_kbd_flags { KITTY_KBD_REPORT_ALTERNATE = 0x04, KITTY_KBD_REPORT_ALL = 0x08, KITTY_KBD_REPORT_ASSOCIATED = 0x10, - KITTY_KBD_SUPPORTED = KITTY_KBD_DISAMBIGUATE | KITTY_KBD_REPORT_EVENT, + KITTY_KBD_SUPPORTED = (KITTY_KBD_DISAMBIGUATE | + KITTY_KBD_REPORT_EVENT | + KITTY_KBD_REPORT_ALL), }; struct grid { From 9649842eca3b84e0dc9c232e86f6630173cc49fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 8 Dec 2021 17:41:29 +0100 Subject: [PATCH 13/24] grid: reload pointers into the uri range vector after inserting Inserting elements into the URI range vector typically triggers a vector resize. This is done using realloc(). Sometimes this causes the vector to move, thus invalidating all existing pointers into the vector. --- CHANGELOG.md | 1 + grid.c | 42 ++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9f28071d..91e2d21f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -52,6 +52,7 @@ * Crash when bitmap fonts are scaled down to very small font sizes (https://codeberg.org/dnkl/foot/issues/830). +* Crash when overwriting/erasing an OSC-8 URL. ### Security diff --git a/grid.c b/grid.c index d6ddafad..17158c3b 100644 --- a/grid.c +++ b/grid.c @@ -69,10 +69,10 @@ verify_uris_are_sorted(const struct row_data *extra) } static void -uri_range_ensure_size(struct row_data *extra, size_t count_to_add) +uri_range_ensure_size(struct row_data *extra, uint32_t count_to_add) { if (extra->uri_ranges.count + count_to_add > extra->uri_ranges.size) { - extra->uri_ranges.size += count_to_add + count_to_add; + extra->uri_ranges.size = extra->uri_ranges.count + count_to_add; extra->uri_ranges.v = xrealloc( extra->uri_ranges.v, extra->uri_ranges.size * sizeof(extra->uri_ranges.v[0])); @@ -81,6 +81,10 @@ uri_range_ensure_size(struct row_data *extra, size_t count_to_add) xassert(extra->uri_ranges.count + count_to_add <= extra->uri_ranges.size); } +/* + * Be careful! This function may xrealloc() the URI range vector, thus + * invalidating pointers into it. + */ static void uri_range_insert(struct row_data *extra, size_t idx, int start, int end, uint64_t id, const char *uri) @@ -1028,6 +1032,9 @@ grid_row_uri_range_put(struct row *row, int col, const char *uri, uint64_t id) uri_range_insert(extra, i + 1, col + 1, r->end, r->id, r->uri); + /* The insertion may xrealloc() the vector, making our + * ‘old’ pointer invalid */ + r = &extra->uri_ranges.v[i]; r->end = col - 1; xassert(r->start <= r->end); @@ -1161,6 +1168,10 @@ grid_row_uri_range_erase(struct row *row, int start, int end) /* Erase range erases a part in the middle of the URI */ uri_range_insert( extra, i + 1, end + 1, old->end, old->id, old->uri); + + /* The insertion may xrealloc() the vector, making our + * ‘old’ pointer invalid */ + old = &extra->uri_ranges.v[i]; old->end = start - 1; return; /* There can be no more URIs affected by the erase range */ } @@ -1231,6 +1242,33 @@ UNITTEST verify_no_overlapping_uris(&row_data); verify_uris_are_sorted(&row_data); + grid_row_uri_range_destroy(&row_data.uri_ranges.v[0]); + grid_row_uri_range_destroy(&row_data.uri_ranges.v[1]); + row_data.uri_ranges.count = 0; + + /* + * Regression test: erasing the middle part of an URI causes us to + * insert a new URI (we split the partly erased URI into two). + * + * The insertion logic typically triggers an xrealloc(), which, in + * some cases, *moves* the entire URI vector to a new base + * address. grid_row_uri_range_erase() did not account for this, + * and tried to update the ‘end’ member in the URI range we just + * split. This causes foot to crash when the xrealloc() has moved + * the URI range vector. + * + * (note: we’re only verifying we don’t crash here, hence the lack + * of assertions). + */ + free(row_data.uri_ranges.v); + row_data.uri_ranges.v = NULL; + row_data.uri_ranges.size = 0; + uri_range_append(&row_data, 1, 10, 0, "dummy"); + xassert(row_data.uri_ranges.size == 1); + + grid_row_uri_range_erase(&row, 5, 7); + xassert(row_data.uri_ranges.count == 2); + for (size_t i = 0; i < row_data.uri_ranges.count; i++) grid_row_uri_range_destroy(&row_data.uri_ranges.v[i]); free(row_data.uri_ranges.v); From 7a5386d883c8b7cb4f23c13da948bd57c1eb3bf4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 6 Dec 2021 23:01:41 +0100 Subject: [PATCH 14/24] =?UTF-8?q?kitty:=20implement=20=E2=80=9Creport=20as?= =?UTF-8?q?sociated=20text=E2=80=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In this mode, key events that generate text now add a third CSI parameter, indicating the actual codepoint. Remember that we always use the *unshifted* key in the CSI escapes. With this mode, those CSI escapes now also included the text codepoint. I.e. what would have been emitted, had we not generated a CSI escape. As far as I can tell, this mode has no effect unless “report all keys as escape sequences” is enabled (reason being, without that, there aren’t any text events that generate CSIs - they’re always emitted as-is). Note that Kitty itself seems to be somewhat buggy in this mode. At least on Wayland, with my Swedish layout. For example ‘a’ and ‘A’ does generate the expected CSIs, but ‘å’ and ‘Å’ appears to be treated as non-text input. Furthermore, Kitty optimizes away the modifier parameter, if no modifiers are pressed (e.g. CSI 97;;97u), while we always emit the modifier (CSI 97;1;97u). Related to #319 --- CHANGELOG.md | 2 ++ input.c | 53 +++++++++++++++++++++++++++++++++++----------------- terminal.h | 3 ++- 3 files changed, 40 insertions(+), 18 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 91e2d21f..6e110a03 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -43,6 +43,8 @@ (mode `0b10`) - [Report all keys as escape codes](https://sw.kovidgoyal.net/kitty/keyboard-protocol/#report-all-keys) (mode `0b1000`) + - [Report associated text](https://sw.kovidgoyal.net/kitty/keyboard-protocol/#report-text) + (mode `0b10000`) ### Changed diff --git a/input.c b/input.c index 6e237c44..3f7a9e57 100644 --- a/input.c +++ b/input.c @@ -1151,10 +1151,18 @@ kitty_kbd_protocol(struct seat *seat, struct terminal *term, const bool composing = ctx->compose_status == XKB_COMPOSE_COMPOSING; const bool composed = ctx->compose_status == XKB_COMPOSE_COMPOSED; + const xkb_keysym_t sym = ctx->sym; + const uint32_t utf32 = ctx->utf32; + const uint8_t *const utf8 = ctx->utf8.buf; + const bool is_text = iswprint(utf32); + const size_t count = ctx->utf8.count; + const enum kitty_kbd_flags flags = term->grid->kitty_kbd.flags[term->grid->kitty_kbd.idx]; const bool disambiguate = flags & KITTY_KBD_DISAMBIGUATE; const bool report_events = flags & KITTY_KBD_REPORT_EVENT; const bool report_all_as_escapes = flags & KITTY_KBD_REPORT_ALL; + const bool report_associated_text = + (flags & KITTY_KBD_REPORT_ASSOCIATED) && is_text && !released; if (!report_events && released) return false; @@ -1173,10 +1181,6 @@ kitty_kbd_protocol(struct seat *seat, struct terminal *term, const xkb_mod_mask_t caps_num = (seat->kbd.mod_caps != XKB_MOD_INVALID ? 1 << seat->kbd.mod_caps : 0) | (seat->kbd.mod_num != XKB_MOD_INVALID ? 1 << seat->kbd.mod_num : 0); - const xkb_keysym_t sym = ctx->sym; - const uint32_t utf32 = ctx->utf32; - const uint8_t *const utf8 = ctx->utf8.buf; - const size_t count = ctx->utf8.count; if (composing) { /* We never emit anything while composing, *except* modifiers @@ -1215,7 +1219,7 @@ kitty_kbd_protocol(struct seat *seat, struct terminal *term, } /* Plain-text without modifiers, or commposed text, is emitted as-is */ - if (((iswprint(utf32) && (effective & ~caps_num) == 0) || composed) + if (((is_text && (effective & ~caps_num) == 0) || composed) && !released) { term_to_slave(term, utf8, count); @@ -1425,6 +1429,7 @@ emit_escapes: if (key == 0) key = sym_to_use; } + final = 'u'; break; } @@ -1442,26 +1447,40 @@ emit_escapes: } else event[0] = '\0'; - char buf[16]; - int bytes; + char buf[64], *p = buf; + size_t left = sizeof(buf); + size_t bytes; if (key < 0) return false; if (final == 'u' || final == '~') { - if (encoded_mods > 1 || event[0] != '\0') - bytes = snprintf(buf, sizeof(buf), "\x1b[%u;%u%s%c", - key, encoded_mods, event, final); - else - bytes = snprintf(buf, sizeof(buf), "\x1b[%u%c", key, final); + bytes = snprintf(p, left, "\x1b[%u", key); + p += bytes; left -= bytes; + + if (encoded_mods > 1 || event[0] != '\0' || report_associated_text) { + bytes = snprintf(p, left, ";%u%s", encoded_mods, event); + p += bytes; left -= bytes; + + if (report_associated_text) { + bytes = snprintf(p, left, ";%u", utf32); + p += bytes; left -= bytes; + } + } + + bytes = snprintf(p, left, "%c", final); + p += bytes; left -= bytes; } else { - if (encoded_mods > 1 || event[0] != '\0') - bytes = snprintf(buf, sizeof(buf), "\x1b[1;%u%s%c", encoded_mods, event, final); - else - bytes = snprintf(buf, sizeof(buf), "\x1b[%c", final); + if (encoded_mods > 1 || event[0] != '\0') { + bytes = snprintf(p, left, "\x1b[1;%u%s%c", encoded_mods, event, final); + p += bytes; left -= bytes; + } else { + bytes = snprintf(p, left, "\x1b[%c", final); + p += bytes; left -= bytes; + } } - term_to_slave(term, buf, bytes); + term_to_slave(term, buf, sizeof(buf) - left); return true; } diff --git a/terminal.h b/terminal.h index 15d263d5..394871f6 100644 --- a/terminal.h +++ b/terminal.h @@ -135,7 +135,8 @@ enum kitty_kbd_flags { KITTY_KBD_REPORT_ASSOCIATED = 0x10, KITTY_KBD_SUPPORTED = (KITTY_KBD_DISAMBIGUATE | KITTY_KBD_REPORT_EVENT | - KITTY_KBD_REPORT_ALL), + KITTY_KBD_REPORT_ALL | + KITTY_KBD_REPORT_ASSOCIATED), }; struct grid { From 305f3122b164342b65c73a21d32c5188048e7dbe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 7 Dec 2021 07:31:34 +0100 Subject: [PATCH 15/24] =?UTF-8?q?kitty:=20composed=20characters=20with=20?= =?UTF-8?q?=E2=80=9Creport=20associated=20text=E2=80=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The generic input handler now converts the composed character to it’s UTF-32 equivalent. This means we now provide a valid UTF-32 codepoint for both composed characters, and non-composed (plain-text) characters. Use this in the kitty protocol to simplify the logic around composed characters, by simply treating them as plain text. --- input.c | 38 ++++++++++++++++++++------------------ 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/input.c b/input.c index 3f7a9e57..273b4fdb 100644 --- a/input.c +++ b/input.c @@ -1151,18 +1151,12 @@ kitty_kbd_protocol(struct seat *seat, struct terminal *term, const bool composing = ctx->compose_status == XKB_COMPOSE_COMPOSING; const bool composed = ctx->compose_status == XKB_COMPOSE_COMPOSED; - const xkb_keysym_t sym = ctx->sym; - const uint32_t utf32 = ctx->utf32; - const uint8_t *const utf8 = ctx->utf8.buf; - const bool is_text = iswprint(utf32); - const size_t count = ctx->utf8.count; + const enum kitty_kbd_flags flags = + term->grid->kitty_kbd.flags[term->grid->kitty_kbd.idx]; - const enum kitty_kbd_flags flags = term->grid->kitty_kbd.flags[term->grid->kitty_kbd.idx]; const bool disambiguate = flags & KITTY_KBD_DISAMBIGUATE; const bool report_events = flags & KITTY_KBD_REPORT_EVENT; const bool report_all_as_escapes = flags & KITTY_KBD_REPORT_ALL; - const bool report_associated_text = - (flags & KITTY_KBD_REPORT_ASSOCIATED) && is_text && !released; if (!report_events && released) return false; @@ -1182,6 +1176,16 @@ kitty_kbd_protocol(struct seat *seat, struct terminal *term, (seat->kbd.mod_caps != XKB_MOD_INVALID ? 1 << seat->kbd.mod_caps : 0) | (seat->kbd.mod_num != XKB_MOD_INVALID ? 1 << seat->kbd.mod_num : 0); + const xkb_keysym_t sym = ctx->sym; + const uint32_t utf32 = ctx->utf32; + const uint8_t *const utf8 = ctx->utf8.buf; + + const bool is_text = iswprint(utf32) && (effective & ~caps_num) == 0; + const size_t count = ctx->utf8.count; + + const bool report_associated_text = + (flags & KITTY_KBD_REPORT_ASSOCIATED) && is_text && !released; + if (composing) { /* We never emit anything while composing, *except* modifiers * (and only in report-all-keys-as-escape-codes mode) */ @@ -1219,9 +1223,7 @@ kitty_kbd_protocol(struct seat *seat, struct terminal *term, } /* Plain-text without modifiers, or commposed text, is emitted as-is */ - if (((is_text && (effective & ~caps_num) == 0) || composed) - && !released) - { + if (is_text) { term_to_slave(term, utf8, count); return true; } @@ -1418,13 +1420,9 @@ emit_escapes: ? ctx->level0_syms.syms[0] : sym; - if (composed) { - wchar_t wc; - if (mbtowc(&wc, (const char *)utf8, count) == count) - key = wc; - } - - if (key < 0) { + if (composed && is_text) + key = utf32; + else { key = xkb_keysym_to_utf32(sym_to_use); if (key == 0) key = sym_to_use; @@ -1653,6 +1651,10 @@ key_press_release(struct seat *seat, struct terminal *term, uint32_t serial, if (composed) { xkb_compose_state_get_utf8( seat->kbd.xkb_compose_state, (char *)utf8, count + 1); + + wchar_t wc; + if (mbtowc(&wc, (const char *)utf8, count) == count) + utf32 = wc; } else { xkb_state_key_get_utf8( seat->kbd.xkb_state, key, (char *)utf8, count + 1); From 40b69ab52182f75663c6fe516f75f83f576915b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 7 Dec 2021 19:11:56 +0100 Subject: [PATCH 16/24] kitty: add ISO_Level{3,5}_Shift keys --- input.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/input.c b/input.c index 273b4fdb..f0acc356 100644 --- a/input.c +++ b/input.c @@ -1204,6 +1204,8 @@ kitty_kbd_protocol(struct seat *seat, struct terminal *term, case XKB_KEY_Super_R: case XKB_KEY_Hyper_R: case XKB_KEY_Meta_R: + case XKB_KEY_ISO_Level3_Shift: + case XKB_KEY_ISO_Level5_Shift: goto emit_escapes; default: @@ -1362,6 +1364,8 @@ emit_escapes: case XKB_KEY_Super_R: if (report_all_as_escapes) {key = 57450; final = 'u';} break; case XKB_KEY_Hyper_R: if (report_all_as_escapes) {key = 57451; final = 'u';} break; case XKB_KEY_Meta_R: if (report_all_as_escapes) {key = 57452; final = 'u';} break; + case XKB_KEY_ISO_Level3_Shift: if (report_all_as_escapes) {key = 57453; final = 'u';} break; + case XKB_KEY_ISO_Level5_Shift: if (report_all_as_escapes) {key = 57454; final = 'u';} break; default: { /* From f3f3f600404d4c565e8223df61220e483e382491 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 7 Dec 2021 20:26:12 +0100 Subject: [PATCH 17/24] kitty: only emit plain text on key press- and repeat events That is, release events always generate CSIs --- input.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/input.c b/input.c index f0acc356..0242371a 100644 --- a/input.c +++ b/input.c @@ -1225,7 +1225,7 @@ kitty_kbd_protocol(struct seat *seat, struct terminal *term, } /* Plain-text without modifiers, or commposed text, is emitted as-is */ - if (is_text) { + if (is_text && !released) { term_to_slave(term, utf8, count); return true; } From 010c4001d2f83aaf1f5c86455c8e5bd4ba708cc7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 7 Dec 2021 19:55:52 +0100 Subject: [PATCH 18/24] =?UTF-8?q?kitty:=20initial=20support=20for=20?= =?UTF-8?q?=E2=80=9Creport=20alternate=20key=E2=80=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In this mode, the “shifted” and “base layout” keys are added to the CSIs, as sub-parameters to the “key” parameter. Note that this PR only implements the “shifted” key, not the “base layout key”. This is done by converting the original XKB symbol to it’s corresponding UTF-32 codepoint. If this codepoint is different from the one we use as “key” in the CSI, we add it as a sub-parameter. Related to #319 --- CHANGELOG.md | 3 +++ input.c | 10 +++++++++- terminal.h | 1 + 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6e110a03..3bec7f8c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -41,6 +41,9 @@ * Kitty keyboard protocol: - [Report event types](https://sw.kovidgoyal.net/kitty/keyboard-protocol/#report-events) (mode `0b10`) + - [Report alternate keys](https://sw.kovidgoyal.net/kitty/keyboard-protocol/#report-alternates) + (mode `0b100`, but not that only the _shifted_ key is reported, + not the _base layout key_) - [Report all keys as escape codes](https://sw.kovidgoyal.net/kitty/keyboard-protocol/#report-all-keys) (mode `0b1000`) - [Report associated text](https://sw.kovidgoyal.net/kitty/keyboard-protocol/#report-text) diff --git a/input.c b/input.c index 0242371a..77818c1a 100644 --- a/input.c +++ b/input.c @@ -1156,6 +1156,7 @@ kitty_kbd_protocol(struct seat *seat, struct terminal *term, const bool disambiguate = flags & KITTY_KBD_DISAMBIGUATE; const bool report_events = flags & KITTY_KBD_REPORT_EVENT; + const bool report_alternate = flags & KITTY_KBD_REPORT_ALTERNATE; const bool report_all_as_escapes = flags & KITTY_KBD_REPORT_ALL; if (!report_events && released) @@ -1247,7 +1248,7 @@ emit_escapes: encoded_mods |= mods & (1 << seat->kbd.mod_num) ? (1 << 7) : 0; encoded_mods++; - int key = -1; + int key = -1, alternate = -1; char final; switch (sym) { @@ -1430,6 +1431,8 @@ emit_escapes: key = xkb_keysym_to_utf32(sym_to_use); if (key == 0) key = sym_to_use; + else + alternate = xkb_keysym_to_utf32(sym); } final = 'u'; @@ -1460,6 +1463,11 @@ emit_escapes: bytes = snprintf(p, left, "\x1b[%u", key); p += bytes; left -= bytes; + if (report_alternate && alternate > 0 && alternate != key) { + bytes = snprintf(p, left, ":%u", alternate); + p += bytes; left -= bytes; + } + if (encoded_mods > 1 || event[0] != '\0' || report_associated_text) { bytes = snprintf(p, left, ";%u%s", encoded_mods, event); p += bytes; left -= bytes; diff --git a/terminal.h b/terminal.h index 394871f6..09b04614 100644 --- a/terminal.h +++ b/terminal.h @@ -135,6 +135,7 @@ enum kitty_kbd_flags { KITTY_KBD_REPORT_ASSOCIATED = 0x10, KITTY_KBD_SUPPORTED = (KITTY_KBD_DISAMBIGUATE | KITTY_KBD_REPORT_EVENT | + KITTY_KBD_REPORT_ALTERNATE | KITTY_KBD_REPORT_ALL | KITTY_KBD_REPORT_ASSOCIATED), }; From 7e9ca65f4d84a9578e604af5632c19cd9cc16101 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 7 Dec 2021 20:25:14 +0100 Subject: [PATCH 19/24] =?UTF-8?q?kitty:=20implement=20=E2=80=9Cbase=20layo?= =?UTF-8?q?ut=20key=E2=80=9D=20in=20=E2=80=9Creport=20alternate=20key?= =?UTF-8?q?=E2=80=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- input.c | 36 +++++++++++++++++++++++++++++++----- 1 file changed, 31 insertions(+), 5 deletions(-) diff --git a/input.c b/input.c index 77818c1a..7b379ed1 100644 --- a/input.c +++ b/input.c @@ -1248,7 +1248,7 @@ emit_escapes: encoded_mods |= mods & (1 << seat->kbd.mod_num) ? (1 << 7) : 0; encoded_mods++; - int key = -1, alternate = -1; + int key = -1, alternate = -1, base = -1; char final; switch (sym) { @@ -1431,8 +1431,23 @@ emit_escapes: key = xkb_keysym_to_utf32(sym_to_use); if (key == 0) key = sym_to_use; - else + + if (report_alternate) { + /* The *shifted* key. May be the same as the unshifted + * key - if so, this is filtered out below, when + * emitting the CSI */ alternate = xkb_keysym_to_utf32(sym); + + /* Base layout key. I.e the symbol the pressed key + * produces in the base/default layout (layout idx + * 0) */ + const xkb_keysym_t *base_syms; + int base_sym_count = xkb_keymap_key_get_syms_by_level( + seat->kbd.xkb_keymap, ctx->key, 0, 0, &base_syms); + + if (base_sym_count > 0) + base = xkb_keysym_to_utf32(base_syms[0]); + } } final = 'u'; @@ -1463,9 +1478,20 @@ emit_escapes: bytes = snprintf(p, left, "\x1b[%u", key); p += bytes; left -= bytes; - if (report_alternate && alternate > 0 && alternate != key) { - bytes = snprintf(p, left, ":%u", alternate); - p += bytes; left -= bytes; + if (report_alternate) { + bool emit_alternate = alternate > 0 && alternate != key; + bool emit_base = base > 0 && base != key && base != alternate; + + if (emit_alternate) { + bytes = snprintf(p, left, ":%u", alternate); + p += bytes; left -= bytes; + } + + if (emit_base) { + bytes = snprintf( + p, left, "%s:%u", !emit_alternate ? ":" : "", base); + p += bytes; left -= bytes; + } } if (encoded_mods > 1 || event[0] != '\0' || report_associated_text) { From db95a90e57a8dfd60856599df7cb26a8d38fb4aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 7 Dec 2021 21:48:42 +0100 Subject: [PATCH 20/24] kitty: report-alternate: apply base-layout key to composed characters --- input.c | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/input.c b/input.c index 7b379ed1..661e881e 100644 --- a/input.c +++ b/input.c @@ -1432,22 +1432,24 @@ emit_escapes: if (key == 0) key = sym_to_use; - if (report_alternate) { - /* The *shifted* key. May be the same as the unshifted - * key - if so, this is filtered out below, when - * emitting the CSI */ + if (report_alternate) alternate = xkb_keysym_to_utf32(sym); + } - /* Base layout key. I.e the symbol the pressed key - * produces in the base/default layout (layout idx - * 0) */ - const xkb_keysym_t *base_syms; - int base_sym_count = xkb_keymap_key_get_syms_by_level( - seat->kbd.xkb_keymap, ctx->key, 0, 0, &base_syms); + if (report_alternate) { + /* The *shifted* key. May be the same as the unshifted + * key - if so, this is filtered out below, when + * emitting the CSI */ - if (base_sym_count > 0) - base = xkb_keysym_to_utf32(base_syms[0]); - } + /* Base layout key. I.e the symbol the pressed key + * produces in the base/default layout (layout idx + * 0) */ + const xkb_keysym_t *base_syms; + int base_sym_count = xkb_keymap_key_get_syms_by_level( + seat->kbd.xkb_keymap, ctx->key, 0, 0, &base_syms); + + if (base_sym_count > 0) + base = xkb_keysym_to_utf32(base_syms[0]); } final = 'u'; From 30a66996b2dc57406311bb140d0aa988bcd19488 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 7 Dec 2021 21:52:43 +0100 Subject: [PATCH 21/24] =?UTF-8?q?kitty:=20when=20emitting=20associated=20t?= =?UTF-8?q?ext,=20don=E2=80=99t=20report=20mods/events=20unless=20necessar?= =?UTF-8?q?y?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- input.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/input.c b/input.c index 661e881e..4ced612a 100644 --- a/input.c +++ b/input.c @@ -1460,7 +1460,7 @@ emit_escapes: xassert(encoded_mods >= 1); char event[4]; - if (report_events) { + if (report_events /*&& !pressed*/) { /* Note: this deviates slightly from Kitty, which omits the * “:1” subparameter for key press events */ event[0] = ':'; @@ -1496,14 +1496,16 @@ emit_escapes: } } - if (encoded_mods > 1 || event[0] != '\0' || report_associated_text) { + bool emit_mods = encoded_mods > 1 || event[0] != '\0'; + + if (emit_mods) { bytes = snprintf(p, left, ";%u%s", encoded_mods, event); p += bytes; left -= bytes; + } - if (report_associated_text) { - bytes = snprintf(p, left, ";%u", utf32); - p += bytes; left -= bytes; - } + if (report_associated_text) { + bytes = snprintf(p, left, "%s;%u", !emit_mods ? ";" : "", utf32); + p += bytes; left -= bytes; } bytes = snprintf(p, left, "%c", final); From 352077c78aaf4e51759e37d1279d3c8ce7d70600 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 7 Dec 2021 22:22:55 +0100 Subject: [PATCH 22/24] kitty: simplify: always calculate alternate/base keys But only _emit_ them if report alternate has been enabled. --- input.c | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/input.c b/input.c index 4ced612a..d2cda4aa 100644 --- a/input.c +++ b/input.c @@ -1432,26 +1432,21 @@ emit_escapes: if (key == 0) key = sym_to_use; - if (report_alternate) - alternate = xkb_keysym_to_utf32(sym); - } - - if (report_alternate) { /* The *shifted* key. May be the same as the unshifted * key - if so, this is filtered out below, when * emitting the CSI */ - - /* Base layout key. I.e the symbol the pressed key - * produces in the base/default layout (layout idx - * 0) */ - const xkb_keysym_t *base_syms; - int base_sym_count = xkb_keymap_key_get_syms_by_level( - seat->kbd.xkb_keymap, ctx->key, 0, 0, &base_syms); - - if (base_sym_count > 0) - base = xkb_keysym_to_utf32(base_syms[0]); + alternate = xkb_keysym_to_utf32(sym); } + /* Base layout key. I.e the symbol the pressed key produces in + * the base/default layout (layout idx 0) */ + const xkb_keysym_t *base_syms; + int base_sym_count = xkb_keymap_key_get_syms_by_level( + seat->kbd.xkb_keymap, ctx->key, 0, 0, &base_syms); + + if (base_sym_count > 0) + base = xkb_keysym_to_utf32(base_syms[0]); + final = 'u'; break; } From 924726ac76530e04bec5a4294b6f1b55cabd60f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 8 Dec 2021 18:08:45 +0100 Subject: [PATCH 23/24] changelog: prepare for 1.10.3 --- CHANGELOG.md | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3bec7f8c..c0b85723 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -* [Unreleased](#unreleased) +* [1.10.3](#1-10-3) * [1.10.2](#1-10-2) * [1.10.1](#1-10-1) * [1.10.0](#1-10-0) @@ -35,7 +35,8 @@ * [1.2.0](#1-2-0) -## Unreleased +## 1.10.3 + ### Added * Kitty keyboard protocol: @@ -50,9 +51,6 @@ (mode `0b10000`) -### Changed -### Deprecated -### Removed ### Fixed * Crash when bitmap fonts are scaled down to very small font sizes @@ -60,10 +58,6 @@ * Crash when overwriting/erasing an OSC-8 URL. -### Security -### Contributors - - ## 1.10.2 ### Added From a9026f16a78971328bb1d5f2c19d05e9d0eb362b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 8 Dec 2021 18:09:50 +0100 Subject: [PATCH 24/24] meson: bump version to 1.10.3 --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 4f74bed6..9faaf44b 100644 --- a/meson.build +++ b/meson.build @@ -1,5 +1,5 @@ project('foot', 'c', - version: '1.10.2', + version: '1.10.3', license: 'MIT', meson_version: '>=0.54.0', default_options: [