diff options
Diffstat (limited to 'sway/input/keyboard.c')
-rw-r--r-- | sway/input/keyboard.c | 147 |
1 files changed, 144 insertions, 3 deletions
diff --git a/sway/input/keyboard.c b/sway/input/keyboard.c index 724941d8..25def7ac 100644 --- a/sway/input/keyboard.c +++ b/sway/input/keyboard.c | |||
@@ -3,6 +3,121 @@ | |||
3 | #include "sway/input/input-manager.h" | 3 | #include "sway/input/input-manager.h" |
4 | #include "log.h" | 4 | #include "log.h" |
5 | 5 | ||
6 | /* | ||
7 | * Get keysyms and modifiers from the keyboard as xkb sees them. | ||
8 | * | ||
9 | * This uses the xkb keysyms translation based on pressed modifiers and clears | ||
10 | * the consumed modifiers from the list of modifiers passed to keybind | ||
11 | * detection. | ||
12 | * | ||
13 | * On US layout, pressing Alt+Shift+2 will trigger Alt+@. | ||
14 | */ | ||
15 | static size_t keyboard_keysyms_translated(struct sway_keyboard *keyboard, | ||
16 | xkb_keycode_t keycode, const xkb_keysym_t **keysyms, | ||
17 | uint32_t *modifiers) { | ||
18 | struct wlr_input_device *device = | ||
19 | keyboard->seat_device->input_device->wlr_device; | ||
20 | *modifiers = wlr_keyboard_get_modifiers(device->keyboard); | ||
21 | xkb_mod_mask_t consumed = xkb_state_key_get_consumed_mods2( | ||
22 | device->keyboard->xkb_state, keycode, XKB_CONSUMED_MODE_XKB); | ||
23 | *modifiers = *modifiers & ~consumed; | ||
24 | |||
25 | return xkb_state_key_get_syms(device->keyboard->xkb_state, | ||
26 | keycode, keysyms); | ||
27 | } | ||
28 | |||
29 | /* | ||
30 | * Get keysyms and modifiers from the keyboard as if modifiers didn't change | ||
31 | * keysyms. | ||
32 | * | ||
33 | * This avoids the xkb keysym translation based on modifiers considered pressed | ||
34 | * in the state. | ||
35 | * | ||
36 | * This will trigger keybinds such as Alt+Shift+2. | ||
37 | */ | ||
38 | static size_t keyboard_keysyms_raw(struct sway_keyboard *keyboard, | ||
39 | xkb_keycode_t keycode, const xkb_keysym_t **keysyms, | ||
40 | uint32_t *modifiers) { | ||
41 | struct wlr_input_device *device = | ||
42 | keyboard->seat_device->input_device->wlr_device; | ||
43 | *modifiers = wlr_keyboard_get_modifiers(device->keyboard); | ||
44 | |||
45 | xkb_layout_index_t layout_index = xkb_state_key_get_layout( | ||
46 | device->keyboard->xkb_state, keycode); | ||
47 | return xkb_keymap_key_get_syms_by_level(device->keyboard->keymap, | ||
48 | keycode, layout_index, 0, keysyms); | ||
49 | } | ||
50 | |||
51 | static ssize_t pressed_keysyms_index(xkb_keysym_t *pressed_keysyms, | ||
52 | xkb_keysym_t keysym) { | ||
53 | for (size_t i = 0; i < SWAY_KEYBOARD_PRESSED_KEYSYMS_CAP; ++i) { | ||
54 | if (pressed_keysyms[i] == keysym) { | ||
55 | return i; | ||
56 | } | ||
57 | } | ||
58 | return -1; | ||
59 | } | ||
60 | |||
61 | static size_t pressed_keysyms_length(xkb_keysym_t *pressed_keysyms) { | ||
62 | size_t n = 0; | ||
63 | for (size_t i = 0; i < SWAY_KEYBOARD_PRESSED_KEYSYMS_CAP; ++i) { | ||
64 | if (pressed_keysyms[i] != XKB_KEY_NoSymbol) { | ||
65 | ++n; | ||
66 | } | ||
67 | } | ||
68 | return n; | ||
69 | } | ||
70 | |||
71 | static void pressed_keysyms_add(xkb_keysym_t *pressed_keysyms, | ||
72 | xkb_keysym_t keysym) { | ||
73 | ssize_t i = pressed_keysyms_index(pressed_keysyms, keysym); | ||
74 | if (i < 0) { | ||
75 | i = pressed_keysyms_index(pressed_keysyms, XKB_KEY_NoSymbol); | ||
76 | if (i >= 0) { | ||
77 | pressed_keysyms[i] = keysym; | ||
78 | } | ||
79 | } | ||
80 | } | ||
81 | |||
82 | static void pressed_keysyms_remove(xkb_keysym_t *pressed_keysyms, | ||
83 | xkb_keysym_t keysym) { | ||
84 | ssize_t i = pressed_keysyms_index(pressed_keysyms, keysym); | ||
85 | if (i >= 0) { | ||
86 | pressed_keysyms[i] = XKB_KEY_NoSymbol; | ||
87 | } | ||
88 | } | ||
89 | |||
90 | static bool keysym_is_modifier(xkb_keysym_t keysym) { | ||
91 | switch (keysym) { | ||
92 | case XKB_KEY_Shift_L: case XKB_KEY_Shift_R: | ||
93 | case XKB_KEY_Control_L: case XKB_KEY_Control_R: | ||
94 | case XKB_KEY_Caps_Lock: | ||
95 | case XKB_KEY_Shift_Lock: | ||
96 | case XKB_KEY_Meta_L: case XKB_KEY_Meta_R: | ||
97 | case XKB_KEY_Alt_L: case XKB_KEY_Alt_R: | ||
98 | case XKB_KEY_Super_L: case XKB_KEY_Super_R: | ||
99 | case XKB_KEY_Hyper_L: case XKB_KEY_Hyper_R: | ||
100 | return true; | ||
101 | default: | ||
102 | return false; | ||
103 | } | ||
104 | } | ||
105 | |||
106 | static void pressed_keysyms_update(xkb_keysym_t *pressed_keysyms, | ||
107 | const xkb_keysym_t *keysyms, size_t keysyms_len, | ||
108 | enum wlr_key_state state) { | ||
109 | for (size_t i = 0; i < keysyms_len; ++i) { | ||
110 | if (keysym_is_modifier(keysyms[i])) { | ||
111 | continue; | ||
112 | } | ||
113 | if (state == WLR_KEY_PRESSED) { | ||
114 | pressed_keysyms_add(pressed_keysyms, keysyms[i]); | ||
115 | } else { // WLR_KEY_RELEASED | ||
116 | pressed_keysyms_remove(pressed_keysyms, keysyms[i]); | ||
117 | } | ||
118 | } | ||
119 | } | ||
120 | |||
6 | static void handle_keyboard_key(struct wl_listener *listener, void *data) { | 121 | static void handle_keyboard_key(struct wl_listener *listener, void *data) { |
7 | struct sway_keyboard *keyboard = | 122 | struct sway_keyboard *keyboard = |
8 | wl_container_of(listener, keyboard, keyboard_key); | 123 | wl_container_of(listener, keyboard, keyboard_key); |
@@ -10,9 +125,35 @@ static void handle_keyboard_key(struct wl_listener *listener, void *data) { | |||
10 | struct wlr_input_device *wlr_device = | 125 | struct wlr_input_device *wlr_device = |
11 | keyboard->seat_device->input_device->wlr_device; | 126 | keyboard->seat_device->input_device->wlr_device; |
12 | struct wlr_event_keyboard_key *event = data; | 127 | struct wlr_event_keyboard_key *event = data; |
13 | wlr_seat_set_keyboard(wlr_seat, wlr_device); | 128 | |
14 | wlr_seat_keyboard_notify_key(wlr_seat, event->time_msec, | 129 | xkb_keycode_t keycode = event->keycode + 8; |
15 | event->keycode, event->state); | 130 | bool handled = false; |
131 | uint32_t modifiers; | ||
132 | const xkb_keysym_t *keysyms; | ||
133 | size_t keysyms_len; | ||
134 | |||
135 | // handle translated keysyms | ||
136 | keysyms_len = keyboard_keysyms_translated(keyboard, keycode, &keysyms, | ||
137 | &modifiers); | ||
138 | pressed_keysyms_update(keyboard->pressed_keysyms_translated, keysyms, | ||
139 | keysyms_len, event->state); | ||
140 | if (event->state == WLR_KEY_PRESSED) { | ||
141 | // TODO execute binding | ||
142 | } | ||
143 | |||
144 | // Handle raw keysyms | ||
145 | keysyms_len = keyboard_keysyms_raw(keyboard, keycode, &keysyms, &modifiers); | ||
146 | pressed_keysyms_update(keyboard->pressed_keysyms_raw, keysyms, keysyms_len, | ||
147 | event->state); | ||
148 | if (event->state == WLR_KEY_PRESSED && !handled) { | ||
149 | // TODO execute binding | ||
150 | } | ||
151 | |||
152 | if (!handled) { | ||
153 | wlr_seat_set_keyboard(wlr_seat, wlr_device); | ||
154 | wlr_seat_keyboard_notify_key(wlr_seat, event->time_msec, | ||
155 | event->keycode, event->state); | ||
156 | } | ||
16 | } | 157 | } |
17 | 158 | ||
18 | static void handle_keyboard_modifiers(struct wl_listener *listener, | 159 | static void handle_keyboard_modifiers(struct wl_listener *listener, |