From 78666d248a6b5a40effd2a30cb9483cd8141bb1e 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 1/4] =?UTF-8?q?kitty:=20implement=20=E2=80=9Creport=20asso?= =?UTF-8?q?ciated=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 6b6fd944..828f51aa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -46,6 +46,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 2b878cd1..0d47faea 100644 --- a/input.c +++ b/input.c @@ -1156,10 +1156,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; @@ -1178,10 +1186,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 @@ -1220,7 +1224,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); @@ -1430,6 +1434,7 @@ emit_escapes: if (key == 0) key = sym_to_use; } + final = 'u'; break; } @@ -1447,26 +1452,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 69f97446faf338c792beb3a548591707f2205c32 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 2/4] =?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 0d47faea..51968e69 100644 --- a/input.c +++ b/input.c @@ -1156,18 +1156,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; @@ -1187,6 +1181,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) */ @@ -1224,9 +1228,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; } @@ -1423,13 +1425,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; @@ -1658,6 +1656,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 e9a762f8a12904a001cd936cb8337b1115a3c904 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 3/4] 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 51968e69..84c83ea8 100644 --- a/input.c +++ b/input.c @@ -1209,6 +1209,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: @@ -1367,6 +1369,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 21fe01099cb2ff84b1530574a228aebd614668bb 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 4/4] 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 84c83ea8..869e1c67 100644 --- a/input.c +++ b/input.c @@ -1230,7 +1230,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; }