diff --git a/src/shl_misc.h b/src/shl_misc.h index 47bee10..9afd058 100644 --- a/src/shl_misc.h +++ b/src/shl_misc.h @@ -95,4 +95,35 @@ static inline unsigned int shl_get_xkb_mods(struct xkb_state *state) return mods; } +static inline uint32_t shl_get_ascii(struct xkb_state *state, uint32_t keycode, + const uint32_t *keysyms, + unsigned int num_keysyms) +{ + struct xkb_keymap *keymap; + xkb_layout_index_t num_layouts; + xkb_layout_index_t layout; + xkb_level_index_t level; + const xkb_keysym_t *syms; + int num_syms; + + if (num_keysyms == 1 && keysyms[0] < 128) + return keysyms[0]; + + keymap = xkb_state_get_map(state); + num_layouts = xkb_keymap_num_layouts_for_key(keymap, keycode); + + for (layout = 0; layout < num_layouts; layout++) { + level = xkb_state_key_get_level(state, keycode, layout); + num_syms = xkb_keymap_key_get_syms_by_level(keymap, keycode, + layout, level, &syms); + if (num_syms != 1) + continue; + + if (syms[0] < 128) + return syms[0]; + } + + return XKB_KEY_NoSymbol; +} + #endif /* SHL_MISC_H */ diff --git a/src/terminal.c b/src/terminal.c index e8f6f24..460dd22 100644 --- a/src/terminal.c +++ b/src/terminal.c @@ -393,8 +393,8 @@ static void input_event(struct uterm_input *input, if (ev->num_syms > 1) return; - if (tsm_vte_handle_keyboard(term->vte, ev->keysyms[0], ev->mods, - ev->codepoints[0])) { + if (tsm_vte_handle_keyboard(term->vte, ev->keysyms[0], ev->ascii, + ev->mods, ev->codepoints[0])) { tsm_screen_sb_reset(term->console); schedule_redraw(term); ev->handled = true; diff --git a/src/tsm_vte.c b/src/tsm_vte.c index 155b242..5fd9bd6 100644 --- a/src/tsm_vte.c +++ b/src/tsm_vte.c @@ -2152,10 +2152,12 @@ void tsm_vte_input(struct tsm_vte *vte, const char *u8, size_t len) } bool tsm_vte_handle_keyboard(struct tsm_vte *vte, uint32_t keysym, - unsigned int mods, uint32_t unicode) + uint32_t ascii, unsigned int mods, + uint32_t unicode) { char val, u8[4]; size_t len; + uint32_t sym; /* MOD1 (mostly labeled 'Alt') prepends an escape character to every * input that is sent by a key. @@ -2167,8 +2169,22 @@ bool tsm_vte_handle_keyboard(struct tsm_vte *vte, uint32_t keysym, if (mods & TSM_ALT_MASK) vte->flags |= FLAG_PREPEND_ESCAPE; + /* A user might actually use multiple layouts for keyboard input. The + * @keysym variable contains the actual keysym that the user used. But + * if this keysym is not in the ascii range, the input handler does + * check all other layouts that the user specified whether one of them + * maps the key to some ASCII keysym and provides this via @ascii. + * We always use the real keysym except when handling CTRL+ + * shortcuts we use the ascii keysym. This is for compatibility to xterm + * et. al. so ctrl+c always works regardless of the currently active + * keyboard layout. + * But if no ascii-sym is found, we still use the real keysym. */ + sym = ascii; + if (sym == XKB_KEY_NoSymbol) + sym = keysym; + if (mods & TSM_CONTROL_MASK) { - switch (keysym) { + switch (sym) { case XKB_KEY_2: case XKB_KEY_space: vte_write(vte, "\x00", 1); diff --git a/src/tsm_vte.h b/src/tsm_vte.h index d12b27a..638ac95 100644 --- a/src/tsm_vte.h +++ b/src/tsm_vte.h @@ -79,6 +79,7 @@ void tsm_vte_reset(struct tsm_vte *vte); void tsm_vte_hard_reset(struct tsm_vte *vte); void tsm_vte_input(struct tsm_vte *vte, const char *u8, size_t len); bool tsm_vte_handle_keyboard(struct tsm_vte *vte, uint32_t keysym, - unsigned int mods, uint32_t unicode); + uint32_t ascii, unsigned int mods, + uint32_t unicode); #endif /* TSM_VTE_H */ diff --git a/src/uterm.h b/src/uterm.h index 333e2cc..5424462 100644 --- a/src/uterm.h +++ b/src/uterm.h @@ -271,6 +271,7 @@ enum uterm_input_modifier { struct uterm_input_event { bool handled; /* user-controlled, default is false */ uint16_t keycode; /* linux keycode - KEY_* - linux/input.h */ + uint32_t ascii; /* ascii keysym for @keycode */ unsigned int mods; /* active modifiers - uterm_modifier mask */ unsigned int num_syms; /* number of keysyms */ diff --git a/src/uterm_input_uxkb.c b/src/uterm_input_uxkb.c index caa1cb4..09b8a52 100644 --- a/src/uterm_input_uxkb.c +++ b/src/uterm_input_uxkb.c @@ -164,6 +164,7 @@ int uxkb_dev_process(struct uterm_input_dev *dev, dev->event.handled = false; dev->event.keycode = code; + dev->event.ascii = shl_get_ascii(state, code, keysyms, num_keysyms); dev->event.mods = shl_get_xkb_mods(state); dev->event.num_syms = num_keysyms; memcpy(dev->event.keysyms, keysyms, sizeof(uint32_t) * num_keysyms); diff --git a/src/wlt_terminal.c b/src/wlt_terminal.c index 505945d..96b5bf0 100644 --- a/src/wlt_terminal.c +++ b/src/wlt_terminal.c @@ -398,7 +398,8 @@ static const struct wl_data_source_listener copy_listener = { }; static bool widget_key(struct wlt_widget *widget, unsigned int mask, - uint32_t sym, uint32_t state, bool handled, void *data) + uint32_t sym, uint32_t ascii, uint32_t state, + bool handled, void *data) { struct wlt_terminal *term = data; uint32_t ucs4; @@ -536,7 +537,7 @@ static bool widget_key(struct wlt_widget *widget, unsigned int mask, return true; } - if (tsm_vte_handle_keyboard(term->vte, sym, mask, ucs4)) { + if (tsm_vte_handle_keyboard(term->vte, sym, ascii, mask, ucs4)) { tsm_screen_sb_reset(term->scr); wlt_window_schedule_redraw(term->wnd); return true; diff --git a/src/wlt_theme.c b/src/wlt_theme.c index b4e65d1..52b7d17 100644 --- a/src/wlt_theme.c +++ b/src/wlt_theme.c @@ -531,7 +531,8 @@ static void widget_pointer_button(struct wlt_widget *widget, } static bool widget_key(struct wlt_widget *widget, unsigned int mask, - uint32_t sym, uint32_t state, bool handled, void *data) + uint32_t sym, uint32_t ascii, uint32_t state, + bool handled, void *data) { struct wlt_theme *theme = data; diff --git a/src/wlt_toolkit.c b/src/wlt_toolkit.c index 8531244..6b358c4 100644 --- a/src/wlt_toolkit.c +++ b/src/wlt_toolkit.c @@ -101,6 +101,7 @@ struct wlt_display { struct wlt_window *keyboard_focus; struct ev_timer *repeat_timer; uint32_t repeat_sym; + uint32_t repeat_ascii; struct wl_data_device_manager *w_manager; struct wl_data_device *w_data_dev; @@ -639,7 +640,7 @@ static void keyboard_key(void *data, struct wl_keyboard *keyboard, { struct wlt_display *disp = data; struct wlt_window *wnd = disp->keyboard_focus; - uint32_t code, num_syms; + uint32_t code, num_syms, ascii; unsigned int mask; enum wl_keyboard_key_state state = state_w; const xkb_keysym_t *syms; @@ -659,6 +660,7 @@ static void keyboard_key(void *data, struct wl_keyboard *keyboard, mask = shl_get_xkb_mods(disp->xkb_state); num_syms = xkb_key_get_syms(disp->xkb_state, code, &syms); + ascii = shl_get_ascii(disp->xkb_state, code, syms, num_syms); sym = XKB_KEY_NoSymbol; if (num_syms == 1) sym = syms[0]; @@ -667,7 +669,7 @@ static void keyboard_key(void *data, struct wl_keyboard *keyboard, shl_dlist_for_each(iter, &wnd->widget_list) { widget = shl_dlist_entry(iter, struct wlt_widget, list); if (widget->keyboard_cb) { - if (widget->keyboard_cb(widget, mask, sym, state, + if (widget->keyboard_cb(widget, mask, sym, ascii, state, handled, widget->data)) handled = true; } @@ -678,6 +680,7 @@ static void keyboard_key(void *data, struct wl_keyboard *keyboard, ev_timer_update(disp->repeat_timer, NULL); } else if (state == WL_KEYBOARD_KEY_STATE_PRESSED) { disp->repeat_sym = sym; + disp->repeat_ascii = ascii; spec.it_interval.tv_sec = 0; spec.it_interval.tv_nsec = wlt_conf.xkb_repeat_rate * 1000000; spec.it_value.tv_sec = 0; @@ -704,6 +707,7 @@ static void repeat_event(struct ev_timer *timer, uint64_t num, void *data) widget = shl_dlist_entry(iter, struct wlt_widget, list); if (widget->keyboard_cb) { if (widget->keyboard_cb(widget, mask, disp->repeat_sym, + disp->repeat_ascii, WL_KEYBOARD_KEY_STATE_PRESSED, handled, widget->data)) handled = true; diff --git a/src/wlt_toolkit.h b/src/wlt_toolkit.h index df2edc1..20fac59 100644 --- a/src/wlt_toolkit.h +++ b/src/wlt_toolkit.h @@ -115,6 +115,7 @@ typedef void (*wlt_widget_pointer_button_cb) (struct wlt_widget *widget, typedef bool (*wlt_widget_keyboard_cb) (struct wlt_widget *widget, unsigned int mods, uint32_t key, + uint32_t ascii, uint32_t state, bool handled, void *data);