i3 - improved tiling WM


Implement the ipc 'binding' event

Patch status: needinfo

Patch by Tony Crisci

Long description:

The binding event will be triggered when a binding is run as a result of
some a user action. The binding event has the following properties:

change: (str) Currently this will only be "run" but may be expanded in
the future. Included for consistency with other events.

binding: (map) the serialized binding

The "binding" member will have these properties:

input_type: (str) either "keyboard" or "mouse"

input_code: (int) the xcb keycode of the keyboard binding if it was
provided or the mouse button if it is a mouse binding.

symbol: (str) the string representation of the input code

command: (str) the bound command

mods: (list of str) a list of the modifiers that were pressed as string
symbols

fixes #1210

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

b/include/bindings.h

41
@@ -48,3 +48,8 @@ void translate_keysyms(void);
42
  *
43
  */
44
 void switch_mode(const char *new_mode);
45
+
46
+/**
47
+ * Runs the given binding and returns a CommandResult
48
+ */
49
+struct CommandResult *run_binding(Binding *bind);

b/include/i3/ipc.h

54
@@ -100,3 +100,6 @@ typedef struct i3_ipc_header {
55
 
56
 /** Bar config update will be triggered to update the bar config */
57
 #define I3_IPC_EVENT_BARCONFIG_UPDATE           (I3_IPC_EVENT_MASK | 4)
58
+
59
+/** The binding event will be triggered when bindings run */
60
+#define I3_IPC_EVENT_BINDING                    (I3_IPC_EVENT_MASK | 5)

b/include/ipc.h

65
@@ -93,3 +93,8 @@ void ipc_send_workspace_focus_event(Con *current, Con *old);
66
  * also the window container, in "container".
67
  */
68
 void ipc_send_window_event(const char *property, Con *con);
69
+
70
+/**
71
+ * For the binding events, we send the serialized binding struct.
72
+ */
73
+void ipc_send_binding_event(const char *event_type, Binding *binding);

b/src/bindings.c

78
@@ -259,3 +259,19 @@ void switch_mode(const char *new_mode) {
79
 
80
     ELOG("ERROR: Mode not found\n");
81
 }
82
+
83
+/*
84
+ * Runs the given binding and returns a CommandResult
85
+ */
86
+struct CommandResult *run_binding(Binding *bind) {
87
+    char *command_copy = sstrdup(bind->command);
88
+    struct CommandResult *command_output = parse_command(command_copy);
89
+    free(command_copy);
90
+
91
+    if (command_output->needs_tree_render)
92
+        tree_render();
93
+
94
+    ipc_send_binding_event("run", bind);
95
+
96
+    return command_output;
97
+}

b/src/ipc.c

102
@@ -147,6 +147,57 @@ static void dump_rect(yajl_gen gen, const char *name, Rect r) {
103
     y(map_close);
104
 }
105
 
106
+static void dump_binding(yajl_gen gen, Binding *bind) {
107
+    y(map_open);
108
+    ystr("input_code");
109
+    y(integer, bind->keycode);
110
+
111
+    ystr("input_type");
112
+    ystr((const char*)(bind->input_type == B_KEYBOARD ? "keyboard" : "mouse"));
113
+
114
+    ystr("symbol");
115
+    ystr(bind->symbol);
116
+
117
+    ystr("command");
118
+    ystr(bind->command);
119
+
120
+    ystr("mods");
121
+    y(array_open);
122
+    for (int i = 0; i < 8; i++) {
123
+        if (bind->mods & (1 << i)) {
124
+            switch (1 << i) {
125
+                case XCB_MOD_MASK_SHIFT:
126
+                    ystr("Shift");
127
+                    break;
128
+                case XCB_MOD_MASK_LOCK:
129
+                    ystr("Lock");
130
+                    break;
131
+                case XCB_MOD_MASK_CONTROL:
132
+                    ystr("Control");
133
+                    break;
134
+                case XCB_MOD_MASK_1:
135
+                    ystr("Mod1");
136
+                    break;
137
+                case XCB_MOD_MASK_2:
138
+                    ystr("Mod2");
139
+                    break;
140
+                case XCB_MOD_MASK_3:
141
+                    ystr("Mod3");
142
+                    break;
143
+                case XCB_MOD_MASK_4:
144
+                    ystr("Mod4");
145
+                    break;
146
+                case XCB_MOD_MASK_5:
147
+                    ystr("Mod5");
148
+                    break;
149
+            }
150
+        }
151
+    }
152
+    y(array_close);
153
+
154
+    y(map_close);
155
+}
156
+
157
 void dump_node(yajl_gen gen, struct Con *con, bool inplace_restart) {
158
     y(map_open);
159
     ystr("id");
160
@@ -1090,3 +1141,33 @@ void ipc_send_window_event(const char *property, Con *con) {
161
     y(free);
162
     setlocale(LC_NUMERIC, "");
163
 }
164
+
165
+/*
166
+ * For the binding events, we send the serialized binding struct.
167
+ */
168
+void ipc_send_binding_event(const char *event_type, Binding *bind) {
169
+    DLOG("Issue IPC binding %s event (sym = %s, code = %d)\n", event_type, bind->symbol, bind->keycode);
170
+
171
+    setlocale(LC_NUMERIC, "C");
172
+
173
+    yajl_gen gen = ygenalloc();
174
+
175
+    y(map_open);
176
+
177
+    ystr("change");
178
+    ystr(event_type);
179
+
180
+    ystr("binding");
181
+    dump_binding(gen, bind);
182
+
183
+    y(map_close);
184
+
185
+    const unsigned char *payload;
186
+    ylength length;
187
+    y(get_buf, &payload, &length);
188
+
189
+    ipc_send_event("binding", I3_IPC_EVENT_BINDING, (const char *)payload);
190
+
191
+    y(free);
192
+    setlocale(LC_NUMERIC, "");
193
+}

b/src/key_press.c

198
@@ -105,12 +105,7 @@ void handle_key_press(xcb_key_press_event_t *event) {
199
         }
200
     }
201
 
202
-    char *command_copy = sstrdup(bind->command);
203
-    struct CommandResult *command_output = parse_command(command_copy);
204
-    free(command_copy);
205
-
206
-    if (command_output->needs_tree_render)
207
-        tree_render();
208
+    struct CommandResult *command_output = run_binding(bind);
209
 
210
     /* We parse the JSON reply to figure out whether there was an error
211
      * ("success" being false in on of the returned dictionaries). */