diff options
author | emersion <contact@emersion.fr> | 2018-06-02 09:18:07 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-06-02 09:18:07 +0100 |
commit | 2d480e754e8287ba747faf1b21d8ecb927d565a1 (patch) | |
tree | cf71dc112c1b4acb944073d93b73f76ee957ceac | |
parent | Merge pull request #2088 from RyanDwyer/fix-floating-border-pixel (diff) | |
parent | Style fix, redundant entry removal, fix extra keysym delete (diff) | |
download | sway-2d480e754e8287ba747faf1b21d8ecb927d565a1.tar.gz sway-2d480e754e8287ba747faf1b21d8ecb927d565a1.tar.zst sway-2d480e754e8287ba747faf1b21d8ecb927d565a1.zip |
Merge pull request #2080 from frsfnrrg/keyboard-remodeling
Reduce work duplication in keyboard and binding code
-rw-r--r-- | include/sway/input/keyboard.h | 31 | ||||
-rw-r--r-- | sway/commands/bind.c | 173 | ||||
-rw-r--r-- | sway/input/keyboard.c | 405 |
3 files changed, 235 insertions, 374 deletions
diff --git a/include/sway/input/keyboard.h b/include/sway/input/keyboard.h index 8ec3eb35..e99a54b1 100644 --- a/include/sway/input/keyboard.h +++ b/include/sway/input/keyboard.h | |||
@@ -3,7 +3,26 @@ | |||
3 | 3 | ||
4 | #include "sway/input/seat.h" | 4 | #include "sway/input/seat.h" |
5 | 5 | ||
6 | #define SWAY_KEYBOARD_PRESSED_KEYSYMS_CAP 32 | 6 | #define SWAY_KEYBOARD_PRESSED_KEYS_CAP 32 |
7 | |||
8 | struct sway_shortcut_state { | ||
9 | /** | ||
10 | * A list of pressed key ids (either keysyms or keycodes), | ||
11 | * including duplicates when different keycodes produce the same key id. | ||
12 | * | ||
13 | * Each key id is associated with the keycode (in `pressed_keycodes`) | ||
14 | * whose press generated it, so that the key id can be removed on | ||
15 | * keycode release without recalculating the transient link between | ||
16 | * keycode and key id at the time of the key press. | ||
17 | */ | ||
18 | uint32_t pressed_keys[SWAY_KEYBOARD_PRESSED_KEYS_CAP]; | ||
19 | /** | ||
20 | * The list of keycodes associated to currently pressed key ids, | ||
21 | * including duplicates when a keycode generates multiple key ids. | ||
22 | */ | ||
23 | uint32_t pressed_keycodes[SWAY_KEYBOARD_PRESSED_KEYS_CAP]; | ||
24 | int last_key_index; | ||
25 | }; | ||
7 | 26 | ||
8 | struct sway_keyboard { | 27 | struct sway_keyboard { |
9 | struct sway_seat_device *seat_device; | 28 | struct sway_seat_device *seat_device; |
@@ -13,11 +32,11 @@ struct sway_keyboard { | |||
13 | struct wl_listener keyboard_key; | 32 | struct wl_listener keyboard_key; |
14 | struct wl_listener keyboard_modifiers; | 33 | struct wl_listener keyboard_modifiers; |
15 | 34 | ||
16 | xkb_keysym_t pressed_keysyms_translated[SWAY_KEYBOARD_PRESSED_KEYSYMS_CAP]; | 35 | struct sway_shortcut_state state_keysyms_translated; |
17 | uint32_t modifiers_translated; | 36 | struct sway_shortcut_state state_keysyms_raw; |
18 | 37 | struct sway_shortcut_state state_keycodes; | |
19 | xkb_keysym_t pressed_keysyms_raw[SWAY_KEYBOARD_PRESSED_KEYSYMS_CAP]; | 38 | struct sway_binding *held_binding; |
20 | uint32_t modifiers_raw; | 39 | uint32_t last_modifiers; |
21 | }; | 40 | }; |
22 | 41 | ||
23 | struct sway_keyboard *sway_keyboard_create(struct sway_seat *seat, | 42 | struct sway_keyboard *sway_keyboard_create(struct sway_seat *seat, |
diff --git a/sway/commands/bind.c b/sway/commands/bind.c index c6b3368a..d0e3e22f 100644 --- a/sway/commands/bind.c +++ b/sway/commands/bind.c | |||
@@ -69,22 +69,25 @@ bool binding_key_compare(struct sway_binding *binding_a, | |||
69 | return true; | 69 | return true; |
70 | } | 70 | } |
71 | 71 | ||
72 | struct cmd_results *cmd_bindsym(int argc, char **argv) { | 72 | static struct cmd_results *cmd_bindsym_or_bindcode(int argc, char **argv, |
73 | bool bindcode) { | ||
74 | const char *bindtype = bindcode ? "bindcode" : "bindsym"; | ||
75 | |||
73 | struct cmd_results *error = NULL; | 76 | struct cmd_results *error = NULL; |
74 | if ((error = checkarg(argc, "bindsym", EXPECTED_MORE_THAN, 1))) { | 77 | if ((error = checkarg(argc, bindtype, EXPECTED_MORE_THAN, 1))) { |
75 | return error; | 78 | return error; |
76 | } | 79 | } |
77 | 80 | ||
78 | struct sway_binding *binding = calloc(1, sizeof(struct sway_binding)); | 81 | struct sway_binding *binding = calloc(1, sizeof(struct sway_binding)); |
79 | if (!binding) { | 82 | if (!binding) { |
80 | return cmd_results_new(CMD_FAILURE, "bindsym", | 83 | return cmd_results_new(CMD_FAILURE, bindtype, |
81 | "Unable to allocate binding"); | 84 | "Unable to allocate binding"); |
82 | } | 85 | } |
83 | binding->keys = create_list(); | 86 | binding->keys = create_list(); |
84 | binding->modifiers = 0; | 87 | binding->modifiers = 0; |
85 | binding->release = false; | 88 | binding->release = false; |
86 | binding->locked = false; | 89 | binding->locked = false; |
87 | binding->bindcode = false; | 90 | binding->bindcode = bindcode; |
88 | 91 | ||
89 | // Handle --release and --locked | 92 | // Handle --release and --locked |
90 | while (argc > 0) { | 93 | while (argc > 0) { |
@@ -100,9 +103,9 @@ struct cmd_results *cmd_bindsym(int argc, char **argv) { | |||
100 | } | 103 | } |
101 | if (argc < 2) { | 104 | if (argc < 2) { |
102 | free_sway_binding(binding); | 105 | free_sway_binding(binding); |
103 | return cmd_results_new(CMD_FAILURE, "bindsym", | 106 | return cmd_results_new(CMD_FAILURE, bindtype, |
104 | "Invalid bindsym command " | 107 | "Invalid %s command " |
105 | "(expected at least 2 non-option arguments, got %d)", argc); | 108 | "(expected at least 2 non-option arguments, got %d)", bindtype, argc); |
106 | } | 109 | } |
107 | 110 | ||
108 | binding->command = join_args(argv + 1, argc - 1); | 111 | binding->command = join_args(argv + 1, argc - 1); |
@@ -115,124 +118,63 @@ struct cmd_results *cmd_bindsym(int argc, char **argv) { | |||
115 | binding->modifiers |= mod; | 118 | binding->modifiers |= mod; |
116 | continue; | 119 | continue; |
117 | } | 120 | } |
118 | // Check for xkb key | ||
119 | xkb_keysym_t sym = xkb_keysym_from_name(split->items[i], | ||
120 | XKB_KEYSYM_CASE_INSENSITIVE); | ||
121 | 121 | ||
122 | // Check for mouse binding | 122 | xkb_keycode_t keycode; |
123 | if (strncasecmp(split->items[i], "button", strlen("button")) == 0 && | 123 | xkb_keysym_t keysym; |
124 | strlen(split->items[i]) == strlen("button0")) { | 124 | if (bindcode) { |
125 | sym = ((char *)split->items[i])[strlen("button")] - '1' + BTN_LEFT; | 125 | // parse keycode |
126 | } | 126 | keycode = (int)strtol(split->items[i], NULL, 10); |
127 | if (!sym) { | 127 | if (!xkb_keycode_is_legal_ext(keycode)) { |
128 | struct cmd_results *ret = cmd_results_new(CMD_INVALID, "bindsym", | 128 | error = |
129 | "Unknown key '%s'", (char *)split->items[i]); | 129 | cmd_results_new(CMD_INVALID, "bindcode", |
130 | free_sway_binding(binding); | 130 | "Invalid keycode '%s'", (char *)split->items[i]); |
131 | free_flat_list(split); | 131 | free_sway_binding(binding); |
132 | return ret; | 132 | list_free(split); |
133 | return error; | ||
134 | } | ||
135 | } else { | ||
136 | // Check for xkb key | ||
137 | keysym = xkb_keysym_from_name(split->items[i], | ||
138 | XKB_KEYSYM_CASE_INSENSITIVE); | ||
139 | |||
140 | // Check for mouse binding | ||
141 | if (strncasecmp(split->items[i], "button", strlen("button")) == 0 && | ||
142 | strlen(split->items[i]) == strlen("button0")) { | ||
143 | keysym = ((char *)split->items[i])[strlen("button")] - '1' + BTN_LEFT; | ||
144 | } | ||
145 | if (!keysym) { | ||
146 | struct cmd_results *ret = cmd_results_new(CMD_INVALID, "bindsym", | ||
147 | "Unknown key '%s'", (char *)split->items[i]); | ||
148 | free_sway_binding(binding); | ||
149 | free_flat_list(split); | ||
150 | return ret; | ||
151 | } | ||
133 | } | 152 | } |
134 | xkb_keysym_t *key = calloc(1, sizeof(xkb_keysym_t)); | 153 | uint32_t *key = calloc(1, sizeof(uint32_t)); |
135 | if (!key) { | 154 | if (!key) { |
136 | free_sway_binding(binding); | 155 | free_sway_binding(binding); |
137 | free_flat_list(split); | 156 | free_flat_list(split); |
138 | return cmd_results_new(CMD_FAILURE, "bindsym", | 157 | return cmd_results_new(CMD_FAILURE, bindtype, |
139 | "Unable to allocate binding"); | 158 | "Unable to allocate binding"); |
140 | } | 159 | } |
141 | *key = sym; | ||
142 | list_add(binding->keys, key); | ||
143 | } | ||
144 | free_flat_list(split); | ||
145 | binding->order = binding_order++; | ||
146 | |||
147 | list_t *mode_bindings = config->current_mode->keysym_bindings; | ||
148 | |||
149 | // overwrite the binding if it already exists | ||
150 | bool overwritten = false; | ||
151 | for (int i = 0; i < mode_bindings->length; ++i) { | ||
152 | struct sway_binding *config_binding = mode_bindings->items[i]; | ||
153 | if (binding_key_compare(binding, config_binding)) { | ||
154 | wlr_log(L_DEBUG, "overwriting old binding with command '%s'", | ||
155 | config_binding->command); | ||
156 | free_sway_binding(config_binding); | ||
157 | mode_bindings->items[i] = binding; | ||
158 | overwritten = true; | ||
159 | } | ||
160 | } | ||
161 | |||
162 | if (!overwritten) { | ||
163 | list_add(mode_bindings, binding); | ||
164 | } | ||
165 | |||
166 | wlr_log(L_DEBUG, "bindsym - Bound %s to command %s", | ||
167 | argv[0], binding->command); | ||
168 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | ||
169 | } | ||
170 | |||
171 | struct cmd_results *cmd_bindcode(int argc, char **argv) { | ||
172 | struct cmd_results *error = NULL; | ||
173 | if ((error = checkarg(argc, "bindcode", EXPECTED_MORE_THAN, 1))) { | ||
174 | return error; | ||
175 | } | ||
176 | |||
177 | struct sway_binding *binding = calloc(1, sizeof(struct sway_binding)); | ||
178 | if (!binding) { | ||
179 | return cmd_results_new(CMD_FAILURE, "bindsym", | ||
180 | "Unable to allocate binding"); | ||
181 | } | ||
182 | binding->keys = create_list(); | ||
183 | binding->modifiers = 0; | ||
184 | binding->release = false; | ||
185 | binding->locked = false; | ||
186 | binding->bindcode = true; | ||
187 | 160 | ||
188 | // Handle --release and --locked | 161 | if (bindcode) { |
189 | while (argc > 0) { | 162 | *key = (uint32_t)keycode; |
190 | if (strcmp("--release", argv[0]) == 0) { | ||
191 | binding->release = true; | ||
192 | } else if (strcmp("--locked", argv[0]) == 0) { | ||
193 | binding->locked = true; | ||
194 | } else { | 163 | } else { |
195 | break; | 164 | *key = (uint32_t)keysym; |
196 | } | 165 | } |
197 | argv++; | ||
198 | argc--; | ||
199 | } | ||
200 | if (argc < 2) { | ||
201 | free_sway_binding(binding); | ||
202 | return cmd_results_new(CMD_FAILURE, "bindcode", | ||
203 | "Invalid bindcode command " | ||
204 | "(expected at least 2 non-option arguments, got %d)", argc); | ||
205 | } | ||
206 | 166 | ||
207 | binding->command = join_args(argv + 1, argc - 1); | ||
208 | |||
209 | list_t *split = split_string(argv[0], "+"); | ||
210 | for (int i = 0; i < split->length; ++i) { | ||
211 | // Check for a modifier key | ||
212 | uint32_t mod; | ||
213 | if ((mod = get_modifier_mask_by_name(split->items[i])) > 0) { | ||
214 | binding->modifiers |= mod; | ||
215 | continue; | ||
216 | } | ||
217 | // parse keycode | ||
218 | xkb_keycode_t keycode = (int)strtol(split->items[i], NULL, 10); | ||
219 | if (!xkb_keycode_is_legal_ext(keycode)) { | ||
220 | error = | ||
221 | cmd_results_new(CMD_INVALID, "bindcode", | ||
222 | "Invalid keycode '%s'", (char *)split->items[i]); | ||
223 | free_sway_binding(binding); | ||
224 | list_free(split); | ||
225 | return error; | ||
226 | } | ||
227 | xkb_keycode_t *key = calloc(1, sizeof(xkb_keycode_t)); | ||
228 | *key = keycode - 8; | ||
229 | list_add(binding->keys, key); | 167 | list_add(binding->keys, key); |
230 | } | 168 | } |
231 | free_flat_list(split); | 169 | free_flat_list(split); |
232 | |||
233 | binding->order = binding_order++; | 170 | binding->order = binding_order++; |
234 | 171 | ||
235 | list_t *mode_bindings = config->current_mode->keycode_bindings; | 172 | list_t *mode_bindings; |
173 | if (bindcode) { | ||
174 | mode_bindings = config->current_mode->keycode_bindings; | ||
175 | } else { | ||
176 | mode_bindings = config->current_mode->keysym_bindings; | ||
177 | } | ||
236 | 178 | ||
237 | // overwrite the binding if it already exists | 179 | // overwrite the binding if it already exists |
238 | bool overwritten = false; | 180 | bool overwritten = false; |
@@ -251,7 +193,16 @@ struct cmd_results *cmd_bindcode(int argc, char **argv) { | |||
251 | list_add(mode_bindings, binding); | 193 | list_add(mode_bindings, binding); |
252 | } | 194 | } |
253 | 195 | ||
254 | wlr_log(L_DEBUG, "bindcode - Bound %s to command %s", | 196 | wlr_log(L_DEBUG, "%s - Bound %s to command %s", |
255 | argv[0], binding->command); | 197 | bindtype, argv[0], binding->command); |
256 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | 198 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); |
199 | |||
200 | } | ||
201 | |||
202 | struct cmd_results *cmd_bindsym(int argc, char **argv) { | ||
203 | return cmd_bindsym_or_bindcode(argc, argv, false); | ||
204 | } | ||
205 | |||
206 | struct cmd_results *cmd_bindcode(int argc, char **argv) { | ||
207 | return cmd_bindsym_or_bindcode(argc, argv, true); | ||
257 | } | 208 | } |
diff --git a/sway/input/keyboard.c b/sway/input/keyboard.c index e873eea3..420cefa6 100644 --- a/sway/input/keyboard.c +++ b/sway/input/keyboard.c | |||
@@ -9,88 +9,91 @@ | |||
9 | #include "sway/commands.h" | 9 | #include "sway/commands.h" |
10 | #include "log.h" | 10 | #include "log.h" |
11 | 11 | ||
12 | static bool keysym_is_modifier(xkb_keysym_t keysym) { | 12 | /** |
13 | switch (keysym) { | 13 | * Update the shortcut model state in response to new input |
14 | case XKB_KEY_Shift_L: case XKB_KEY_Shift_R: | 14 | */ |
15 | case XKB_KEY_Control_L: case XKB_KEY_Control_R: | 15 | static void update_shortcut_state(struct sway_shortcut_state *state, |
16 | case XKB_KEY_Caps_Lock: | 16 | struct wlr_event_keyboard_key *event, uint32_t new_key, |
17 | case XKB_KEY_Shift_Lock: | 17 | bool last_key_was_a_modifier) { |
18 | case XKB_KEY_Meta_L: case XKB_KEY_Meta_R: | 18 | if (event->state == WLR_KEY_PRESSED) { |
19 | case XKB_KEY_Alt_L: case XKB_KEY_Alt_R: | 19 | if (last_key_was_a_modifier && state->last_key_index >= 0) { |
20 | case XKB_KEY_Super_L: case XKB_KEY_Super_R: | 20 | // Last pressed key before this one was a modifier |
21 | case XKB_KEY_Hyper_L: case XKB_KEY_Hyper_R: | 21 | state->pressed_keycodes[state->last_key_index] = 0; |
22 | return true; | 22 | state->pressed_keys[state->last_key_index] = 0; |
23 | default: | 23 | state->last_key_index = -1; |
24 | return false; | ||
25 | } | ||
26 | } | ||
27 | |||
28 | static size_t pressed_keysyms_length(xkb_keysym_t *pressed_keysyms) { | ||
29 | size_t n = 0; | ||
30 | for (size_t i = 0; i < SWAY_KEYBOARD_PRESSED_KEYSYMS_CAP; ++i) { | ||
31 | if (pressed_keysyms[i] != XKB_KEY_NoSymbol) { | ||
32 | ++n; | ||
33 | } | 24 | } |
34 | } | ||
35 | return n; | ||
36 | } | ||
37 | 25 | ||
38 | static ssize_t pressed_keysyms_index(xkb_keysym_t *pressed_keysyms, | 26 | // Add current key to set; there may be duplicates |
39 | xkb_keysym_t keysym) { | 27 | for (size_t i = 0; i < SWAY_KEYBOARD_PRESSED_KEYS_CAP; ++i) { |
40 | for (size_t i = 0; i < SWAY_KEYBOARD_PRESSED_KEYSYMS_CAP; ++i) { | 28 | if (!state->pressed_keys[i]) { |
41 | if (pressed_keysyms[i] == keysym) { | 29 | state->pressed_keys[i] = new_key; |
42 | return i; | 30 | state->pressed_keycodes[i] = event->keycode; |
31 | state->last_key_index = i; | ||
32 | break; | ||
33 | } | ||
43 | } | 34 | } |
44 | } | 35 | } else { |
45 | return -1; | 36 | for (size_t i = 0; i < SWAY_KEYBOARD_PRESSED_KEYS_CAP; ++i) { |
46 | } | 37 | // The same keycode may match multiple keysyms. |
47 | 38 | if (state->pressed_keycodes[i] == event->keycode) { | |
48 | static void pressed_keysyms_add(xkb_keysym_t *pressed_keysyms, | 39 | state->pressed_keys[i] = 0; |
49 | xkb_keysym_t keysym) { | 40 | state->pressed_keycodes[i] = 0; |
50 | ssize_t i = pressed_keysyms_index(pressed_keysyms, keysym); | 41 | } |
51 | if (i < 0) { | ||
52 | i = pressed_keysyms_index(pressed_keysyms, XKB_KEY_NoSymbol); | ||
53 | if (i >= 0) { | ||
54 | pressed_keysyms[i] = keysym; | ||
55 | } | 42 | } |
56 | } | 43 | } |
57 | } | 44 | } |
58 | 45 | ||
59 | static void pressed_keysyms_remove(xkb_keysym_t *pressed_keysyms, | 46 | /** |
60 | xkb_keysym_t keysym) { | 47 | * |
61 | ssize_t i = pressed_keysyms_index(pressed_keysyms, keysym); | 48 | * Returns a binding which matches the shortcut model state (ignoring the |
62 | if (i >= 0) { | 49 | * `release` flag). |
63 | pressed_keysyms[i] = XKB_KEY_NoSymbol; | 50 | */ |
51 | static struct sway_binding *get_active_binding( | ||
52 | struct sway_shortcut_state *state, list_t *bindings, | ||
53 | uint32_t modifiers, bool locked) { | ||
54 | int npressed_keys = 0; | ||
55 | for (size_t i = 0; i < SWAY_KEYBOARD_PRESSED_KEYS_CAP; ++i) { | ||
56 | if (state->pressed_keys[i]) { | ||
57 | ++npressed_keys; | ||
58 | } | ||
64 | } | 59 | } |
65 | } | 60 | for (int i = 0; i < bindings->length; ++i) { |
61 | struct sway_binding *binding = bindings->items[i]; | ||
66 | 62 | ||
67 | static void pressed_keysyms_update(xkb_keysym_t *pressed_keysyms, | 63 | if (modifiers ^ binding->modifiers || |
68 | const xkb_keysym_t *keysyms, size_t keysyms_len, | 64 | npressed_keys != binding->keys->length || |
69 | enum wlr_key_state state) { | 65 | locked > binding->locked) { |
70 | for (size_t i = 0; i < keysyms_len; ++i) { | ||
71 | if (keysym_is_modifier(keysyms[i])) { | ||
72 | continue; | 66 | continue; |
73 | } | 67 | } |
74 | if (state == WLR_KEY_PRESSED) { | 68 | |
75 | pressed_keysyms_add(pressed_keysyms, keysyms[i]); | 69 | bool match = true; |
76 | } else { // WLR_KEY_RELEASED | 70 | for (int j = 0; j < binding->keys->length; ++j) { |
77 | pressed_keysyms_remove(pressed_keysyms, keysyms[i]); | 71 | uint32_t key = *(uint32_t *)binding->keys->items[j]; |
72 | |||
73 | bool key_found = false; | ||
74 | for (int k = 0; k < SWAY_KEYBOARD_PRESSED_KEYS_CAP; ++k) { | ||
75 | if (state->pressed_keys[k] == key) { | ||
76 | key_found = true; | ||
77 | break; | ||
78 | } | ||
79 | } | ||
80 | if (!key_found) { | ||
81 | match = false; | ||
82 | break; | ||
83 | } | ||
78 | } | 84 | } |
79 | } | ||
80 | } | ||
81 | 85 | ||
82 | static bool binding_matches_key_state(struct sway_binding *binding, | 86 | if (match) { |
83 | enum wlr_key_state key_state) { | 87 | return binding; |
84 | if (key_state == WLR_KEY_PRESSED && !binding->release) { | 88 | } |
85 | return true; | ||
86 | } | ||
87 | if (key_state == WLR_KEY_RELEASED && binding->release) { | ||
88 | return true; | ||
89 | } | 89 | } |
90 | 90 | ||
91 | return false; | 91 | return NULL; |
92 | } | 92 | } |
93 | 93 | ||
94 | /** | ||
95 | * Execute the command associated to a binding | ||
96 | */ | ||
94 | static void keyboard_execute_command(struct sway_keyboard *keyboard, | 97 | static void keyboard_execute_command(struct sway_keyboard *keyboard, |
95 | struct sway_binding *binding) { | 98 | struct sway_binding *binding) { |
96 | wlr_log(L_DEBUG, "running command for binding: %s", | 99 | wlr_log(L_DEBUG, "running command for binding: %s", |
@@ -113,7 +116,7 @@ static void keyboard_execute_command(struct sway_keyboard *keyboard, | |||
113 | * should be propagated to clients. | 116 | * should be propagated to clients. |
114 | */ | 117 | */ |
115 | static bool keyboard_execute_compositor_binding(struct sway_keyboard *keyboard, | 118 | static bool keyboard_execute_compositor_binding(struct sway_keyboard *keyboard, |
116 | xkb_keysym_t *pressed_keysyms, uint32_t modifiers, size_t keysyms_len) { | 119 | const xkb_keysym_t *pressed_keysyms, uint32_t modifiers, size_t keysyms_len) { |
117 | for (size_t i = 0; i < keysyms_len; ++i) { | 120 | for (size_t i = 0; i < keysyms_len; ++i) { |
118 | xkb_keysym_t keysym = pressed_keysyms[i]; | 121 | xkb_keysym_t keysym = pressed_keysyms[i]; |
119 | if (keysym >= XKB_KEY_XF86Switch_VT_1 && | 122 | if (keysym >= XKB_KEY_XF86Switch_VT_1 && |
@@ -134,157 +137,6 @@ static bool keyboard_execute_compositor_binding(struct sway_keyboard *keyboard, | |||
134 | } | 137 | } |
135 | 138 | ||
136 | /** | 139 | /** |
137 | * Execute keyboard bindings bound with `bindysm` for the given keyboard state. | ||
138 | * | ||
139 | * Returns true if the keysym was handled by a binding and false if the event | ||
140 | * should be propagated to clients. | ||
141 | */ | ||
142 | static bool keyboard_execute_bindsym(struct sway_keyboard *keyboard, | ||
143 | xkb_keysym_t *pressed_keysyms, uint32_t modifiers, | ||
144 | enum wlr_key_state key_state, bool locked) { | ||
145 | // configured bindings | ||
146 | int n = pressed_keysyms_length(pressed_keysyms); | ||
147 | list_t *keysym_bindings = config->current_mode->keysym_bindings; | ||
148 | for (int i = 0; i < keysym_bindings->length; ++i) { | ||
149 | struct sway_binding *binding = keysym_bindings->items[i]; | ||
150 | if (!binding_matches_key_state(binding, key_state) || | ||
151 | modifiers ^ binding->modifiers || | ||
152 | n != binding->keys->length || locked > binding->locked) { | ||
153 | continue; | ||
154 | } | ||
155 | |||
156 | bool match = true; | ||
157 | for (int j = 0; j < binding->keys->length; ++j) { | ||
158 | match = | ||
159 | pressed_keysyms_index(pressed_keysyms, | ||
160 | *(int*)binding->keys->items[j]) >= 0; | ||
161 | |||
162 | if (!match) { | ||
163 | break; | ||
164 | } | ||
165 | } | ||
166 | |||
167 | if (match) { | ||
168 | keyboard_execute_command(keyboard, binding); | ||
169 | return true; | ||
170 | } | ||
171 | } | ||
172 | |||
173 | return false; | ||
174 | } | ||
175 | |||
176 | static bool binding_matches_keycodes(struct wlr_keyboard *keyboard, | ||
177 | struct sway_binding *binding, struct wlr_event_keyboard_key *event, bool locked) { | ||
178 | assert(binding->bindcode); | ||
179 | |||
180 | uint32_t keycode = event->keycode + 8; | ||
181 | |||
182 | if (!binding_matches_key_state(binding, event->state)) { | ||
183 | return false; | ||
184 | } | ||
185 | |||
186 | if (locked > binding->locked) { | ||
187 | return false; | ||
188 | } | ||
189 | |||
190 | uint32_t modifiers = wlr_keyboard_get_modifiers(keyboard); | ||
191 | if (modifiers ^ binding->modifiers) { | ||
192 | return false; | ||
193 | } | ||
194 | |||
195 | // on release, the released key must be in the binding | ||
196 | if (event->state == WLR_KEY_RELEASED) { | ||
197 | bool found = false; | ||
198 | for (int i = 0; i < binding->keys->length; ++i) { | ||
199 | uint32_t binding_keycode = *(uint32_t*)binding->keys->items[i] + 8; | ||
200 | if (binding_keycode == keycode) { | ||
201 | found = true; | ||
202 | break; | ||
203 | } | ||
204 | } | ||
205 | if (!found) { | ||
206 | return false; | ||
207 | } | ||
208 | } | ||
209 | |||
210 | // every keycode in the binding must be present in the pressed keys on the | ||
211 | // keyboard | ||
212 | for (int i = 0; i < binding->keys->length; ++i) { | ||
213 | uint32_t binding_keycode = *(uint32_t*)binding->keys->items[i] + 8; | ||
214 | if (event->state == WLR_KEY_RELEASED && keycode == binding_keycode) { | ||
215 | continue; | ||
216 | } | ||
217 | |||
218 | bool found = false; | ||
219 | for (size_t j = 0; j < keyboard->num_keycodes; ++j) { | ||
220 | xkb_keycode_t keycode = keyboard->keycodes[j] + 8; | ||
221 | if (keycode == binding_keycode) { | ||
222 | found = true; | ||
223 | break; | ||
224 | } | ||
225 | } | ||
226 | |||
227 | if (!found) { | ||
228 | return false; | ||
229 | } | ||
230 | } | ||
231 | |||
232 | // every keycode pressed on the keyboard must be present within the binding | ||
233 | // keys (unless it is a modifier) | ||
234 | for (size_t i = 0; i < keyboard->num_keycodes; ++i) { | ||
235 | xkb_keycode_t keycode = keyboard->keycodes[i] + 8; | ||
236 | bool found = false; | ||
237 | for (int j = 0; j < binding->keys->length; ++j) { | ||
238 | uint32_t binding_keycode = *(uint32_t*)binding->keys->items[j] + 8; | ||
239 | if (binding_keycode == keycode) { | ||
240 | found = true; | ||
241 | break; | ||
242 | } | ||
243 | } | ||
244 | |||
245 | if (!found) { | ||
246 | if (!binding->modifiers) { | ||
247 | return false; | ||
248 | } | ||
249 | |||
250 | // check if it is a modifier, which we know matched from the check | ||
251 | // above | ||
252 | const xkb_keysym_t *keysyms; | ||
253 | int num_keysyms = | ||
254 | xkb_state_key_get_syms(keyboard->xkb_state, | ||
255 | keycode, &keysyms); | ||
256 | if (num_keysyms != 1 || !keysym_is_modifier(keysyms[0])) { | ||
257 | return false; | ||
258 | } | ||
259 | } | ||
260 | } | ||
261 | |||
262 | return true; | ||
263 | } | ||
264 | |||
265 | /** | ||
266 | * Execute keyboard bindings bound with `bindcode` for the given keyboard state. | ||
267 | * | ||
268 | * Returns true if the keysym was handled by a binding and false if the event | ||
269 | * should be propagated to clients. | ||
270 | */ | ||
271 | static bool keyboard_execute_bindcode(struct sway_keyboard *keyboard, | ||
272 | struct wlr_event_keyboard_key *event, bool locked) { | ||
273 | struct wlr_keyboard *wlr_keyboard = | ||
274 | keyboard->seat_device->input_device->wlr_device->keyboard; | ||
275 | list_t *keycode_bindings = config->current_mode->keycode_bindings; | ||
276 | for (int i = 0; i < keycode_bindings->length; ++i) { | ||
277 | struct sway_binding *binding = keycode_bindings->items[i]; | ||
278 | if (binding_matches_keycodes(wlr_keyboard, binding, event, locked)) { | ||
279 | keyboard_execute_command(keyboard, binding); | ||
280 | return true; | ||
281 | } | ||
282 | } | ||
283 | |||
284 | return false; | ||
285 | } | ||
286 | |||
287 | /** | ||
288 | * Get keysyms and modifiers from the keyboard as xkb sees them. | 140 | * Get keysyms and modifiers from the keyboard as xkb sees them. |
289 | * | 141 | * |
290 | * This uses the xkb keysyms translation based on pressed modifiers and clears | 142 | * This uses the xkb keysyms translation based on pressed modifiers and clears |
@@ -339,61 +191,96 @@ static void handle_keyboard_key(struct wl_listener *listener, void *data) { | |||
339 | struct wlr_event_keyboard_key *event = data; | 191 | struct wlr_event_keyboard_key *event = data; |
340 | bool input_inhibited = keyboard->seat_device->sway_seat->exclusive_client != NULL; | 192 | bool input_inhibited = keyboard->seat_device->sway_seat->exclusive_client != NULL; |
341 | 193 | ||
194 | // Identify new keycode, raw keysym(s), and translated keysym(s) | ||
342 | xkb_keycode_t keycode = event->keycode + 8; | 195 | xkb_keycode_t keycode = event->keycode + 8; |
343 | bool handled = false; | ||
344 | 196 | ||
345 | // handle keycodes | ||
346 | handled = keyboard_execute_bindcode(keyboard, event, input_inhibited); | ||
347 | |||
348 | // handle translated keysyms | ||
349 | if (!handled && event->state == WLR_KEY_RELEASED) { | ||
350 | handled = keyboard_execute_bindsym(keyboard, | ||
351 | keyboard->pressed_keysyms_translated, | ||
352 | keyboard->modifiers_translated, | ||
353 | event->state, input_inhibited); | ||
354 | } | ||
355 | const xkb_keysym_t *translated_keysyms; | 197 | const xkb_keysym_t *translated_keysyms; |
198 | uint32_t translated_modifiers; | ||
356 | size_t translated_keysyms_len = | 199 | size_t translated_keysyms_len = |
357 | keyboard_keysyms_translated(keyboard, keycode, &translated_keysyms, | 200 | keyboard_keysyms_translated(keyboard, keycode, &translated_keysyms, |
358 | &keyboard->modifiers_translated); | 201 | &translated_modifiers); |
359 | pressed_keysyms_update(keyboard->pressed_keysyms_translated, | ||
360 | translated_keysyms, translated_keysyms_len, event->state); | ||
361 | if (!handled && event->state == WLR_KEY_PRESSED) { | ||
362 | handled = keyboard_execute_bindsym(keyboard, | ||
363 | keyboard->pressed_keysyms_translated, | ||
364 | keyboard->modifiers_translated, | ||
365 | event->state, input_inhibited); | ||
366 | } | ||
367 | 202 | ||
368 | // Handle raw keysyms | ||
369 | if (!handled && event->state == WLR_KEY_RELEASED) { | ||
370 | handled = keyboard_execute_bindsym(keyboard, | ||
371 | keyboard->pressed_keysyms_raw, keyboard->modifiers_raw, | ||
372 | event->state, input_inhibited); | ||
373 | } | ||
374 | const xkb_keysym_t *raw_keysyms; | 203 | const xkb_keysym_t *raw_keysyms; |
204 | uint32_t raw_modifiers; | ||
375 | size_t raw_keysyms_len = | 205 | size_t raw_keysyms_len = |
376 | keyboard_keysyms_raw(keyboard, keycode, &raw_keysyms, &keyboard->modifiers_raw); | 206 | keyboard_keysyms_raw(keyboard, keycode, &raw_keysyms, &raw_modifiers); |
377 | pressed_keysyms_update(keyboard->pressed_keysyms_raw, raw_keysyms, | 207 | |
378 | raw_keysyms_len, event->state); | 208 | struct wlr_input_device *device = |
379 | if (!handled && event->state == WLR_KEY_PRESSED) { | 209 | keyboard->seat_device->input_device->wlr_device; |
380 | handled = keyboard_execute_bindsym(keyboard, | 210 | uint32_t code_modifiers = wlr_keyboard_get_modifiers(device->keyboard); |
381 | keyboard->pressed_keysyms_raw, keyboard->modifiers_raw, | 211 | |
382 | event->state, input_inhibited); | 212 | bool last_key_was_a_modifier = code_modifiers != keyboard->last_modifiers; |
213 | keyboard->last_modifiers = code_modifiers; | ||
214 | |||
215 | // Update shortcut model state | ||
216 | update_shortcut_state(&keyboard->state_keycodes, event, | ||
217 | (uint32_t)keycode, last_key_was_a_modifier); | ||
218 | for (size_t i = 0; i < translated_keysyms_len; ++i) { | ||
219 | update_shortcut_state(&keyboard->state_keysyms_translated, | ||
220 | event, (uint32_t)translated_keysyms[i], | ||
221 | last_key_was_a_modifier && i == 0); | ||
222 | } | ||
223 | for (size_t i = 0; i < raw_keysyms_len; ++i) { | ||
224 | update_shortcut_state(&keyboard->state_keysyms_raw, | ||
225 | event, (uint32_t)raw_keysyms[i], | ||
226 | last_key_was_a_modifier && i == 0); | ||
227 | } | ||
228 | |||
229 | // identify which binding should be executed. | ||
230 | struct sway_binding *binding = get_active_binding( | ||
231 | &keyboard->state_keycodes, | ||
232 | config->current_mode->keycode_bindings, | ||
233 | code_modifiers, input_inhibited); | ||
234 | struct sway_binding *translated_binding = get_active_binding( | ||
235 | &keyboard->state_keysyms_translated, | ||
236 | config->current_mode->keysym_bindings, | ||
237 | translated_modifiers, input_inhibited); | ||
238 | if (translated_binding && !binding) { | ||
239 | binding = translated_binding; | ||
240 | } else if (binding && translated_binding && binding != translated_binding) { | ||
241 | wlr_log(L_DEBUG, "encountered duplicate bindings %d and %d", | ||
242 | binding->order, translated_binding->order); | ||
243 | } | ||
244 | struct sway_binding *raw_binding = get_active_binding( | ||
245 | &keyboard->state_keysyms_raw, | ||
246 | config->current_mode->keysym_bindings, | ||
247 | raw_modifiers, input_inhibited); | ||
248 | if (raw_binding && !binding) { | ||
249 | binding = raw_binding; | ||
250 | } else if (binding && raw_binding && binding != raw_binding) { | ||
251 | wlr_log(L_DEBUG, "encountered duplicate bindings %d and %d", | ||
252 | binding->order, raw_binding->order); | ||
253 | } | ||
254 | |||
255 | bool handled = false; | ||
256 | |||
257 | // Execute the identified binding if need be. | ||
258 | if (keyboard->held_binding && binding != keyboard->held_binding && | ||
259 | event->state == WLR_KEY_RELEASED) { | ||
260 | keyboard_execute_command(keyboard, keyboard->held_binding); | ||
261 | handled = true; | ||
262 | } | ||
263 | if (binding != keyboard->held_binding) { | ||
264 | keyboard->held_binding = NULL; | ||
265 | } | ||
266 | if (binding && event->state == WLR_KEY_PRESSED) { | ||
267 | if (binding->release) { | ||
268 | keyboard->held_binding = binding; | ||
269 | } else { | ||
270 | keyboard_execute_command(keyboard, binding); | ||
271 | handled = true; | ||
272 | } | ||
383 | } | 273 | } |
384 | 274 | ||
385 | // Compositor bindings | 275 | // Compositor bindings |
386 | if (!handled && event->state == WLR_KEY_PRESSED) { | 276 | if (!handled && event->state == WLR_KEY_PRESSED) { |
387 | handled = | 277 | handled = keyboard_execute_compositor_binding( |
388 | keyboard_execute_compositor_binding(keyboard, | 278 | keyboard, translated_keysyms, translated_modifiers, |
389 | keyboard->pressed_keysyms_translated, | ||
390 | keyboard->modifiers_translated, | ||
391 | translated_keysyms_len); | 279 | translated_keysyms_len); |
392 | } | 280 | } |
393 | if (!handled && event->state == WLR_KEY_PRESSED) { | 281 | if (!handled && event->state == WLR_KEY_PRESSED) { |
394 | handled = | 282 | handled = keyboard_execute_compositor_binding( |
395 | keyboard_execute_compositor_binding(keyboard, | 283 | keyboard, raw_keysyms, raw_modifiers, |
396 | keyboard->pressed_keysyms_raw, keyboard->modifiers_raw, | ||
397 | raw_keysyms_len); | 284 | raw_keysyms_len); |
398 | } | 285 | } |
399 | 286 | ||
@@ -429,6 +316,10 @@ struct sway_keyboard *sway_keyboard_create(struct sway_seat *seat, | |||
429 | wl_list_init(&keyboard->keyboard_key.link); | 316 | wl_list_init(&keyboard->keyboard_key.link); |
430 | wl_list_init(&keyboard->keyboard_modifiers.link); | 317 | wl_list_init(&keyboard->keyboard_modifiers.link); |
431 | 318 | ||
319 | keyboard->state_keycodes.last_key_index = -1; | ||
320 | keyboard->state_keysyms_raw.last_key_index = -1; | ||
321 | keyboard->state_keysyms_translated.last_key_index = -1; | ||
322 | |||
432 | return keyboard; | 323 | return keyboard; |
433 | } | 324 | } |
434 | 325 | ||