#include "common/mem.h"
#include "config/keybind.h"
#include "config/rcxml.h"
+#include "labwc.h"
uint32_t
parse_modifier(const char *symname)
return true;
}
+static void
+update_keycodes_iter(struct xkb_keymap *keymap, xkb_keycode_t key, void *data)
+{
+ struct keybind *keybind;
+ const xkb_keysym_t *syms;
+ xkb_layout_index_t layout = *(xkb_layout_index_t *)data;
+ int nr_syms = xkb_keymap_key_get_syms_by_level(keymap, key, layout, 0, &syms);
+ if (!nr_syms) {
+ return;
+ }
+ wl_list_for_each(keybind, &rc.keybinds, link) {
+ if (keybind->keycodes_layout >= 0
+ && (xkb_layout_index_t)keybind->keycodes_layout != layout) {
+ /* Prevent storing keycodes from multiple layouts */
+ continue;
+ }
+ for (int i = 0; i < nr_syms; i++) {
+ xkb_keysym_t sym = syms[i];
+ for (size_t j = 0; j < keybind->keysyms_len; j++) {
+ if (sym != keybind->keysyms[j]) {
+ continue;
+ }
+ /* Found keycode for sym */
+ if (keybind->keycodes_len == MAX_KEYCODES) {
+ wlr_log(WLR_ERROR,
+ "Already stored %lu keycodes for keybind",
+ keybind->keycodes_len);
+ break;
+ }
+ bool keycode_exists = false;
+ for (size_t k = 0; k < keybind->keycodes_len; k++) {
+ if (keybind->keycodes[k] == key) {
+ keycode_exists = true;
+ break;
+ }
+ }
+ if (keycode_exists) {
+ continue;
+ }
+ keybind->keycodes[keybind->keycodes_len++] = key;
+ keybind->keycodes_layout = layout;
+ }
+ }
+ }
+}
+
+void
+keybind_update_keycodes(struct server *server)
+{
+ struct xkb_state *state = server->seat.keyboard_group->keyboard.xkb_state;
+ struct xkb_keymap *keymap = xkb_state_get_keymap(state);
+
+ struct keybind *keybind;
+ wl_list_for_each(keybind, &rc.keybinds, link) {
+ keybind->keycodes_len = 0;
+ keybind->keycodes_layout = -1;
+ }
+ xkb_layout_index_t layouts = xkb_keymap_num_layouts(keymap);
+ for (xkb_layout_index_t i = 0; i < layouts; i++) {
+ wlr_log(WLR_DEBUG, "Found layout %s", xkb_keymap_layout_get_name(keymap, i));
+ xkb_keymap_key_for_each(keymap, update_keycodes_iter, &i);
+ }
+}
+
struct keybind *
keybind_create(const char *keybind)
{
}
static bool
-handle_keybinding(struct server *server, uint32_t modifiers, xkb_keysym_t sym)
+handle_keybinding(struct server *server, uint32_t modifiers, xkb_keysym_t sym, xkb_keycode_t code)
{
struct keybind *keybind;
wl_list_for_each(keybind, &rc.keybinds, link) {
&& !actions_contain_toggle_keybinds(&keybind->actions)) {
continue;
}
- for (size_t i = 0; i < keybind->keysyms_len; i++) {
- if (xkb_keysym_to_lower(sym) == keybind->keysyms[i]) {
- key_state_store_pressed_keys_as_bound();
- actions_run(NULL, server, &keybind->actions, 0);
- return true;
+ if (sym == XKB_KEY_NoSymbol) {
+ /* Use keycodes */
+ for (size_t i = 0; i < keybind->keycodes_len; i++) {
+ if (keybind->keycodes[i] == code) {
+ key_state_store_pressed_keys_as_bound();
+ actions_run(NULL, server, &keybind->actions, 0);
+ return true;
+ }
+ }
+ } else {
+ /* Use syms */
+ for (size_t i = 0; i < keybind->keysyms_len; i++) {
+ if (xkb_keysym_to_lower(sym) == keybind->keysyms[i]) {
+ key_state_store_pressed_keys_as_bound();
+ actions_run(NULL, server, &keybind->actions, 0);
+ return true;
+ }
}
}
}
goto out;
}
- /* Handle compositor key bindings */
+ /*
+ * Handle compositor keybinds
+ *
+ * When matching against keybinds, we process the input keys in the
+ * following order of precedence:
+ * a. Keycodes (of physical keys) (not if keybind is layoutDependent)
+ * b. Translated keysyms (taking into account modifiers, so if Shift+1
+ * were pressed on a us keyboard, the keysym would be '!')
+ * c. Raw keysyms (ignoring modifiers such as shift, so in the above
+ * example the keysym would just be '1')
+ *
+ * The reasons for this approach are:
+ * 1. To make keybinds keyboard-layout agnostic (by checking keycodes
+ * before keysyms). This means that in a multi-layout situation,
+ * keybinds work regardless of which layout is active at the time
+ * of the key-press.
+ * 2. To support keybinds relating to keysyms that are only available
+ * in a particular layout, for example å, ä and ö.
+ * 3. To support keybinds that are only valid with a modifier, for
+ * example the numpad keys with NumLock enabled: KP_x. These would
+ * only be matched by the translated keysyms.
+ * 4. To support keybinds such as `S-1` (by checking raw keysyms).
+ *
+ * Reason 4 will also be satisfied by matching the keycodes. However,
+ * when a keybind is configured to be layoutDependent we still need
+ * the raw keysym fallback.
+ */
+
if (event->state == WL_KEYBOARD_KEY_STATE_PRESSED) {
+ /* First try keycodes */
+ handled |= handle_keybinding(server, modifiers, XKB_KEY_NoSymbol, keycode);
+ if (handled) {
+ wlr_log(WLR_DEBUG, "keycodes matched");
+ goto out;
+ }
+
+ /* Then fall back to keysyms */
for (int i = 0; i < translated.nr_syms; i++) {
- handled |= handle_keybinding(server, modifiers, translated.syms[i]);
+ handled |= handle_keybinding(server, modifiers,
+ translated.syms[i], keycode);
}
if (handled) {
+ wlr_log(WLR_DEBUG, "translated keysyms matched");
goto out;
}
+
+ /* And finally test for keysyms without modifier */
for (int i = 0; i < raw.nr_syms; i++) {
- handled |= handle_keybinding(server, modifiers, raw.syms[i]);
+ handled |= handle_keybinding(server, modifiers, raw.syms[i], keycode);
+ }
+ if (handled) {
+ wlr_log(WLR_DEBUG, "raw keysyms matched");
}
}
}
xkb_context_unref(context);
wlr_keyboard_set_repeat_info(kb, rc.repeat_rate, rc.repeat_delay);
+
+ keybind_update_keycodes(seat->server);
}
void