diff --git a/CHANGELOG.md b/CHANGELOG.md index bf83285c..8e2283d7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -53,6 +53,8 @@ (https://codeberg.org/dnkl/foot/issues/170). * Restored window size when window is un-tiled. * XCursor shape in CSD corners when window is tiled. +* Error handling when processing keyboard input (maybe + https://codeberg.org/dnkl/foot/issues/171). ### Security diff --git a/input.c b/input.c index 99a2a7ad..0de94391 100644 --- a/input.c +++ b/input.c @@ -487,29 +487,34 @@ keyboard_keymap(void *data, struct wl_keyboard *wl_keyboard, tll_free(seat->mouse.bindings); seat->kbd.xkb = xkb_context_new(XKB_CONTEXT_NO_FLAGS); - seat->kbd.xkb_keymap = xkb_keymap_new_from_buffer( - seat->kbd.xkb, map_str, size, XKB_KEYMAP_FORMAT_TEXT_V1, - XKB_KEYMAP_COMPILE_NO_FLAGS); - seat->kbd.xkb_state = xkb_state_new(seat->kbd.xkb_keymap); + if (seat->kbd.xkb != NULL) { + seat->kbd.xkb_keymap = xkb_keymap_new_from_buffer( + seat->kbd.xkb, map_str, size, XKB_KEYMAP_FORMAT_TEXT_V1, + XKB_KEYMAP_COMPILE_NO_FLAGS); - seat->kbd.mod_shift = xkb_keymap_mod_get_index(seat->kbd.xkb_keymap, "Shift"); - seat->kbd.mod_alt = xkb_keymap_mod_get_index(seat->kbd.xkb_keymap, "Mod1") ; - seat->kbd.mod_ctrl = xkb_keymap_mod_get_index(seat->kbd.xkb_keymap, "Control"); - seat->kbd.mod_meta = xkb_keymap_mod_get_index(seat->kbd.xkb_keymap, "Mod4"); + /* Compose (dead keys) */ + seat->kbd.xkb_compose_table = xkb_compose_table_new_from_locale( + seat->kbd.xkb, setlocale(LC_CTYPE, NULL), XKB_COMPOSE_COMPILE_NO_FLAGS); - seat->kbd.key_arrow_up = xkb_keymap_key_by_name(seat->kbd.xkb_keymap, "UP"); - seat->kbd.key_arrow_down = xkb_keymap_key_by_name(seat->kbd.xkb_keymap, "DOWN"); + if (seat->kbd.xkb_compose_table == NULL) { + LOG_WARN("failed to instantiate compose table; dead keys will not work"); + } else { + seat->kbd.xkb_compose_state = xkb_compose_state_new( + seat->kbd.xkb_compose_table, XKB_COMPOSE_STATE_NO_FLAGS); + } + } - /* Compose (dead keys) */ - seat->kbd.xkb_compose_table = xkb_compose_table_new_from_locale( - seat->kbd.xkb, setlocale(LC_CTYPE, NULL), XKB_COMPOSE_COMPILE_NO_FLAGS); + if (seat->kbd.xkb_keymap != NULL) { + seat->kbd.xkb_state = xkb_state_new(seat->kbd.xkb_keymap); - if (seat->kbd.xkb_compose_table == NULL) { - LOG_WARN("failed to instantiate compose table; dead keys will not work"); - } else { - seat->kbd.xkb_compose_state = xkb_compose_state_new( - seat->kbd.xkb_compose_table, XKB_COMPOSE_STATE_NO_FLAGS); + seat->kbd.mod_shift = xkb_keymap_mod_get_index(seat->kbd.xkb_keymap, "Shift"); + seat->kbd.mod_alt = xkb_keymap_mod_get_index(seat->kbd.xkb_keymap, "Mod1") ; + seat->kbd.mod_ctrl = xkb_keymap_mod_get_index(seat->kbd.xkb_keymap, "Control"); + seat->kbd.mod_meta = xkb_keymap_mod_get_index(seat->kbd.xkb_keymap, "Mod4"); + + seat->kbd.key_arrow_up = xkb_keymap_key_by_name(seat->kbd.xkb_keymap, "UP"); + seat->kbd.key_arrow_down = xkb_keymap_key_by_name(seat->kbd.xkb_keymap, "DOWN"); } munmap(map_str, size); @@ -749,8 +754,12 @@ keyboard_key(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, struct seat *seat = data; struct terminal *term = seat->kbd_focus; - if (seat->kbd.xkb == NULL) + if (seat->kbd.xkb == NULL || + seat->kbd.xkb_keymap == NULL || + seat->kbd.xkb_state == NULL) + { return; + } assert(term != NULL); @@ -870,39 +879,43 @@ keyboard_key(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, goto maybe_repeat; } + if (compose_status == XKB_COMPOSE_CANCELLED) + goto maybe_repeat; + /* * Compose, and maybe emit "normal" character */ - uint8_t buf[64] = {0}; - int count = 0; + assert(seat->kbd.xkb_compose_state != NULL || + compose_status != XKB_COMPOSE_COMPOSED); - if (compose_status == XKB_COMPOSE_COMPOSED) { - assert(seat->kbd.xkb_compose_state != NULL); + int count = compose_status == XKB_COMPOSE_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); - count = xkb_compose_state_get_utf8( - seat->kbd.xkb_compose_state, (char *)buf, sizeof(buf)); + if (count <= 0) + goto maybe_repeat; + + /* Buffer for translated key. Use a static buffer in most cases, + * and use a malloc:ed buffer when necessary */ + uint8_t buf[32]; + uint8_t *utf8 = count < sizeof(buf) ? buf : xmalloc(count + 1); + + compose_status == XKB_COMPOSE_COMPOSED + ? xkb_compose_state_get_utf8( + seat->kbd.xkb_compose_state, (char *)utf8, count + 1) + : xkb_state_key_get_utf8( + seat->kbd.xkb_state, key, (char *)utf8, count + 1); + + if (seat->kbd.xkb_compose_state != NULL) xkb_compose_state_reset(seat->kbd.xkb_compose_state); - } - - else if (compose_status == XKB_COMPOSE_CANCELLED) { - goto maybe_repeat; - } - - else { - count = xkb_state_key_get_utf8( - seat->kbd.xkb_state, key, (char *)buf, sizeof(buf)); - } - - if (count == 0) - goto maybe_repeat; #define is_control_key(x) ((x) >= 0x40 && (x) <= 0x7f) #define IS_CTRL(x) ((x) < 0x20 || ((x) >= 0x7f && (x) <= 0x9f)) if ((keymap_mods & MOD_CTRL) && !is_control_key(sym) && - (count == 1 && !IS_CTRL(buf[0])) && + (count == 1 && !IS_CTRL(utf8[0])) && sym < 256) { static const int mod_param_map[32] = { @@ -953,11 +966,11 @@ keyboard_key(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, */ if (term->meta.esc_prefix) { term_to_slave(term, "\x1b", 1); - term_to_slave(term, buf, count); + term_to_slave(term, utf8, count); } else if (term->meta.eight_bit && count == 1) { - const wchar_t wc = 0x80 | buf[0]; + const wchar_t wc = 0x80 | utf8[0]; char utf8[8]; mbstate_t ps = {0}; @@ -966,17 +979,20 @@ keyboard_key(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, if (chars != (size_t)-1) term_to_slave(term, utf8, chars); else - term_to_slave(term, buf, count); + term_to_slave(term, utf8, count); } else { /* Alt ignored */ - term_to_slave(term, buf, count); + term_to_slave(term, utf8, count); } } else - term_to_slave(term, buf, count); + term_to_slave(term, utf8, count); } + if (utf8 != buf) + free(utf8); + term_reset_view(term); selection_cancel(term); @@ -986,7 +1002,6 @@ maybe_repeat: if (should_repeat) start_repeater(seat, key - 8); - } static void @@ -999,21 +1014,20 @@ keyboard_modifiers(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, LOG_DBG("modifiers: depressed=0x%x, latched=0x%x, locked=0x%x, group=%u", mods_depressed, mods_latched, mods_locked, group); - if (seat->kbd.xkb == NULL) - return; + if (seat->kbd.xkb_state != NULL) { + xkb_state_update_mask( + seat->kbd.xkb_state, mods_depressed, mods_latched, mods_locked, 0, 0, group); - xkb_state_update_mask( - seat->kbd.xkb_state, mods_depressed, mods_latched, mods_locked, 0, 0, group); - - /* Update state of modifiers we're interested in for e.g mouse events */ - seat->kbd.shift = xkb_state_mod_index_is_active( - seat->kbd.xkb_state, seat->kbd.mod_shift, XKB_STATE_MODS_DEPRESSED); - seat->kbd.alt = xkb_state_mod_index_is_active( - seat->kbd.xkb_state, seat->kbd.mod_alt, XKB_STATE_MODS_DEPRESSED); - seat->kbd.ctrl = xkb_state_mod_index_is_active( - seat->kbd.xkb_state, seat->kbd.mod_ctrl, XKB_STATE_MODS_DEPRESSED); - seat->kbd.meta = xkb_state_mod_index_is_active( - seat->kbd.xkb_state, seat->kbd.mod_meta, XKB_STATE_MODS_DEPRESSED); + /* Update state of modifiers we're interested in for e.g mouse events */ + seat->kbd.shift = xkb_state_mod_index_is_active( + seat->kbd.xkb_state, seat->kbd.mod_shift, XKB_STATE_MODS_DEPRESSED); + seat->kbd.alt = xkb_state_mod_index_is_active( + seat->kbd.xkb_state, seat->kbd.mod_alt, XKB_STATE_MODS_DEPRESSED); + seat->kbd.ctrl = xkb_state_mod_index_is_active( + seat->kbd.xkb_state, seat->kbd.mod_ctrl, XKB_STATE_MODS_DEPRESSED); + seat->kbd.meta = xkb_state_mod_index_is_active( + seat->kbd.xkb_state, seat->kbd.mod_meta, XKB_STATE_MODS_DEPRESSED); + } if (seat->kbd_focus && seat->kbd_focus->active_surface == TERM_SURF_GRID) term_xcursor_update_for_seat(seat->kbd_focus, seat); @@ -1571,7 +1585,7 @@ wl_pointer_button(void *data, struct wl_pointer *wl_pointer, switch (state) { case WL_POINTER_BUTTON_STATE_PRESSED: { if (!seat->mouse.consumed) { - if (seat->wl_keyboard != NULL) { + if (seat->wl_keyboard != NULL && seat->kbd.xkb_state != NULL) { /* Seat has keyboard - use mouse bindings *with* modifiers */ xkb_mod_mask_t mods = xkb_state_serialize_mods(