i3 - improved tiling WM


Abstract binding functions to bindings.h

Patch status: superseded

Patch by Tony Crisci

Long description:

Adds the following new functions:

* `initialize_bindings`
* `configure_binding`
* `run_binding`

Abstracts these existing functions with minor changes:

* Renames `get_binding` to `get_keyboard_binding` and switches the
parameters `keycode` and `release`.
* Renames `grab_keys`to `grab_bound_keys`.
* Renames `translate_keysyms` to `translate_binding_keysyms`.
* Renames `switch_mode` to `switch_binding_mode`.
* Renames `check_for_duplicate_bindings` to `validate_bindings`

Additionally makes the modes structure and list of modes private to
bindings.c, and makes `modifiers_from_str` public.

To apply this patch, use:
curl http://cr.i3wm.org/patch/382/raw.patch | git am

b/include/all.h

44
@@ -79,6 +79,7 @@
45
 #include "scratchpad.h"
46
 #include "commands.h"
47
 #include "commands_parser.h"
48
+#include "bindings.h"
49
 #include "config_directives.h"
50
 #include "config_parser.h"
51
 #include "fake_outputs.h"

b/include/bindings.h

57
@@ -0,0 +1,74 @@
58
+/*
59
+ * vim:ts=4:sw=4:expandtab
60
+ *
61
+ * i3 - an improved dynamic tiling window manager
62
+ * © 2009-2014 Michael Stapelberg and contributors (see also: LICENSE)
63
+ *
64
+ * bindings.h: Functions for configuring, finding, and running bindings.
65
+ *
66
+ */
67
+#pragma once
68
+
69
+/**
70
+ * The name of the default mode.
71
+ *
72
+ */
73
+#define DEFAULT_BINDING_MODE "default"
74
+
75
+/**
76
+ * Clears any existing modes and bindings and initializes the data structures.
77
+ * To be called before parsing or reloading the config.
78
+ *
79
+ */
80
+void initialize_bindings(void);
81
+
82
+/**
83
+ * Adds a binding from config parameters given as strings and returns a
84
+ * pointer to the binding structure. Returns NULL if the input code could not
85
+ * be parsed.
86
+ *
87
+ */
88
+Binding *configure_binding(const char *bindtype, const char *modifiers, const char *input_code, const char *release, const char *command, const char *mode);
89
+
90
+/**
91
+ * Gets the keyboard binding with the specified modifiers and keycode. Returns
92
+ * NULL if no such binding exists.
93
+ *
94
+ */
95
+Binding *get_keyboard_binding(uint16_t modifiers, uint32_t keycode, bool release);
96
+
97
+/**
98
+ * Runs the specified binding and returns the `CommandResult`.
99
+ *
100
+ */
101
+struct CommandResult *run_binding(Binding *binding);
102
+
103
+
104
+/**
105
+ * Translates keysymbols to keycodes for all bindings which use keysyms.
106
+ *
107
+ */
108
+void translate_binding_keysyms(void);
109
+
110
+/**
111
+ * Grabs the keys for which bindings exist. The server will send us keypress
112
+ * events for those keycodes.
113
+ *
114
+ */
115
+void grab_bound_keys(xcb_connection_t *conn, bool bind_mode_switch);
116
+
117
+/**
118
+ * Changes the set of currently active bindings to those that were configured
119
+ * in the specified mode if the mode exists.
120
+ *
121
+ */
122
+void switch_binding_mode(const char *new_mode);
123
+
124
+/**
125
+ * Checks for duplicate bindings (the same keycode, keysym, or button is
126
+ * configured more than once). If a duplicate binding is found, a message is
127
+ * printed to stderr and the has_errors variable is set to true, which will
128
+ * start i3-nagbar.
129
+ *
130
+ */
131
+void validate_bindings(struct context *context);

b/include/config.h

136
@@ -21,7 +21,6 @@ typedef struct Config Config;
137
 typedef struct Barconfig Barconfig;
138
 extern char *current_configpath;
139
 extern Config config;
140
-extern SLIST_HEAD(modes_head, Mode) modes;
141
 extern TAILQ_HEAD(barconfig_head, Barconfig) barconfigs;
142
 
143
 /**
144
@@ -70,19 +69,6 @@ struct Variable {
145
 };
146
 
147
 /**
148
- * The configuration file can contain multiple sets of bindings. Apart from the
149
- * default set (name == "default"), you can specify other sets and change the
150
- * currently active set of bindings by using the "mode <name>" command.
151
- *
152
- */
153
-struct Mode {
154
-    char *name;
155
-    struct bindings_head *bindings;
156
-
157
-    SLIST_ENTRY(Mode) modes;
158
-};
159
-
160
-/**
161
  * Holds part of the configuration (the part which is not already in dedicated
162
  * structures in include/data.h).
163
  *
164
@@ -340,13 +326,6 @@ void switch_mode(const char *new_mode);
165
  */void update_barconfig();
166
 
167
 /**
168
- * Returns a pointer to the Binding with the specified modifiers and keycode
169
- * or NULL if no such binding exists.
170
- *
171
- */
172
-Binding *get_binding(uint16_t modifiers, bool key_release, xcb_keycode_t keycode);
173
-
174
-/**
175
  * Kills the configerror i3-nagbar process, if any.
176
  *
177
  * Called when reloading/restarting.

b/include/config_directives.h

182
@@ -11,6 +11,12 @@
183
 
184
 #include "config_parser.h"
185
 
186
+/**
187
+ * A utility function to convert a string of modifiers to the corresponding bit
188
+ * mask.
189
+ */
190
+uint32_t modifiers_from_str(const char *str);
191
+
192
 /** The beginning of the prototype for every cfg_ function. */
193
 #define I3_CFG Match *current_match, struct ConfigResult *result
194
 

b/src/bindings.c

200
@@ -0,0 +1,364 @@
201
+/*
202
+ * vim:ts=4:sw=4:expandtab
203
+ *
204
+ * i3 - an improved dynamic tiling window manager
205
+ * © 2009-2014 Michael Stapelberg and contributors (see also: LICENSE)
206
+ *
207
+ * binding.c: Functions for configuring, finding and, running bindings.
208
+ */
209
+#include "all.h"
210
+
211
+/* We need Xlib for XStringToKeysym */
212
+#include <X11/Xlib.h>
213
+
214
+/*
215
+ * The configuration file can contain multiple sets of bindings. Apart from the
216
+ * default set, you can specify other sets and change the currently active set
217
+ * of bindings by using the "mode <name>" command.
218
+ *
219
+ */
220
+static SLIST_HEAD(modes_head, Mode) modes;
221
+
222
+struct Mode {
223
+    char *name;
224
+    struct bindings_head *bindings;
225
+
226
+    SLIST_ENTRY(Mode) modes;
227
+};
228
+
229
+/*
230
+ * Returns the mode specified by `name` or creates a new mode and adds it to
231
+ * the list of modes.
232
+ */
233
+static struct Mode *mode_from_name(const char *name) {
234
+    struct Mode *mode;
235
+    SLIST_FOREACH(mode, &modes, modes) {
236
+        if (strcmp(mode->name, name) == 0)
237
+            break;
238
+    }
239
+
240
+    if (mode == NULL) {
241
+        mode = scalloc(sizeof(struct Mode));
242
+        mode->name = sstrdup(name);
243
+        mode->bindings = scalloc(sizeof(struct bindings_head));
244
+        TAILQ_INIT(mode->bindings);
245
+        SLIST_INSERT_HEAD(&modes, mode, modes);
246
+    }
247
+
248
+    return mode;
249
+}
250
+
251
+/*
252
+ * Destroys all bindings and all modes.
253
+ */
254
+static void clear_bindings(void) {
255
+    struct Mode *mode;
256
+    Binding *bind;
257
+    while (!SLIST_EMPTY(&modes)) {
258
+        mode = SLIST_FIRST(&modes);
259
+        FREE(mode->name);
260
+
261
+        /* Clear the old binding list */
262
+        bindings = mode->bindings;
263
+        while (!TAILQ_EMPTY(bindings)) {
264
+            bind = TAILQ_FIRST(bindings);
265
+            TAILQ_REMOVE(bindings, bind, bindings);
266
+            FREE(bind->translated_to);
267
+            FREE(bind->command);
268
+            FREE(bind);
269
+        }
270
+        FREE(bindings);
271
+        SLIST_REMOVE(&modes, mode, Mode, modes);
272
+    }
273
+}
274
+
275
+/*
276
+ * Clears any existing modes and bindings and initializes the data structures.
277
+ * To be called before parsing or reloading the config.
278
+ *
279
+ */
280
+void initialize_bindings() {
281
+    if (bindings != NULL)
282
+        clear_bindings();
283
+
284
+    SLIST_INIT(&modes);
285
+    struct Mode *default_mode = mode_from_name(DEFAULT_BINDING_MODE);
286
+    bindings = default_mode->bindings;
287
+}
288
+
289
+/*
290
+ * Adds a binding from config parameters given as strings and returns a
291
+ * pointer to the binding structure. Returns NULL if the input code could not
292
+ * be parsed.
293
+ *
294
+ */
295
+Binding *configure_binding(const char *bindtype, const char *modifiers, const char *input_code, const char *release, const char *command, const char *modename) {
296
+    Binding *new_binding = scalloc(sizeof(Binding));
297
+    DLOG("bindtype %s, modifiers %s, input code %s, release %s\n", bindtype, modifiers, input_code, release);
298
+    new_binding->release = (release != NULL ? B_UPON_KEYRELEASE : B_UPON_KEYPRESS);
299
+    if (strcmp(bindtype, "bindsym") == 0) {
300
+        new_binding->symbol = sstrdup(input_code);
301
+    } else {
302
+        // TODO: strtol with proper error handling
303
+        new_binding->keycode = atoi(input_code);
304
+        if (new_binding->keycode == 0) {
305
+            ELOG("Could not parse \"%s\" as an input code, ignoring this binding.\n", input_code);
306
+            return NULL;
307
+        }
308
+    }
309
+    new_binding->mods = modifiers_from_str(modifiers);
310
+    new_binding->command = sstrdup(command);
311
+    new_binding->input_type = B_KEYBOARD;
312
+
313
+    struct Mode *mode = mode_from_name(modename);
314
+    TAILQ_INSERT_TAIL(mode->bindings, new_binding, bindings);
315
+
316
+    return new_binding;
317
+}
318
+
319
+/*
320
+ * Gets the keyboard binding with the specified modifiers and keycode. Returns
321
+ * NULL if no such binding exists.
322
+ *
323
+ */
324
+Binding *get_keyboard_binding(uint16_t modifiers, uint32_t keycode, bool key_release) {
325
+    Binding *bind;
326
+
327
+    if (!key_release) {
328
+        /* On a KeyPress event, we first reset all
329
+         * B_UPON_KEYRELEASE_IGNORE_MODS bindings back to B_UPON_KEYRELEASE */
330
+        TAILQ_FOREACH(bind, bindings, bindings) {
331
+            if (bind->release == B_UPON_KEYRELEASE_IGNORE_MODS)
332
+                bind->release = B_UPON_KEYRELEASE;
333
+        }
334
+    }
335
+
336
+    TAILQ_FOREACH(bind, bindings, bindings) {
337
+        /* First compare the modifiers (unless this is a
338
+         * B_UPON_KEYRELEASE_IGNORE_MODS binding and this is a KeyRelease
339
+         * event) */
340
+        if (bind->mods != modifiers &&
341
+            (bind->release != B_UPON_KEYRELEASE_IGNORE_MODS ||
342
+             !key_release))
343
+            continue;
344
+
345
+        /* If a symbol was specified by the user, we need to look in
346
+         * the array of translated keycodes for the event’s keycode */
347
+        if (bind->symbol != NULL) {
348
+            if (memmem(bind->translated_to,
349
+                       bind->number_keycodes * sizeof(xcb_keycode_t),
350
+                       &keycode, sizeof(xcb_keycode_t)) == NULL)
351
+                continue;
352
+        } else {
353
+            /* This case is easier: The user specified a keycode */
354
+            if (bind->keycode != keycode)
355
+                continue;
356
+        }
357
+
358
+        /* If this keybinding is a KeyRelease binding, it matches the key which
359
+         * the user pressed. We therefore mark it as
360
+         * B_UPON_KEYRELEASE_IGNORE_MODS for later, so that the user can
361
+         * release the modifiers before the actual key and the KeyRelease will
362
+         * still be matched. */
363
+        if (bind->release == B_UPON_KEYRELEASE && !key_release)
364
+            bind->release = B_UPON_KEYRELEASE_IGNORE_MODS;
365
+
366
+        /* Check if the binding is for a KeyPress or a KeyRelease event */
367
+        if ((bind->release == B_UPON_KEYPRESS && key_release) ||
368
+            (bind->release >= B_UPON_KEYRELEASE && !key_release))
369
+            continue;
370
+
371
+        break;
372
+    }
373
+
374
+    return (bind == TAILQ_END(bindings) ? NULL : bind);
375
+}
376
+
377
+/*
378
+ * Runs the specified binding and returns the `CommandResult`.
379
+ *
380
+ */
381
+struct CommandResult *run_binding(Binding *binding) {
382
+    char *command_copy = sstrdup(binding->command);
383
+    struct CommandResult *command_output = parse_command(command_copy);
384
+    free(command_copy);
385
+
386
+    if (command_output->needs_tree_render)
387
+        tree_render();
388
+
389
+    return command_output;
390
+}
391
+
392
+
393
+static void grab_keycode_for_binding(xcb_connection_t *conn, Binding *bind, uint32_t keycode) {
394
+    DLOG("Grabbing %d with modifiers %d (with mod_mask_lock %d)\n", keycode, bind->mods, bind->mods | XCB_MOD_MASK_LOCK);
395
+    /* Grab the key in all combinations */
396
+    #define GRAB_KEY(modifier) \
397
+        do { \
398
+            xcb_grab_key(conn, 0, root, modifier, keycode, \
399
+                         XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC); \
400
+        } while (0)
401
+    int mods = bind->mods;
402
+    if ((bind->mods & BIND_MODE_SWITCH) != 0) {
403
+        mods &= ~BIND_MODE_SWITCH;
404
+        if (mods == 0)
405
+            mods = XCB_MOD_MASK_ANY;
406
+    }
407
+    GRAB_KEY(mods);
408
+    GRAB_KEY(mods | xcb_numlock_mask);
409
+    GRAB_KEY(mods | XCB_MOD_MASK_LOCK);
410
+    GRAB_KEY(mods | xcb_numlock_mask | XCB_MOD_MASK_LOCK);
411
+}
412
+
413
+/*
414
+ * Translates keysymbols to keycodes for all bindings which use keysyms.
415
+ *
416
+ */
417
+void translate_binding_keysyms(void) {
418
+    Binding *bind;
419
+    xcb_keysym_t keysym;
420
+    int col;
421
+    xcb_keycode_t i,
422
+                  min_keycode = xcb_get_setup(conn)->min_keycode,
423
+                  max_keycode = xcb_get_setup(conn)->max_keycode;
424
+
425
+    TAILQ_FOREACH(bind, bindings, bindings) {
426
+        if (bind->keycode > 0)
427
+            continue;
428
+
429
+        /* We need to translate the symbol to a keycode */
430
+        keysym = XStringToKeysym(bind->symbol);
431
+        if (keysym == NoSymbol) {
432
+            ELOG("Could not translate string to key symbol: \"%s\"\n",
433
+                 bind->symbol);
434
+            continue;
435
+        }
436
+
437
+        /* Base column we use for looking up key symbols. We always consider
438
+         * the base column and the corresponding shift column, so without
439
+         * mode_switch, we look in 0 and 1, with mode_switch we look in 2 and
440
+         * 3. */
441
+        col = (bind->mods & BIND_MODE_SWITCH ? 2 : 0);
442
+
443
+        FREE(bind->translated_to);
444
+        bind->number_keycodes = 0;
445
+
446
+        for (i = min_keycode; i && i <= max_keycode; i++) {
447
+            if ((xcb_key_symbols_get_keysym(keysyms, i, col) != keysym) &&
448
+                (xcb_key_symbols_get_keysym(keysyms, i, col+1) != keysym))
449
+                continue;
450
+            bind->number_keycodes++;
451
+            bind->translated_to = srealloc(bind->translated_to,
452
+                                           (sizeof(xcb_keycode_t) *
453
+                                            bind->number_keycodes));
454
+            bind->translated_to[bind->number_keycodes-1] = i;
455
+        }
456
+
457
+        DLOG("Translated symbol \"%s\" to %d keycode\n", bind->symbol,
458
+             bind->number_keycodes);
459
+    }
460
+}
461
+
462
+/*
463
+ * Grabs the keys for which bindings exist. The server will send us keypress
464
+ * events for those keycodes.
465
+ *
466
+ */
467
+void grab_bound_keys(xcb_connection_t *conn, bool bind_mode_switch) {
468
+    Binding *bind;
469
+    TAILQ_FOREACH(bind, bindings, bindings) {
470
+        if ((bind_mode_switch && (bind->mods & BIND_MODE_SWITCH) == 0) ||
471
+            (!bind_mode_switch && (bind->mods & BIND_MODE_SWITCH) != 0))
472
+            continue;
473
+
474
+        /* The easy case: the user specified a keycode directly. */
475
+        if (bind->keycode > 0) {
476
+            grab_keycode_for_binding(conn, bind, bind->keycode);
477
+            continue;
478
+        }
479
+
480
+        xcb_keycode_t *walk = bind->translated_to;
481
+        for (uint32_t i = 0; i < bind->number_keycodes; i++)
482
+            grab_keycode_for_binding(conn, bind, *walk++);
483
+    }
484
+}
485
+
486
+/*
487
+ * Changes the set of currently active bindings to those that were configured
488
+ * in the specified mode if the mode exists.
489
+ *
490
+ */
491
+void switch_binding_mode(const char *new_mode) {
492
+    struct Mode *mode;
493
+
494
+    LOG("Switching to mode %s\n", new_mode);
495
+
496
+    SLIST_FOREACH(mode, &modes, modes) {
497
+        if (strcasecmp(mode->name, new_mode) != 0)
498
+            continue;
499
+
500
+        ungrab_all_keys(conn);
501
+        bindings = mode->bindings;
502
+        translate_binding_keysyms();
503
+        grab_bound_keys(conn, false);
504
+
505
+        char *event_msg;
506
+        sasprintf(&event_msg, "{\"change\":\"%s\"}", mode->name);
507
+
508
+        ipc_send_event("mode", I3_IPC_EVENT_MODE, event_msg);
509
+        FREE(event_msg);
510
+
511
+        return;
512
+    }
513
+
514
+    ELOG("ERROR: Mode not found\n");
515
+}
516
+
517
+/*
518
+ * Checks for duplicate bindings (the same keycode, keysym, or button is
519
+ * configured more than once). If a duplicate binding is found, a message is
520
+ * printed to stderr and the has_errors variable is set to true, which will
521
+ * start i3-nagbar.
522
+ *
523
+ */
524
+void validate_bindings(struct context *context) {
525
+    Binding *bind, *current;
526
+    TAILQ_FOREACH(current, bindings, bindings) {
527
+        TAILQ_FOREACH(bind, bindings, bindings) {
528
+            /* Abort when we reach the current keybinding, only check the
529
+             * bindings before */
530
+            if (bind == current)
531
+                break;
532
+
533
+            /* Check if one is using keysym while the other is using bindsym.
534
+             * If so, skip. */
535
+            /* XXX: It should be checked at a later place (when translating the
536
+             * keysym to keycodes) if there are any duplicates */
537
+            if ((bind->symbol == NULL && current->symbol != NULL) ||
538
+                (bind->symbol != NULL && current->symbol == NULL))
539
+                continue;
540
+
541
+            /* If bind is NULL, current has to be NULL, too (see above).
542
+             * If the keycodes differ, it can't be a duplicate. */
543
+            if (bind->symbol != NULL &&
544
+                strcasecmp(bind->symbol, current->symbol) != 0)
545
+                continue;
546
+
547
+            /* Check if the keycodes or modifiers are different. If so, they
548
+             * can't be duplicate */
549
+            if (bind->keycode != current->keycode ||
550
+                bind->mods != current->mods ||
551
+                bind->release != current->release)
552
+                continue;
553
+
554
+            context->has_errors = true;
555
+            if (current->keycode != 0) {
556
+                ELOG("Duplicate keybinding in config file:\n  modmask %d with keycode %d, command \"%s\"\n",
557
+                     current->mods, current->keycode, current->command);
558
+            } else {
559
+                ELOG("Duplicate keybinding in config file:\n  modmask %d with keysym %s, command \"%s\"\n",
560
+                     current->mods, current->symbol, current->command);
561
+            }
562
+        }
563
+    }
564
+}

b/src/commands.c

569
@@ -1057,7 +1057,7 @@ void cmd_unmark(I3_CMD, char *mark) {
570
  */
571
 void cmd_mode(I3_CMD, char *mode) {
572
     DLOG("mode=%s\n", mode);
573
-    switch_mode(mode);
574
+    switch_binding_mode(mode);
575
 
576
     // XXX: default reply for now, make this a better reply
577
     ysuccess(true);

b/src/config.c

582
@@ -17,7 +17,6 @@
583
 
584
 char *current_configpath = NULL;
585
 Config config;
586
-struct modes_head modes;
587
 struct barconfig_head barconfigs = TAILQ_HEAD_INITIALIZER(barconfigs);
588
 
589
 /**
590
@@ -30,186 +29,6 @@ void ungrab_all_keys(xcb_connection_t *conn) {
591
     xcb_ungrab_key(conn, XCB_GRAB_ANY, root, XCB_BUTTON_MASK_ANY);
592
 }
593
 
594
-static void grab_keycode_for_binding(xcb_connection_t *conn, Binding *bind, uint32_t keycode) {
595
-    DLOG("Grabbing %d with modifiers %d (with mod_mask_lock %d)\n", keycode, bind->mods, bind->mods | XCB_MOD_MASK_LOCK);
596
-    /* Grab the key in all combinations */
597
-    #define GRAB_KEY(modifier) \
598
-        do { \
599
-            xcb_grab_key(conn, 0, root, modifier, keycode, \
600
-                         XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC); \
601
-        } while (0)
602
-    int mods = bind->mods;
603
-    if ((bind->mods & BIND_MODE_SWITCH) != 0) {
604
-        mods &= ~BIND_MODE_SWITCH;
605
-        if (mods == 0)
606
-            mods = XCB_MOD_MASK_ANY;
607
-    }
608
-    GRAB_KEY(mods);
609
-    GRAB_KEY(mods | xcb_numlock_mask);
610
-    GRAB_KEY(mods | XCB_MOD_MASK_LOCK);
611
-    GRAB_KEY(mods | xcb_numlock_mask | XCB_MOD_MASK_LOCK);
612
-}
613
-
614
-/*
615
- * Returns a pointer to the Binding with the specified modifiers and keycode
616
- * or NULL if no such binding exists.
617
- *
618
- */
619
-Binding *get_binding(uint16_t modifiers, bool key_release, xcb_keycode_t keycode) {
620
-    Binding *bind;
621
-
622
-    if (!key_release) {
623
-        /* On a KeyPress event, we first reset all
624
-         * B_UPON_KEYRELEASE_IGNORE_MODS bindings back to B_UPON_KEYRELEASE */
625
-        TAILQ_FOREACH(bind, bindings, bindings) {
626
-            if (bind->release == B_UPON_KEYRELEASE_IGNORE_MODS)
627
-                bind->release = B_UPON_KEYRELEASE;
628
-        }
629
-    }
630
-
631
-    TAILQ_FOREACH(bind, bindings, bindings) {
632
-        /* First compare the modifiers (unless this is a
633
-         * B_UPON_KEYRELEASE_IGNORE_MODS binding and this is a KeyRelease
634
-         * event) */
635
-        if (bind->mods != modifiers &&
636
-            (bind->release != B_UPON_KEYRELEASE_IGNORE_MODS ||
637
-             !key_release))
638
-            continue;
639
-
640
-        /* If a symbol was specified by the user, we need to look in
641
-         * the array of translated keycodes for the event’s keycode */
642
-        if (bind->symbol != NULL) {
643
-            if (memmem(bind->translated_to,
644
-                       bind->number_keycodes * sizeof(xcb_keycode_t),
645
-                       &keycode, sizeof(xcb_keycode_t)) == NULL)
646
-                continue;
647
-        } else {
648
-            /* This case is easier: The user specified a keycode */
649
-            if (bind->keycode != keycode)
650
-                continue;
651
-        }
652
-
653
-        /* If this keybinding is a KeyRelease binding, it matches the key which
654
-         * the user pressed. We therefore mark it as
655
-         * B_UPON_KEYRELEASE_IGNORE_MODS for later, so that the user can
656
-         * release the modifiers before the actual key and the KeyRelease will
657
-         * still be matched. */
658
-        if (bind->release == B_UPON_KEYRELEASE && !key_release)
659
-            bind->release = B_UPON_KEYRELEASE_IGNORE_MODS;
660
-
661
-        /* Check if the binding is for a KeyPress or a KeyRelease event */
662
-        if ((bind->release == B_UPON_KEYPRESS && key_release) ||
663
-            (bind->release >= B_UPON_KEYRELEASE && !key_release))
664
-            continue;
665
-
666
-        break;
667
-    }
668
-
669
-    return (bind == TAILQ_END(bindings) ? NULL : bind);
670
-}
671
-
672
-/*
673
- * Translates keysymbols to keycodes for all bindings which use keysyms.
674
- *
675
- */
676
-void translate_keysyms(void) {
677
-    Binding *bind;
678
-    xcb_keysym_t keysym;
679
-    int col;
680
-    xcb_keycode_t i,
681
-                  min_keycode = xcb_get_setup(conn)->min_keycode,
682
-                  max_keycode = xcb_get_setup(conn)->max_keycode;
683
-
684
-    TAILQ_FOREACH(bind, bindings, bindings) {
685
-        if (bind->keycode > 0)
686
-            continue;
687
-
688
-        /* We need to translate the symbol to a keycode */
689
-        keysym = XStringToKeysym(bind->symbol);
690
-        if (keysym == NoSymbol) {
691
-            ELOG("Could not translate string to key symbol: \"%s\"\n",
692
-                 bind->symbol);
693
-            continue;
694
-        }
695
-
696
-        /* Base column we use for looking up key symbols. We always consider
697
-         * the base column and the corresponding shift column, so without
698
-         * mode_switch, we look in 0 and 1, with mode_switch we look in 2 and
699
-         * 3. */
700
-        col = (bind->mods & BIND_MODE_SWITCH ? 2 : 0);
701
-
702
-        FREE(bind->translated_to);
703
-        bind->number_keycodes = 0;
704
-
705
-        for (i = min_keycode; i && i <= max_keycode; i++) {
706
-            if ((xcb_key_symbols_get_keysym(keysyms, i, col) != keysym) &&
707
-                (xcb_key_symbols_get_keysym(keysyms, i, col+1) != keysym))
708
-                continue;
709
-            bind->number_keycodes++;
710
-            bind->translated_to = srealloc(bind->translated_to,
711
-                                           (sizeof(xcb_keycode_t) *
712
-                                            bind->number_keycodes));
713
-            bind->translated_to[bind->number_keycodes-1] = i;
714
-        }
715
-
716
-        DLOG("Translated symbol \"%s\" to %d keycode\n", bind->symbol,
717
-             bind->number_keycodes);
718
-    }
719
-}
720
-
721
-/*
722
- * Grab the bound keys (tell X to send us keypress events for those keycodes)
723
- *
724
- */
725
-void grab_all_keys(xcb_connection_t *conn, bool bind_mode_switch) {
726
-    Binding *bind;
727
-    TAILQ_FOREACH(bind, bindings, bindings) {
728
-        if ((bind_mode_switch && (bind->mods & BIND_MODE_SWITCH) == 0) ||
729
-            (!bind_mode_switch && (bind->mods & BIND_MODE_SWITCH) != 0))
730
-            continue;
731
-
732
-        /* The easy case: the user specified a keycode directly. */
733
-        if (bind->keycode > 0) {
734
-            grab_keycode_for_binding(conn, bind, bind->keycode);
735
-            continue;
736
-        }
737
-
738
-        xcb_keycode_t *walk = bind->translated_to;
739
-        for (uint32_t i = 0; i < bind->number_keycodes; i++)
740
-            grab_keycode_for_binding(conn, bind, *walk++);
741
-    }
742
-}
743
-
744
-/*
745
- * Switches the key bindings to the given mode, if the mode exists
746
- *
747
- */
748
-void switch_mode(const char *new_mode) {
749
-    struct Mode *mode;
750
-
751
-    LOG("Switching to mode %s\n", new_mode);
752
-
753
-    SLIST_FOREACH(mode, &modes, modes) {
754
-        if (strcasecmp(mode->name, new_mode) != 0)
755
-            continue;
756
-
757
-        ungrab_all_keys(conn);
758
-        bindings = mode->bindings;
759
-        translate_keysyms();
760
-        grab_all_keys(conn, false);
761
-
762
-        char *event_msg;
763
-        sasprintf(&event_msg, "{\"change\":\"%s\"}", mode->name);
764
-
765
-        ipc_send_event("mode", I3_IPC_EVENT_MODE, event_msg);
766
-        FREE(event_msg);
767
-
768
-        return;
769
-    }
770
-
771
-    ELOG("ERROR: Mode not found\n");
772
-}
773
-
774
 /*
775
  * Sends the current bar configuration as an event to all barconfig_update listeners.
776
  * This update mechnism currently only includes the hidden_state and the mode in the config.
777
@@ -344,25 +163,6 @@ void load_configuration(xcb_connection_t *conn, const char *override_configpath,
778
         /* First ungrab the keys */
779
         ungrab_all_keys(conn);
780
 
781
-        struct Mode *mode;
782
-        Binding *bind;
783
-        while (!SLIST_EMPTY(&modes)) {
784
-            mode = SLIST_FIRST(&modes);
785
-            FREE(mode->name);
786
-
787
-            /* Clear the old binding list */
788
-            bindings = mode->bindings;
789
-            while (!TAILQ_EMPTY(bindings)) {
790
-                bind = TAILQ_FIRST(bindings);
791
-                TAILQ_REMOVE(bindings, bind, bindings);
792
-                FREE(bind->translated_to);
793
-                FREE(bind->command);
794
-                FREE(bind);
795
-            }
796
-            FREE(bindings);
797
-            SLIST_REMOVE(&modes, mode, Mode, modes);
798
-        }
799
-
800
         struct Assignment *assign;
801
         while (!TAILQ_EMPTY(&assignments)) {
802
             assign = TAILQ_FIRST(&assignments);
803
@@ -424,15 +224,7 @@ void load_configuration(xcb_connection_t *conn, const char *override_configpath,
804
         free_font();
805
     }
806
 
807
-    SLIST_INIT(&modes);
808
-
809
-    struct Mode *default_mode = scalloc(sizeof(struct Mode));
810
-    default_mode->name = sstrdup("default");
811
-    default_mode->bindings = scalloc(sizeof(struct bindings_head));
812
-    TAILQ_INIT(default_mode->bindings);
813
-    SLIST_INSERT_HEAD(&modes, default_mode, modes);
814
-
815
-    bindings = default_mode->bindings;
816
+    initialize_bindings();
817
 
818
 #define REQUIRED_OPTION(name) \
819
     if (config.name == NULL) \
820
@@ -477,8 +269,8 @@ void load_configuration(xcb_connection_t *conn, const char *override_configpath,
821
     parse_configuration(override_configpath);
822
 
823
     if (reload) {
824
-        translate_keysyms();
825
-        grab_all_keys(conn, false);
826
+        translate_binding_keysyms();
827
+        grab_bound_keys(conn, false);
828
     }
829
 
830
     if (config.font.type == FONT_TYPE_NONE) {

b/src/config_directives.c

835
@@ -130,7 +130,11 @@ static bool eval_boolstr(const char *str) {
836
             strcasecmp(str, "active") == 0);
837
 }
838
 
839
-static uint32_t modifiers_from_str(const char *str) {
840
+/*
841
+ * A utility function to convert a string of modifiers to the corresponding bit
842
+ * mask.
843
+ */
844
+uint32_t modifiers_from_str(const char *str) {
845
     /* It might be better to use strtok() here, but the simpler strstr() should
846
      * do for now. */
847
     uint32_t result = 0;
848
@@ -167,24 +171,8 @@ CFGFUN(font, const char *font) {
849
 	font_pattern = sstrdup(font);
850
 }
851
 
852
-// TODO: refactor with mode_binding
853
 CFGFUN(binding, const char *bindtype, const char *modifiers, const char *key, const char *release, const char *command) {
854
-    Binding *new_binding = scalloc(sizeof(Binding));
855
-    DLOG("bindtype %s, modifiers %s, key %s, release %s\n", bindtype, modifiers, key, release);
856
-    new_binding->release = (release != NULL ? B_UPON_KEYRELEASE : B_UPON_KEYPRESS);
857
-    if (strcmp(bindtype, "bindsym") == 0) {
858
-        new_binding->symbol = sstrdup(key);
859
-    } else {
860
-        // TODO: strtol with proper error handling
861
-        new_binding->keycode = atoi(key);
862
-        if (new_binding->keycode == 0) {
863
-            ELOG("Could not parse \"%s\" as a keycode, ignoring this binding.\n", key);
864
-            return;
865
-        }
866
-    }
867
-    new_binding->mods = modifiers_from_str(modifiers);
868
-    new_binding->command = sstrdup(command);
869
-    TAILQ_INSERT_TAIL(bindings, new_binding, bindings);
870
+    configure_binding(bindtype, modifiers, key, release, command, DEFAULT_BINDING_MODE);
871
 }
872
 
873
 
874
@@ -192,39 +180,20 @@ CFGFUN(binding, const char *bindtype, const char *modifiers, const char *key, co
875
  * Mode handling
876
  ******************************************************************************/
877
 
878
-static struct bindings_head *current_bindings;
879
+static char *current_mode;
880
 
881
 CFGFUN(mode_binding, const char *bindtype, const char *modifiers, const char *key, const char *release, const char *command) {
882
-    Binding *new_binding = scalloc(sizeof(Binding));
883
-    DLOG("bindtype %s, modifiers %s, key %s, release %s\n", bindtype, modifiers, key, release);
884
-    new_binding->release = (release != NULL ? B_UPON_KEYRELEASE : B_UPON_KEYPRESS);
885
-    if (strcmp(bindtype, "bindsym") == 0) {
886
-        new_binding->symbol = sstrdup(key);
887
-    } else {
888
-        // TODO: strtol with proper error handling
889
-        new_binding->keycode = atoi(key);
890
-        if (new_binding->keycode == 0) {
891
-            ELOG("Could not parse \"%s\" as a keycode, ignoring this binding.\n", key);
892
-            return;
893
-        }
894
-    }
895
-    new_binding->mods = modifiers_from_str(modifiers);
896
-    new_binding->command = sstrdup(command);
897
-    TAILQ_INSERT_TAIL(current_bindings, new_binding, bindings);
898
+    configure_binding(bindtype, modifiers, key, release, command, current_mode);
899
 }
900
 
901
 CFGFUN(enter_mode, const char *modename) {
902
-    if (strcasecmp(modename, "default") == 0) {
903
-        ELOG("You cannot use the name \"default\" for your mode\n");
904
+    if (strcasecmp(modename,DEFAULT_BINDING_MODE) == 0) {
905
+        ELOG("You cannot use the name %s for your mode\n", DEFAULT_BINDING_MODE);
906
         exit(1);
907
     }
908
     DLOG("\t now in mode %s\n", modename);
909
-    struct Mode *mode = scalloc(sizeof(struct Mode));
910
-    mode->name = sstrdup(modename);
911
-    mode->bindings = scalloc(sizeof(struct bindings_head));
912
-    TAILQ_INIT(mode->bindings);
913
-    current_bindings = mode->bindings;
914
-    SLIST_INSERT_HEAD(&modes, mode, modes);
915
+    FREE(current_mode);
916
+    current_mode = sstrdup(modename);
917
 }
918
 
919
 CFGFUN(exec, const char *exectype, const char *no_startup_id, const char *command) {

b/src/config_parser.c

924
@@ -844,55 +844,6 @@ static char *migrate_config(char *input, off_t size) {
925
 }
926
 
927
 /*
928
- * Checks for duplicate key bindings (the same keycode or keysym is configured
929
- * more than once). If a duplicate binding is found, a message is printed to
930
- * stderr and the has_errors variable is set to true, which will start
931
- * i3-nagbar.
932
- *
933
- */
934
-static void check_for_duplicate_bindings(struct context *context) {
935
-    Binding *bind, *current;
936
-    TAILQ_FOREACH(current, bindings, bindings) {
937
-        TAILQ_FOREACH(bind, bindings, bindings) {
938
-            /* Abort when we reach the current keybinding, only check the
939
-             * bindings before */
940
-            if (bind == current)
941
-                break;
942
-
943
-            /* Check if one is using keysym while the other is using bindsym.
944
-             * If so, skip. */
945
-            /* XXX: It should be checked at a later place (when translating the
946
-             * keysym to keycodes) if there are any duplicates */
947
-            if ((bind->symbol == NULL && current->symbol != NULL) ||
948
-                (bind->symbol != NULL && current->symbol == NULL))
949
-                continue;
950
-
951
-            /* If bind is NULL, current has to be NULL, too (see above).
952
-             * If the keycodes differ, it can't be a duplicate. */
953
-            if (bind->symbol != NULL &&
954
-                strcasecmp(bind->symbol, current->symbol) != 0)
955
-                continue;
956
-
957
-            /* Check if the keycodes or modifiers are different. If so, they
958
-             * can't be duplicate */
959
-            if (bind->keycode != current->keycode ||
960
-                bind->mods != current->mods ||
961
-                bind->release != current->release)
962
-                continue;
963
-
964
-            context->has_errors = true;
965
-            if (current->keycode != 0) {
966
-                ELOG("Duplicate keybinding in config file:\n  modmask %d with keycode %d, command \"%s\"\n",
967
-                     current->mods, current->keycode, current->command);
968
-            } else {
969
-                ELOG("Duplicate keybinding in config file:\n  modmask %d with keysym %s, command \"%s\"\n",
970
-                     current->mods, current->symbol, current->command);
971
-            }
972
-        }
973
-    }
974
-}
975
-
976
-/*
977
  * Parses the given file by first replacing the variables, then calling
978
  * parse_config and possibly launching i3-nagbar.
979
  *
980
@@ -1056,7 +1007,7 @@ void parse_file(const char *f) {
981
     struct ConfigResult *config_output = parse_config(new, context);
982
     yajl_gen_free(config_output->json_gen);
983
 
984
-    check_for_duplicate_bindings(context);
985
+    validate_bindings(context);
986
 
987
     if (context->has_errors || context->has_warnings) {
988
         ELOG("FYI: You are using i3 version " I3_VERSION "\n");

b/src/handlers.c

993
@@ -259,8 +259,8 @@ static void handle_mapping_notify(xcb_mapping_notify_event_t *event) {
994
     xcb_numlock_mask = aio_get_mod_mask_for(XCB_NUM_LOCK, keysyms);
995
 
996
     ungrab_all_keys(conn);
997
-    translate_keysyms();
998
-    grab_all_keys(conn, false);
999
+    translate_binding_keysyms();
1000
+    grab_bound_keys(conn, false);
1001
 
1002
     return;
1003
 }

b/src/key_press.c

1008
@@ -84,7 +84,7 @@ void handle_key_press(xcb_key_press_event_t *event) {
1009
     DLOG("(checked mode_switch, state %d)\n", state_filtered);
1010
 
1011
     /* Find the binding */
1012
-    Binding *bind = get_binding(state_filtered, key_release, event->detail);
1013
+    Binding *bind = get_keyboard_binding(state_filtered, event->detail, key_release);
1014
 
1015
     /* No match? Then the user has Mode_switch enabled but does not have a
1016
      * specific keybinding. Fall back to the default keybindings (without
1017
@@ -93,7 +93,7 @@ void handle_key_press(xcb_key_press_event_t *event) {
1018
     if (bind == NULL) {
1019
         state_filtered &= ~(BIND_MODE_SWITCH);
1020
         DLOG("no match, new state_filtered = %d\n", state_filtered);
1021
-        if ((bind = get_binding(state_filtered, key_release, event->detail)) == NULL) {
1022
+        if ((bind = get_keyboard_binding(state_filtered, event->detail, key_release)) == NULL) {
1023
             /* This is not a real error since we can have release and
1024
              * non-release keybindings. On a KeyPress event for which there is
1025
              * only a !release-binding, but no release-binding, the
1026
@@ -105,12 +105,7 @@ void handle_key_press(xcb_key_press_event_t *event) {
1027
         }
1028
     }
1029
 
1030
-    char *command_copy = sstrdup(bind->command);
1031
-    struct CommandResult *command_output = parse_command(command_copy);
1032
-    free(command_copy);
1033
-
1034
-    if (command_output->needs_tree_render)
1035
-        tree_render();
1036
+    struct CommandResult *command_output = run_binding(bind);
1037
 
1038
     /* We parse the JSON reply to figure out whether there was an error
1039
      * ("success" being false in on of the returned dictionaries). */

b/src/main.c

1044
@@ -206,13 +206,13 @@ static void xkb_got_event(EV_P_ struct ev_io *w, int revents) {
1045
 
1046
         if (ev.state.group == XkbGroup2Index) {
1047
             DLOG("Mode_switch enabled\n");
1048
-            grab_all_keys(conn, true);
1049
+            grab_bound_keys(conn, true);
1050
         }
1051
 
1052
         if (ev.state.group == XkbGroup1Index) {
1053
             DLOG("Mode_switch disabled\n");
1054
             ungrab_all_keys(conn);
1055
-            grab_all_keys(conn, false);
1056
+            grab_bound_keys(conn, false);
1057
         }
1058
     }
1059
 
1060
@@ -227,8 +227,8 @@ static void xkb_got_event(EV_P_ struct ev_io *w, int revents) {
1061
 
1062
     ungrab_all_keys(conn);
1063
     DLOG("Re-grabbing...\n");
1064
-    translate_keysyms();
1065
-    grab_all_keys(conn, (xkb_current_group == XkbGroup2Index));
1066
+    translate_binding_keysyms();
1067
+    grab_bound_keys(conn, (xkb_current_group == XkbGroup2Index));
1068
     DLOG("Done\n");
1069
 }
1070
 
1071
@@ -663,8 +663,8 @@ int main(int argc, char *argv[]) {
1072
 
1073
     xcb_numlock_mask = aio_get_mod_mask_for(XCB_NUM_LOCK, keysyms);
1074
 
1075
-    translate_keysyms();
1076
-    grab_all_keys(conn, false);
1077
+    translate_binding_keysyms();
1078
+    grab_bound_keys(conn, false);
1079
 
1080
     bool needs_tree_init = true;
1081
     if (layout_path) {

b/src/workspace.c

1086
@@ -116,7 +116,9 @@ Con *create_workspace_on_output(Output *output, Con *content) {
1087
     Con *ws = con_new(NULL, NULL);
1088
     ws->type = CT_WORKSPACE;
1089
 
1090
-    /* try the configured workspace bindings first to find a free name */
1091
+    /* try the configured workspace bindings first to find a free name.
1092
+    *  TODO: try to figure out a way to do this without directly using the list
1093
+    *  of bindings.*/
1094
     Binding *bind;
1095
     TAILQ_FOREACH(bind, bindings, bindings) {
1096
         DLOG("binding with command %s\n", bind->command);