i3 - improved tiling WM


Add new bar.binding_mode_button configuration.

Patch status: needinfo

Patch by syl20bnr

Long description:

This patch adds a new configuration for i3bar called
'binding_mode_button' which acts like the workspace_buttons
configiration but for the binding mode button (i.e the
default resize mode button).
This way it is possible to configure i3bar to hide the
workspace buttons and keep showing the binding mode button.
This should make the hide workspace buttons configuration
more convenient for those who are heavily using binding
modes.

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

b/docs/ipc

33
@@ -494,6 +494,8 @@ font (string)::
34
 	The font to use for text on the bar.
35
 workspace_buttons (boolean)::
36
 	Display workspace buttons or not? Defaults to true.
37
+binding_mode_button (boolean)::
38
+	Display the mode button or not? Defaults to true.
39
 verbose (boolean)::
40
 	Should the bar enable verbose output for debugging? Defaults to false.
41
 colors (map)::
42
@@ -539,6 +541,7 @@ urgent_workspace_text/urgent_workspace_bar::
43
  "status_command": "i3status",
44
  "font": "-misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1",
45
  "workspace_buttons": true,
46
+ "binding_mode_button": true,
47
  "verbose": false,
48
  "colors": {
49
    "background": "#c0c0c0",

b/docs/userguide

54
@@ -1186,6 +1186,28 @@ bar {
55
 }
56
 --------------------
57
 
58
+=== Binding Mode button
59
+
60
+Specifies whether the current binding mode button should be shown or not.
61
+This is useful if you want to hide the workspace buttons but still be able
62
+to see the current binding mode button.
63
+For an example of a +mode+ definition, please refer to the resize mode
64
+section.
65
+
66
+The default is to show the mode button.
67
+
68
+*Syntax*:
69
+--------------------------
70
+binding_mode_button <yes|no>
71
+--------------------------
72
+
73
+*Example*:
74
+--------------------
75
+bar {
76
+    binding_mode_button no
77
+}
78
+--------------------
79
+
80
 === Colors
81
 
82
 As with i3, colors are in HTML hex format (#rrggbb). The following colors can

b/i3bar/include/config.h

87
@@ -23,6 +23,7 @@ typedef struct config_t {
88
     position_t   position;
89
     int          verbose;
90
     struct xcb_color_strings_t colors;
91
+    int          disable_binding_mode;
92
     int          disable_ws;
93
     char         *bar_id;
94
     char         *command;

b/i3bar/src/config.c

99
@@ -193,6 +193,12 @@ static int config_string_cb(void *params_, const unsigned char *val, unsigned in
100
  *
101
  */
102
 static int config_boolean_cb(void *params_, int val) {
103
+    if (!strcmp(cur_key, "binding_mode_button")) {
104
+        DLOG("binding_mode_button = %d\n", val);
105
+        config.disable_binding_mode = !val;
106
+        return 1;
107
+    }
108
+
109
     if (!strcmp(cur_key, "workspace_buttons")) {
110
         DLOG("workspace_buttons = %d\n", val);
111
         config.disable_ws = !val;

b/i3bar/src/xcb.c

116
@@ -1666,72 +1666,72 @@ void draw_bars(bool unhide) {
117
                           MIN(outputs_walk->rect.w - traypx - 4, statusline_width), font.height + 2);
118
         }
119
 
120
-        if (config.disable_ws) {
121
-            continue;
122
-        }
123
-
124
-        i3_ws *ws_walk;
125
-
126
-        TAILQ_FOREACH(ws_walk, outputs_walk->workspaces, tailq) {
127
-            DLOG("Drawing Button for WS %s at x = %d, len = %d\n", i3string_as_utf8(ws_walk->name), i, ws_walk->name_width);
128
-            uint32_t fg_color = colors.inactive_ws_fg;
129
-            uint32_t bg_color = colors.inactive_ws_bg;
130
-            uint32_t border_color = colors.inactive_ws_border;
131
-            if (ws_walk->visible) {
132
-                if (!ws_walk->focused) {
133
-                    fg_color = colors.active_ws_fg;
134
-                    bg_color = colors.active_ws_bg;
135
-                    border_color = colors.active_ws_border;
136
-                } else {
137
-                    fg_color = colors.focus_ws_fg;
138
-                    bg_color = colors.focus_ws_bg;
139
-                    border_color = colors.focus_ws_border;
140
-                    if (last_urgent_ws && strcmp(i3string_as_utf8(ws_walk->name), last_urgent_ws) == 0)
141
-                        walks_away = false;
142
+        if (!config.disable_ws) {
143
+            i3_ws *ws_walk;
144
+            TAILQ_FOREACH(ws_walk, outputs_walk->workspaces, tailq) {
145
+                DLOG("Drawing Button for WS %s at x = %d, len = %d\n",
146
+                     i3string_as_utf8(ws_walk->name), i, ws_walk->name_width);
147
+                uint32_t fg_color = colors.inactive_ws_fg;
148
+                uint32_t bg_color = colors.inactive_ws_bg;
149
+                uint32_t border_color = colors.inactive_ws_border;
150
+                if (ws_walk->visible) {
151
+                    if (!ws_walk->focused) {
152
+                        fg_color = colors.active_ws_fg;
153
+                        bg_color = colors.active_ws_bg;
154
+                        border_color = colors.active_ws_border;
155
+                    } else {
156
+                        fg_color = colors.focus_ws_fg;
157
+                        bg_color = colors.focus_ws_bg;
158
+                        border_color = colors.focus_ws_border;
159
+                        if (last_urgent_ws && strcmp(i3string_as_utf8(ws_walk->name),
160
+                                                     last_urgent_ws) == 0)
161
+                            walks_away = false;
162
+                    }
163
                 }
164
-            }
165
-            if (ws_walk->urgent) {
166
-                DLOG("WS %s is urgent!\n", i3string_as_utf8(ws_walk->name));
167
-                fg_color = colors.urgent_ws_fg;
168
-                bg_color = colors.urgent_ws_bg;
169
-                border_color = colors.urgent_ws_border;
170
-                unhide = true;
171
-                if (!ws_walk->focused) {
172
-                    FREE(last_urgent_ws);
173
-                    last_urgent_ws = sstrdup(i3string_as_utf8(ws_walk->name));
174
+                if (ws_walk->urgent) {
175
+                    DLOG("WS %s is urgent!\n", i3string_as_utf8(ws_walk->name));
176
+                    fg_color = colors.urgent_ws_fg;
177
+                    bg_color = colors.urgent_ws_bg;
178
+                    border_color = colors.urgent_ws_border;
179
+                    unhide = true;
180
+                    if (!ws_walk->focused) {
181
+                        FREE(last_urgent_ws);
182
+                        last_urgent_ws = sstrdup(i3string_as_utf8(ws_walk->name));
183
+                    }
184
                 }
185
-            }
186
-            uint32_t mask = XCB_GC_FOREGROUND | XCB_GC_BACKGROUND;
187
-            uint32_t vals_border[] = { border_color, border_color };
188
-            xcb_change_gc(xcb_connection,
189
-                          outputs_walk->bargc,
190
-                          mask,
191
-                          vals_border);
192
-            xcb_rectangle_t rect_border = { i, 1, ws_walk->name_width + 10, font.height + 4 };
193
-            xcb_poly_fill_rectangle(xcb_connection,
194
-                                    outputs_walk->buffer,
195
-                                    outputs_walk->bargc,
196
-                                    1,
197
-                                    &rect_border);
198
-            uint32_t vals[] = { bg_color, bg_color };
199
-            xcb_change_gc(xcb_connection,
200
-                          outputs_walk->bargc,
201
-                          mask,
202
-                          vals);
203
-            xcb_rectangle_t rect = { i + 1, 2, ws_walk->name_width + 8, font.height + 2 };
204
-            xcb_poly_fill_rectangle(xcb_connection,
205
-                                    outputs_walk->buffer,
206
-                                    outputs_walk->bargc,
207
-                                    1,
208
-                                    &rect);
209
-            set_font_colors(outputs_walk->bargc, fg_color, bg_color);
210
-            draw_text(ws_walk->name, outputs_walk->buffer, outputs_walk->bargc, i + 5, 3, ws_walk->name_width);
211
-            i += 10 + ws_walk->name_width + 1;
212
+                uint32_t mask = XCB_GC_FOREGROUND | XCB_GC_BACKGROUND;
213
+                uint32_t vals_border[] = { border_color, border_color };
214
+                xcb_change_gc(xcb_connection,
215
+                              outputs_walk->bargc,
216
+                              mask,
217
+                              vals_border);
218
+                xcb_rectangle_t rect_border = { i, 1, ws_walk->name_width + 10, font.height + 4 };
219
+                xcb_poly_fill_rectangle(xcb_connection,
220
+                                        outputs_walk->buffer,
221
+                                        outputs_walk->bargc,
222
+                                        1,
223
+                                        &rect_border);
224
+                uint32_t vals[] = { bg_color, bg_color };
225
+                xcb_change_gc(xcb_connection,
226
+                              outputs_walk->bargc,
227
+                              mask,
228
+                              vals);
229
+                xcb_rectangle_t rect = { i + 1, 2, ws_walk->name_width + 8, font.height + 2 };
230
+                xcb_poly_fill_rectangle(xcb_connection,
231
+                                        outputs_walk->buffer,
232
+                                        outputs_walk->bargc,
233
+                                        1,
234
+                                        &rect);
235
+                set_font_colors(outputs_walk->bargc, fg_color, bg_color);
236
+                draw_text(ws_walk->name, outputs_walk->buffer, outputs_walk->bargc,
237
+                          i + 5, 3, ws_walk->name_width);
238
+                i += 10 + ws_walk->name_width + 1;
239
 
240
+            }
241
         }
242
 
243
-        if (binding.name) {
244
 
245
+        if (binding.name && !config.disable_binding_mode) {
246
             uint32_t fg_color = colors.urgent_ws_fg;
247
             uint32_t bg_color = colors.urgent_ws_bg;
248
             uint32_t mask = XCB_GC_FOREGROUND | XCB_GC_BACKGROUND;
249
@@ -1766,6 +1766,7 @@ void draw_bars(bool unhide) {
250
             unhide = true;
251
         }
252
 
253
+
254
         i = 0;
255
     }
256
 

b/include/config.h

261
@@ -267,6 +267,10 @@ struct Barconfig {
262
      * zero. */
263
     bool hide_workspace_buttons;
264
 
265
+    /** Hide mode button? Configuration option is 'binding_mode_button no'
266
+     * but we invert the bool for the same reason as hide_workspace_buttons.*/
267
+    bool hide_binding_mode_button;
268
+
269
     /** Enable verbose mode? Useful for debugging purposes. */
270
     bool verbose;
271
 

b/include/config_directives.h

276
@@ -74,6 +74,7 @@ CFGFUN(bar_socket_path, const char *socket_path);
277
 CFGFUN(bar_tray_output, const char *output);
278
 CFGFUN(bar_color_single, const char *colorclass, const char *color);
279
 CFGFUN(bar_status_command, const char *command);
280
+CFGFUN(bar_binding_mode_button, const char *value);
281
 CFGFUN(bar_workspace_buttons, const char *value);
282
 CFGFUN(bar_finish);
283
 

b/parser-specs/config.spec

288
@@ -345,20 +345,21 @@ state BAR:
289
   error ->
290
   '#' -> BAR_IGNORE_LINE
291
   'set' -> BAR_IGNORE_LINE
292
-  'i3bar_command'     -> BAR_BAR_COMMAND
293
-  'status_command'    -> BAR_STATUS_COMMAND
294
-  'socket_path'       -> BAR_SOCKET_PATH
295
-  'mode'              -> BAR_MODE
296
-  'hidden_state'      -> BAR_HIDDEN_STATE
297
-  'id'                -> BAR_ID
298
-  'modifier'          -> BAR_MODIFIER
299
-  'position'          -> BAR_POSITION
300
-  'output'            -> BAR_OUTPUT
301
-  'tray_output'       -> BAR_TRAY_OUTPUT
302
-  'font'              -> BAR_FONT
303
-  'workspace_buttons' -> BAR_WORKSPACE_BUTTONS
304
-  'verbose'           -> BAR_VERBOSE
305
-  'colors'            -> BAR_COLORS_BRACE
306
+  'i3bar_command'       -> BAR_BAR_COMMAND
307
+  'status_command'      -> BAR_STATUS_COMMAND
308
+  'socket_path'         -> BAR_SOCKET_PATH
309
+  'mode'                -> BAR_MODE
310
+  'hidden_state'        -> BAR_HIDDEN_STATE
311
+  'id'                  -> BAR_ID
312
+  'modifier'            -> BAR_MODIFIER
313
+  'position'            -> BAR_POSITION
314
+  'output'              -> BAR_OUTPUT
315
+  'tray_output'         -> BAR_TRAY_OUTPUT
316
+  'font'                -> BAR_FONT
317
+  'binding_mode_button' -> BAR_BINDING_MODE_BUTTON
318
+  'workspace_buttons'   -> BAR_WORKSPACE_BUTTONS
319
+  'verbose'             -> BAR_VERBOSE
320
+  'colors'              -> BAR_COLORS_BRACE
321
   '}'
322
       -> call cfg_bar_finish(); INITIAL
323
 
324
@@ -411,6 +412,10 @@ state BAR_FONT:
325
   font = string
326
       -> call cfg_bar_font($font); BAR
327
 
328
+state BAR_BINDING_MODE_BUTTON:
329
+  value = word
330
+      -> call cfg_bar_binding_mode_button($value); BAR
331
+
332
 state BAR_WORKSPACE_BUTTONS:
333
   value = word
334
       -> call cfg_bar_workspace_buttons($value); BAR

b/src/config_directives.c

339
@@ -550,6 +550,10 @@ CFGFUN(bar_status_command, const char *command) {
340
     current_bar.status_command = sstrdup(command);
341
 }
342
 
343
+CFGFUN(bar_binding_mode_button, const char *value) {
344
+    current_bar.hide_binding_mode_button = !eval_boolstr(value);
345
+}
346
+
347
 CFGFUN(bar_workspace_buttons, const char *value) {
348
     current_bar.hide_workspace_buttons = !eval_boolstr(value);
349
 }

b/src/ipc.c

354
@@ -686,6 +686,9 @@ IPC_HANDLER(get_bar_config) {
355
         ystr("workspace_buttons");
356
         y(bool, !config->hide_workspace_buttons);
357
 
358
+        ystr("binding_mode_button");
359
+        y(bool, !config->hide_binding_mode_button);
360
+
361
         ystr("verbose");
362
         y(bool, config->verbose);
363
 

b/testcases/t/177-bar-config.t

368
@@ -63,6 +63,7 @@ my $bar_config = $i3->get_bar_config($bar_id)->recv;
369
 is($bar_config->{status_command}, 'i3status --foo', 'status_command correct');
370
 ok(!$bar_config->{verbose}, 'verbose off by default');
371
 ok($bar_config->{workspace_buttons}, 'workspace buttons enabled per default');
372
+ok($bar_config->{binding_mode_button}, 'mode button enabled per default');
373
 is($bar_config->{mode}, 'dock', 'dock mode by default');
374
 is($bar_config->{position}, 'bottom', 'position bottom by default');
375
 
376
@@ -85,7 +86,8 @@ $config = <<EOT;
377
 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
378
 
379
 bar {
380
-    # Start a default instance of i3bar which provides workspace buttons.
381
+    # Start a default instance of i3bar which does not provide
382
+    # workspace buttons.
383
     # Additionally, i3status will provide a statusline.
384
     status_command i3status --bar
385
 
386
@@ -98,6 +100,7 @@ bar {
387
     mode dock
388
     font Terminus
389
     workspace_buttons no
390
+    binding_mode_button no
391
     verbose yes
392
     socket_path /tmp/foobar
393
 
394
@@ -125,6 +128,7 @@ $bar_config = $i3->get_bar_config($bar_id)->recv;
395
 is($bar_config->{status_command}, 'i3status --bar', 'status_command correct');
396
 ok($bar_config->{verbose}, 'verbose on');
397
 ok(!$bar_config->{workspace_buttons}, 'workspace buttons disabled');
398
+ok(!$bar_config->{binding_mode_button}, 'mode button disabled');
399
 is($bar_config->{mode}, 'dock', 'dock mode');
400
 is($bar_config->{position}, 'top', 'position top');
401
 is_deeply($bar_config->{outputs}, [ 'HDMI1', 'HDMI2' ], 'outputs ok');
402
@@ -230,7 +234,8 @@ $config = <<EOT;
403
 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
404
 
405
 bar {
406
-    # Start a default instance of i3bar which provides workspace buttons.
407
+    # Start a default instance of i3bar which does not provide
408
+    # workspace buttons.
409
     # Additionally, i3status will provide a statusline.
410
     status_command i3status --bar
411
 
412
@@ -243,6 +248,7 @@ bar {
413
     mode dock
414
     font Terminus
415
     workspace_buttons no
416
+    binding_mode_button yes
417
     verbose yes
418
     socket_path /tmp/foobar
419
 
420
@@ -271,6 +277,7 @@ $bar_config = $i3->get_bar_config($bar_id)->recv;
421
 is($bar_config->{status_command}, 'i3status --bar', 'status_command correct');
422
 ok($bar_config->{verbose}, 'verbose on');
423
 ok(!$bar_config->{workspace_buttons}, 'workspace buttons disabled');
424
+ok($bar_config->{binding_mode_button}, 'mode button enabled');
425
 is($bar_config->{mode}, 'dock', 'dock mode');
426
 is($bar_config->{position}, 'top', 'position top');
427
 is_deeply($bar_config->{outputs}, [ 'HDMI1', 'HDMI2' ], 'outputs ok');

b/testcases/t/201-config-parser.t

432
@@ -627,7 +627,7 @@ EOT
433
 
434
 $expected = <<'EOT';
435
 cfg_bar_output(LVDS-1)
436
-ERROR: CONFIG: Expected one of these tokens: <end>, '#', 'set', 'i3bar_command', 'status_command', 'socket_path', 'mode', 'hidden_state', 'id', 'modifier', 'position', 'output', 'tray_output', 'font', 'workspace_buttons', 'verbose', 'colors', '}'
437
+ERROR: CONFIG: Expected one of these tokens: <end>, '#', 'set', 'i3bar_command', 'status_command', 'socket_path', 'mode', 'hidden_state', 'id', 'modifier', 'position', 'output', 'tray_output', 'font', 'binding_mode_button', 'workspace_buttons', 'verbose', 'colors', '}'
438
 ERROR: CONFIG: (in file <stdin>)
439
 ERROR: CONFIG: Line   1: bar {
440
 ERROR: CONFIG: Line   2:     output LVDS-1