Refactor binding accessor
Patch status: merged
Patch by Tony Crisci
Long description:
Change the primary binding accessor to `get_binding_from_xcb_event`. This function gets a binding from a generic xcb event of type KeyPress, KeyRelease, ButtonPress, or ButtonRelease by determining the input type (keyboard or mouse), the modifiers pressed from the filtered event `state`, managing the proper fall back in case mode switch is enabled, and finally querying the bindings for a binding that matches the event. The logic of querying keyboard bindings is not intended to be altered by this change. The general accessor has been slightly modified to work with mouse bindings and made private because it is only used in bindings.c
To apply this patch, use:
curl http://cr.i3wm.org/patch/532/raw.patch | git am
b/include/bindings.h
29 |
@@ -31,11 +31,11 @@ Binding *configure_binding(const char *bindtype, const char *modifiers, const ch |
30 |
void grab_all_keys(xcb_connection_t *conn, bool bind_mode_switch); |
31 |
|
32 |
/** |
33 |
- * Returns a pointer to the keyboard Binding with the specified modifiers and |
34 |
- * keycode or NULL if no such binding exists. |
35 |
+ * Returns a pointer to the Binding that matches the given xcb event or NULL if |
36 |
+ * no such binding exists. |
37 |
* |
38 |
*/ |
39 |
-Binding *get_keyboard_binding(uint16_t modifiers, bool key_release, xcb_keycode_t keycode); |
40 |
+Binding *get_binding_from_xcb_event(xcb_generic_event_t *event); |
41 |
|
42 |
/** |
43 |
* Translates keysymbols to keycodes for all bindings which use keysyms. |
b/include/data.h
48 |
@@ -92,6 +92,14 @@ typedef enum { |
49 |
} layout_t; |
50 |
|
51 |
/** |
52 |
+ * Binding input types. See Binding::input_type. |
53 |
+ */ |
54 |
+typedef enum { |
55 |
+ B_KEYBOARD = 0, |
56 |
+ B_MOUSE = 1 |
57 |
+} input_type_t; |
58 |
+ |
59 |
+/** |
60 |
* Stores a rectangle, for example the size of a window, the child window etc. |
61 |
* It needs to be packed so that the compiler will not add any padding bytes. |
62 |
* (it is used in src/ewmh.c for example) |
63 |
@@ -215,12 +223,7 @@ struct regex { |
64 |
struct Binding { |
65 |
/* The type of input this binding is for. (Mouse bindings are not yet |
66 |
* implemented. All bindings are currently assumed to be keyboard bindings.) */ |
67 |
- enum { |
68 |
- /* Created with "bindsym", "bindcode", and "bind" */ |
69 |
- B_KEYBOARD = 0, |
70 |
- /* Created with "bindmouse" (not yet implemented). */ |
71 |
- B_MOUSE = 1, |
72 |
- } input_type; |
73 |
+ input_type_t input_type; |
74 |
|
75 |
/** If true, the binding should be executed upon a KeyRelease event, not a |
76 |
* KeyPress (the default). */ |
b/src/bindings.c
81 |
@@ -123,18 +123,18 @@ void grab_all_keys(xcb_connection_t *conn, bool bind_mode_switch) { |
82 |
} |
83 |
|
84 |
/* |
85 |
- * Returns a pointer to the keyboard Binding with the specified modifiers and |
86 |
+ * Returns a pointer to the Binding with the specified modifiers and |
87 |
* keycode or NULL if no such binding exists. |
88 |
* |
89 |
*/ |
90 |
-Binding *get_keyboard_binding(uint16_t modifiers, bool key_release, xcb_keycode_t keycode) { |
91 |
+static Binding *get_binding(uint16_t modifiers, bool is_release, uint16_t input_code, input_type_t input_type) { |
92 |
Binding *bind; |
93 |
|
94 |
- if (!key_release) { |
95 |
- /* On a KeyPress event, we first reset all |
96 |
- * B_UPON_KEYRELEASE_IGNORE_MODS bindings back to B_UPON_KEYRELEASE */ |
97 |
+ if (!is_release) { |
98 |
+ /* On a press event, we first reset all B_UPON_KEYRELEASE_IGNORE_MODS |
99 |
+ * bindings back to B_UPON_KEYRELEASE */ |
100 |
TAILQ_FOREACH(bind, bindings, bindings) { |
101 |
- if (bind->input_type != B_KEYBOARD) |
102 |
+ if (bind->input_type != input_type) |
103 |
continue; |
104 |
if (bind->release == B_UPON_KEYRELEASE_IGNORE_MODS) |
105 |
bind->release = B_UPON_KEYRELEASE; |
106 |
@@ -145,37 +145,37 @@ Binding *get_keyboard_binding(uint16_t modifiers, bool key_release, xcb_keycode_ |
107 |
/* First compare the modifiers (unless this is a |
108 |
* B_UPON_KEYRELEASE_IGNORE_MODS binding and this is a KeyRelease |
109 |
* event) */ |
110 |
- if (bind->input_type != B_KEYBOARD) |
111 |
+ if (bind->input_type != input_type) |
112 |
continue; |
113 |
if (bind->mods != modifiers && |
114 |
(bind->release != B_UPON_KEYRELEASE_IGNORE_MODS || |
115 |
- !key_release)) |
116 |
+ !is_release)) |
117 |
continue; |
118 |
|
119 |
- /* If a symbol was specified by the user, we need to look in |
120 |
- * the array of translated keycodes for the event’s keycode */ |
121 |
- if (bind->symbol != NULL) { |
122 |
+ /* For keyboard bindings where a symbol was specified by the user, we |
123 |
+ * need to look in the array of translated keycodes for the event’s |
124 |
+ * keycode */ |
125 |
+ if (input_type == B_KEYBOARD && bind->symbol != NULL) { |
126 |
if (memmem(bind->translated_to, |
127 |
bind->number_keycodes * sizeof(xcb_keycode_t), |
128 |
- &keycode, sizeof(xcb_keycode_t)) == NULL) |
129 |
+ &input_code, sizeof(xcb_keycode_t)) == NULL) |
130 |
continue; |
131 |
} else { |
132 |
/* This case is easier: The user specified a keycode */ |
133 |
- if (bind->keycode != keycode) |
134 |
+ if (bind->keycode != input_code) |
135 |
continue; |
136 |
} |
137 |
|
138 |
- /* If this keybinding is a KeyRelease binding, it matches the key which |
139 |
- * the user pressed. We therefore mark it as |
140 |
- * B_UPON_KEYRELEASE_IGNORE_MODS for later, so that the user can |
141 |
- * release the modifiers before the actual key and the KeyRelease will |
142 |
- * still be matched. */ |
143 |
- if (bind->release == B_UPON_KEYRELEASE && !key_release) |
144 |
+ /* If this binding is a release binding, it matches the key which the |
145 |
+ * user pressed. We therefore mark it as B_UPON_KEYRELEASE_IGNORE_MODS |
146 |
+ * for later, so that the user can release the modifiers before the |
147 |
+ * actual key or button and the release event will still be matched. */ |
148 |
+ if (bind->release == B_UPON_KEYRELEASE && !is_release) |
149 |
bind->release = B_UPON_KEYRELEASE_IGNORE_MODS; |
150 |
|
151 |
- /* Check if the binding is for a KeyPress or a KeyRelease event */ |
152 |
- if ((bind->release == B_UPON_KEYPRESS && key_release) || |
153 |
- (bind->release >= B_UPON_KEYRELEASE && !key_release)) |
154 |
+ /* Check if the binding is for a press or a release event */ |
155 |
+ if ((bind->release == B_UPON_KEYPRESS && is_release) || |
156 |
+ (bind->release >= B_UPON_KEYRELEASE && !is_release)) |
157 |
continue; |
158 |
|
159 |
break; |
160 |
@@ -185,6 +185,59 @@ Binding *get_keyboard_binding(uint16_t modifiers, bool key_release, xcb_keycode_ |
161 |
} |
162 |
|
163 |
/* |
164 |
+ * Returns a pointer to the Binding that matches the given xcb button or key |
165 |
+ * event or NULL if no such binding exists. |
166 |
+ * |
167 |
+ */ |
168 |
+Binding *get_binding_from_xcb_event(xcb_generic_event_t *event) { |
169 |
+ bool is_release = (event->response_type == XCB_KEY_RELEASE |
170 |
+ || event->response_type == XCB_BUTTON_RELEASE); |
171 |
+ |
172 |
+ input_type_t input_type = ((event->response_type == XCB_BUTTON_RELEASE |
173 |
+ || event->response_type == XCB_BUTTON_PRESS) |
174 |
+ ? B_MOUSE |
175 |
+ : B_KEYBOARD); |
176 |
+ |
177 |
+ uint16_t event_state = ((xcb_key_press_event_t *)event)->state; |
178 |
+ uint16_t event_detail = ((xcb_key_press_event_t *)event)->detail; |
179 |
+ |
180 |
+ /* Remove the numlock bit, all other bits are modifiers we can bind to */ |
181 |
+ uint16_t state_filtered = event_state & ~(xcb_numlock_mask | XCB_MOD_MASK_LOCK); |
182 |
+ DLOG("(removed numlock, state = %d)\n", state_filtered); |
183 |
+ /* Only use the lower 8 bits of the state (modifier masks) so that mouse |
184 |
+ * button masks are filtered out */ |
185 |
+ state_filtered &= 0xFF; |
186 |
+ DLOG("(removed upper 8 bits, state = %d)\n", state_filtered); |
187 |
+ |
188 |
+ if (xkb_current_group == XkbGroup2Index) |
189 |
+ state_filtered |= BIND_MODE_SWITCH; |
190 |
+ |
191 |
+ DLOG("(checked mode_switch, state %d)\n", state_filtered); |
192 |
+ |
193 |
+ /* Find the binding */ |
194 |
+ Binding *bind = get_binding(state_filtered, is_release, event_detail, input_type); |
195 |
+ |
196 |
+ /* No match? Then the user has Mode_switch enabled but does not have a |
197 |
+ * specific keybinding. Fall back to the default keybindings (without |
198 |
+ * Mode_switch). Makes it much more convenient for users of a hybrid |
199 |
+ * layout (like ru). */ |
200 |
+ if (bind == NULL) { |
201 |
+ state_filtered &= ~(BIND_MODE_SWITCH); |
202 |
+ DLOG("no match, new state_filtered = %d\n", state_filtered); |
203 |
+ if ((bind = get_binding(state_filtered, is_release, event_detail, input_type)) == NULL) { |
204 |
+ /* This is not a real error since we can have release and |
205 |
+ * non-release bindings. On a press event for which there is only a |
206 |
+ * !release-binding, but no release-binding, the corresponding |
207 |
+ * release event will trigger this. No problem, though. */ |
208 |
+ DLOG("Could not lookup key binding (modifiers %d, keycode %d)\n", |
209 |
+ state_filtered, event_detail); |
210 |
+ } |
211 |
+ } |
212 |
+ |
213 |
+ return bind; |
214 |
+} |
215 |
+ |
216 |
+/* |
217 |
* Translates keysymbols to keycodes for all bindings which use keysyms. |
218 |
* |
219 |
*/ |
b/src/key_press.c
224 |
@@ -70,40 +70,11 @@ void handle_key_press(xcb_key_press_event_t *event) { |
225 |
|
226 |
DLOG("%s %d, state raw = %d\n", (key_release ? "KeyRelease" : "KeyPress"), event->detail, event->state); |
227 |
|
228 |
- /* Remove the numlock bit, all other bits are modifiers we can bind to */ |
229 |
- uint16_t state_filtered = event->state & ~(xcb_numlock_mask | XCB_MOD_MASK_LOCK); |
230 |
- DLOG("(removed numlock, state = %d)\n", state_filtered); |
231 |
- /* Only use the lower 8 bits of the state (modifier masks) so that mouse |
232 |
- * button masks are filtered out */ |
233 |
- state_filtered &= 0xFF; |
234 |
- DLOG("(removed upper 8 bits, state = %d)\n", state_filtered); |
235 |
- |
236 |
- if (xkb_current_group == XkbGroup2Index) |
237 |
- state_filtered |= BIND_MODE_SWITCH; |
238 |
- |
239 |
- DLOG("(checked mode_switch, state %d)\n", state_filtered); |
240 |
- |
241 |
- /* Find the binding */ |
242 |
- Binding *bind = get_keyboard_binding(state_filtered, key_release, event->detail); |
243 |
- |
244 |
- /* No match? Then the user has Mode_switch enabled but does not have a |
245 |
- * specific keybinding. Fall back to the default keybindings (without |
246 |
- * Mode_switch). Makes it much more convenient for users of a hybrid |
247 |
- * layout (like us, ru). */ |
248 |
- if (bind == NULL) { |
249 |
- state_filtered &= ~(BIND_MODE_SWITCH); |
250 |
- DLOG("no match, new state_filtered = %d\n", state_filtered); |
251 |
- if ((bind = get_keyboard_binding(state_filtered, key_release, event->detail)) == NULL) { |
252 |
- /* This is not a real error since we can have release and |
253 |
- * non-release keybindings. On a KeyPress event for which there is |
254 |
- * only a !release-binding, but no release-binding, the |
255 |
- * corresponding KeyRelease event will trigger this. No problem, |
256 |
- * though. */ |
257 |
- DLOG("Could not lookup key binding (modifiers %d, keycode %d)\n", |
258 |
- state_filtered, event->detail); |
259 |
- return; |
260 |
- } |
261 |
- } |
262 |
+ Binding *bind = get_binding_from_xcb_event((xcb_generic_event_t *)event); |
263 |
+ |
264 |
+ /* if we couldn't find a binding, we are done */ |
265 |
+ if (bind == NULL) |
266 |
+ return; |
267 |
|
268 |
char *command_copy = sstrdup(bind->command); |
269 |
struct CommandResult *command_output = parse_command(command_copy); |