Merge branch 'kitty-kbd-event-types'

Related to #319
This commit is contained in:
Daniel Eklöf 2021-12-04 21:59:10 +01:00
commit a62e3cdb3d
No known key found for this signature in database
GPG key ID: 5BBD4992C116573F
3 changed files with 190 additions and 144 deletions

View file

@ -41,6 +41,9 @@
* `[mouse-bindings].selection-override-modifiers` option, specifying * `[mouse-bindings].selection-override-modifiers` option, specifying
which modifiers to hold to override mouse grabs by client which modifiers to hold to override mouse grabs by client
applications and force selection instead. applications and force selection instead.
* Kitty keyboard protocol:
- [Report event types](https://sw.kovidgoyal.net/kitty/keyboard-protocol/#report-events)
(mode `0b10`)
### Changed ### Changed
@ -58,7 +61,7 @@
* New value, `max`, for `[tweak].grapheme-width-method`. * New value, `max`, for `[tweak].grapheme-width-method`.
* Initial support for the [Kitty keyboard protocol](https://sw.kovidgoyal.net/kitty/keyboard-protocol/). * Initial support for the [Kitty keyboard protocol](https://sw.kovidgoyal.net/kitty/keyboard-protocol/).
Modes supported: 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`)
* “Window menu” (compositor provided) on right clicks on the CSD title * “Window menu” (compositor provided) on right clicks on the CSD title
bar. bar.

327
input.c
View file

@ -1031,6 +1031,9 @@ static bool
legacy_kbd_protocol(struct seat *seat, struct terminal *term, legacy_kbd_protocol(struct seat *seat, struct terminal *term,
const struct kbd_ctx *ctx) const struct kbd_ctx *ctx)
{ {
if (ctx->key_state != WL_KEYBOARD_KEY_STATE_PRESSED)
return false;
enum modifier keymap_mods = MOD_NONE; enum modifier keymap_mods = MOD_NONE;
keymap_mods |= seat->kbd.shift ? MOD_SHIFT : MOD_NONE; keymap_mods |= seat->kbd.shift ? MOD_SHIFT : MOD_NONE;
keymap_mods |= seat->kbd.alt ? MOD_ALT : MOD_NONE; keymap_mods |= seat->kbd.alt ? MOD_ALT : MOD_NONE;
@ -1145,6 +1148,25 @@ static bool
kitty_kbd_protocol(struct seat *seat, struct terminal *term, kitty_kbd_protocol(struct seat *seat, struct terminal *term,
const struct kbd_ctx *ctx) 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 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;
if (!report_events && released)
return false;
if (composed && released)
return false;
/* TODO: should we even bother with this, or just say its 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 mods = ctx->mods & seat->kbd.kitty_significant;
const xkb_mod_mask_t consumed = xkb_state_key_get_consumed_mods2( 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; seat->kbd.xkb_state, ctx->key, XKB_CONSUMED_MODE_GTK) & seat->kbd.kitty_significant;
@ -1157,11 +1179,6 @@ kitty_kbd_protocol(struct seat *seat, struct terminal *term,
const uint8_t *const utf8 = ctx->utf8.buf; const uint8_t *const utf8 = ctx->utf8.buf;
const size_t count = ctx->utf8.count; 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) { if (effective == 0) {
switch (sym) { switch (sym) {
case XKB_KEY_Return: term_to_slave(term, "\r", 1); return true; case XKB_KEY_Return: term_to_slave(term, "\r", 1); return true;
@ -1170,16 +1187,10 @@ kitty_kbd_protocol(struct seat *seat, struct terminal *term,
} }
} }
/* /* Plain-text without modifiers, or commposed text, is emitted as-is */
* Printables without any modifiers are printed as is. if (((iswprint(utf32) && (effective & ~caps_num) == 0) || composed)
* && !released)
* 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 theres some
* better magic than filtering out Caps- and Num-Lock here..
*/
if (iswprint(utf32) && (effective & ~caps_num) == 0) {
term_to_slave(term, utf8, count); term_to_slave(term, utf8, count);
return true; return true;
} }
@ -1301,93 +1312,109 @@ kitty_kbd_protocol(struct seat *seat, struct terminal *term,
case XKB_KEY_XF86AudioRaiseVolume: key = 57439; final = 'u'; break; case XKB_KEY_XF86AudioRaiseVolume: key = 57439; final = 'u'; break;
case XKB_KEY_XF86AudioMute: key = 57440; 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: if (false) {key = 57358; final = 'u';} break;
case XKB_KEY_Caps_Lock: key = 57358; final = 'u'; break; case XKB_KEY_Num_Lock: if (false) {key = 57360; final = 'u';} break;
case XKB_KEY_Num_Lock: key = 57360; final = 'u'; break;
case XKB_KEY_Shift_L: key = 57441; final = 'u'; break; case XKB_KEY_Shift_L: if (false) {key = 57441; final = 'u';} break;
case XKB_KEY_Control_L: key = 57442; final = 'u'; break; case XKB_KEY_Control_L: if (false) {key = 57442; final = 'u';} break;
case XKB_KEY_Alt_L: key = 57443; final = 'u'; break; case XKB_KEY_Alt_L: if (false) {key = 57443; final = 'u';} break;
case XKB_KEY_Super_L: key = 57444; final = 'u'; break; case XKB_KEY_Super_L: if (false) {key = 57444; final = 'u';} break;
case XKB_KEY_Hyper_L: key = 57445; final = 'u'; break; case XKB_KEY_Hyper_L: if (false) {key = 57445; final = 'u';} break;
case XKB_KEY_Meta_L: key = 57446; final = 'u'; break; case XKB_KEY_Meta_L: if (false) {key = 57446; final = 'u';} break;
case XKB_KEY_Shift_R: key = 57447; final = 'u'; break; case XKB_KEY_Shift_R: if (false) {key = 57447; final = 'u';} break;
case XKB_KEY_Control_R: key = 57448; final = 'u'; break; case XKB_KEY_Control_R: if (false) {key = 57448; final = 'u';} break;
case XKB_KEY_Alt_R: key = 57449; final = 'u'; break; case XKB_KEY_Alt_R: if (false) {key = 57449; final = 'u';} break;
case XKB_KEY_Super_R: key = 57450; final = 'u'; break; case XKB_KEY_Super_R: if (false) {key = 57450; final = 'u';} break;
case XKB_KEY_Hyper_R: key = 57451; final = 'u'; break; case XKB_KEY_Hyper_R: if (false) {key = 57451; final = 'u';} break;
case XKB_KEY_Meta_R: key = 57452; final = 'u'; break; case XKB_KEY_Meta_R: if (false) {key = 57452; final = 'u';} break;
#endif
default: default: {
if (count > 0) { /*
if (effective == 0) { * Use keysym (typically its Unicode codepoint value).
term_to_slave(term, utf8, count); *
return true; * 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 cant 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 keys 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 cant 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 keys 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; break;
} }
}
xassert(encoded_mods >= 1); 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]; char buf[16];
int bytes; int bytes;
@ -1395,14 +1422,14 @@ kitty_kbd_protocol(struct seat *seat, struct terminal *term,
return false; return false;
if (final == 'u' || final == '~') { if (final == 'u' || final == '~') {
if (encoded_mods > 1) if (encoded_mods > 1 || event[0] != '\0')
bytes = snprintf(buf, sizeof(buf), "\x1b[%u;%u%c", bytes = snprintf(buf, sizeof(buf), "\x1b[%u;%u%s%c",
key, encoded_mods, final); key, encoded_mods, event, final);
else else
bytes = snprintf(buf, sizeof(buf), "\x1b[%u%c", key, final); bytes = snprintf(buf, sizeof(buf), "\x1b[%u%c", key, final);
} else { } else {
if (encoded_mods > 1) if (encoded_mods > 1 || event[0] != '\0')
bytes = snprintf(buf, sizeof(buf), "\x1b[1;%u%c", encoded_mods, final); bytes = snprintf(buf, sizeof(buf), "\x1b[1;%u%s%c", encoded_mods, event, final);
else else
bytes = snprintf(buf, sizeof(buf), "\x1b[%c", final); bytes = snprintf(buf, sizeof(buf), "\x1b[%c", final);
} }
@ -1423,15 +1450,20 @@ key_press_release(struct seat *seat, struct terminal *term, uint32_t serial,
return; return;
} }
if (state == XKB_KEY_UP) { const bool pressed = state == WL_KEYBOARD_KEY_STATE_PRESSED;
stop_repeater(seat, key); //const bool repeated = pressed && seat->kbd.repeat.dont_re_repeat;
return; 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); 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 */ /* TODO: better way to detect modifiers */
sym != XKB_KEY_Shift_L && sym != XKB_KEY_Shift_R && sym != XKB_KEY_Shift_L && sym != XKB_KEY_Shift_R &&
sym != XKB_KEY_Control_L && sym != XKB_KEY_Control_R && sym != XKB_KEY_Control_L && sym != XKB_KEY_Control_R &&
@ -1448,7 +1480,8 @@ key_press_release(struct seat *seat, struct terminal *term, uint32_t serial,
enum xkb_compose_status compose_status = XKB_COMPOSE_NOTHING; enum xkb_compose_status compose_status = XKB_COMPOSE_NOTHING;
if (seat->kbd.xkb_compose_state != NULL) { 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( compose_status = xkb_compose_state_get_status(
seat->kbd.xkb_compose_state); seat->kbd.xkb_compose_state);
} }
@ -1469,18 +1502,26 @@ key_press_release(struct seat *seat, struct terminal *term, uint32_t serial,
size_t raw_count = xkb_keymap_key_get_syms_by_level( size_t raw_count = xkb_keymap_key_get_syms_by_level(
seat->kbd.xkb_keymap, key, layout_idx, 0, &raw_syms); seat->kbd.xkb_keymap, key, layout_idx, 0, &raw_syms);
if (term->is_searching) { if (pressed) {
if (should_repeat) if (term->is_searching) {
start_repeater(seat, key); if (should_repeat)
search_input( start_repeater(seat, key);
seat, term, key, sym, bind_mods, bind_consumed, raw_syms, raw_count, serial);
return; search_input(
} else if (urls_mode_is_active(term)) { seat, term, key, sym, bind_mods, bind_consumed,
if (should_repeat) raw_syms, raw_count, serial);
start_repeater(seat, key); return;
urls_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 #if 0
@ -1504,36 +1545,38 @@ key_press_release(struct seat *seat, struct terminal *term, uint32_t serial,
/* /*
* User configurable bindings * User configurable bindings
*/ */
tll_foreach(seat->kbd.bindings.key, it) { if (pressed) {
const struct key_binding *bind = &it->item; tll_foreach(seat->kbd.bindings.key, it) {
const struct key_binding *bind = &it->item;
/* Match translated symbol */ /* Match translated symbol */
if (bind->sym == sym && if (bind->sym == sym &&
bind->mods == (bind_mods & ~bind_consumed) && bind->mods == (bind_mods & ~bind_consumed) &&
execute_binding( 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)) seat, term, bind->action, bind->pipe_argv, serial))
{ {
goto maybe_repeat; goto maybe_repeat;
} }
}
/* Match raw key code */ if (bind->mods != bind_mods)
tll_foreach(bind->key_codes, code) { continue;
if (code->item == key && execute_binding(
seat, term, bind->action, bind->pipe_argv, serial)) /* Match untranslated symbols */
{ for (size_t i = 0; i < raw_count; i++) {
goto maybe_repeat; 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;
}
} }
} }
} }
@ -1595,7 +1638,7 @@ key_press_release(struct seat *seat, struct terminal *term, uint32_t serial,
? kitty_kbd_protocol(seat, term, &ctx) ? kitty_kbd_protocol(seat, term, &ctx)
: legacy_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 && released)
xkb_compose_state_reset(seat->kbd.xkb_compose_state); xkb_compose_state_reset(seat->kbd.xkb_compose_state);
if (utf8 != buf) if (utf8 != buf)

View file

@ -133,7 +133,7 @@ enum kitty_kbd_flags {
KITTY_KBD_REPORT_ALTERNATE = 0x04, KITTY_KBD_REPORT_ALTERNATE = 0x04,
KITTY_KBD_REPORT_ALL = 0x08, KITTY_KBD_REPORT_ALL = 0x08,
KITTY_KBD_REPORT_ASSOCIATED = 0x10, KITTY_KBD_REPORT_ASSOCIATED = 0x10,
KITTY_KBD_SUPPORTED = KITTY_KBD_DISAMBIGUATE, KITTY_KBD_SUPPORTED = KITTY_KBD_DISAMBIGUATE | KITTY_KBD_REPORT_EVENT,
}; };
struct grid { struct grid {