i3 - improved tiling WM


Separator color and width via config; separator off via ipc

Patch status: rejected

Patch by Artem Shinkarov

Long description:

This patch adds the following features:
1) Configure a color of the separator via config.  It is done like
   bar {
      colors {
         separator #000000
      }
   }
2) Configure a width of the gap between the items.  In the middle of that
   gap separating line is being drawn.  The syntax is the following:
   bar {
      separator_block_width 11
   }
   which would set the width of the gap to 11 pixels.  If not specified
   the default is 9 pixels.

3) A block can have a boolean entry "separator" and if it is set
   to false, then the drawing of the separating line would be disabled.

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

b/docs/i3bar-protocol

45
@@ -154,6 +154,11 @@ urgent::
46
 	A boolean which specifies whether the current value is urgent. Examples
47
 	are battery charge values below 1 percent or no more available disk
48
 	space (for non-root users). The presentation of urgency is up to i3bar.
49
+separator::
50
+	The boolean value false disables drawing of a separating line after the
51
+	block.  If the field is not present then the separator will be still
52
+	drawn.  Keep in mind that absence of a separator is only responsible for
53
+        the line itself, the gap between the items would be still present.
54
 
55
 If you want to put in your own entries into a block, prefix the key with an
56
 underscore (_). i3bar will ignore all keys it doesn’t understand, and prefixing

b/docs/ipc

61
@@ -497,6 +497,9 @@ workspace_buttons (boolean)::
62
 	Display workspace buttons or not? Defaults to true.
63
 verbose (boolean)::
64
 	Should the bar enable verbose output for debugging? Defaults to false.
65
+separator_block_width (integer)::
66
+	The width of the gap between items.  In the middle of the gap, a separating
67
+	line is going to be drawn.
68
 colors (map)::
69
 	Contains key/value pairs of colors. Each value is a color code in hex,
70
 	formatted #rrggbb (like in HTML).
71
@@ -507,6 +510,8 @@ background::
72
 	Background color of the bar.
73
 statusline::
74
 	Text color to be used for the statusline.
75
+separator::
76
+	Text color to be used for the separator.
77
 focused_workspace_text/focused_workspace_bg::
78
 	Text color/background color for a workspace button when the workspace
79
 	has focus.

b/docs/userguide

84
@@ -1145,6 +1145,26 @@ bar {
85
 }
86
 --------------------
87
 
88
+=== Width of the gap between items
89
+
90
+Specifies the width of the gap between the items in the statusline.  The number
91
+represents the width in pixels, value 0 means no gap.  Please keep in mind that
92
+the separating line is drawn in the middle of the gap, so it is recommended to
93
+keep the value odd.  If not specified, the default gap width is 9 pixels.
94
+
95
+*Syntax*:
96
+--------------------------
97
+separator_block_width <number>
98
+--------------------------
99
+
100
+*Example*:
101
+--------------------
102
+bar {
103
+    separator_block_width 5
104
+}
105
+--------------------
106
+
107
+
108
 === Colors
109
 
110
 As with i3, colors are in HTML hex format (#rrggbb). The following colors can
111
@@ -1154,6 +1174,8 @@ background::
112
 	Background color of the bar.
113
 statusline::
114
 	Text color to be used for the statusline.
115
+separator::	
116
+	Text color to be used for the separator.
117
 focused_workspace::
118
 	Border, background and text color for a workspace button when the workspace
119
 	has focus.
120
@@ -1175,6 +1197,7 @@ urgent_workspace::
121
 colors {
122
     background <color>
123
     statusline <color>
124
+    separator <color>
125
 
126
     colorclass <border> <background> <text>
127
 }
128
@@ -1186,6 +1209,7 @@ bar {
129
     colors {
130
         background #000000
131
         statusline #ffffff
132
+        separator #666666
133
 
134
         focused_workspace  #4c7899 #285577 #ffffff
135
         active_workspace   #333333 #5f676a #ffffff

b/i3bar/include/common.h

140
@@ -43,6 +43,7 @@ struct status_block {
141
     blockalign_t align;
142
 
143
     bool urgent;
144
+    bool no_separator;
145
 
146
     /* The amount of pixels necessary to render this block. These variables are
147
      * only temporarily used in refresh_statusline(). */

b/i3bar/include/config.h

152
@@ -30,6 +30,7 @@ typedef struct config_t {
153
     char         *fontname;
154
     char         *tray_output;
155
     int          num_outputs;
156
+    uint32_t     sep_block_width;
157
     char         **outputs;
158
 } config_t;
159
 

b/i3bar/include/xcb.h

164
@@ -28,6 +28,7 @@
165
 struct xcb_color_strings_t {
166
     char *bar_fg;
167
     char *bar_bg;
168
+    char *sep_fg;
169
     char *active_ws_fg;
170
     char *active_ws_bg;
171
     char *active_ws_border;
172
@@ -65,6 +66,13 @@ void init_xcb_late(char *fontname);
173
 void init_colors(const struct xcb_color_strings_t *colors);
174
 
175
 /*
176
+ * Initialize the sizes
177
+ *
178
+ */
179
+void init_sizes(const uint32_t _sep_block_width);
180
+
181
+
182
+/*
183
  * Cleanup the xcb-stuff.
184
  * Called once, before the program terminates.
185
  *

b/i3bar/src/child.c

190
@@ -117,6 +117,9 @@ static int stdin_boolean(void *context, int val) {
191
     if (strcasecmp(ctx->last_map_key, "urgent") == 0) {
192
         ctx->block.urgent = val;
193
     }
194
+    if (strcasecmp(ctx->last_map_key, "separator") == 0) {
195
+        ctx->block.no_separator = !val;
196
+    }
197
     return 1;
198
 }
199
 

b/i3bar/src/config.c

204
@@ -161,6 +161,7 @@ static int config_string_cb(void *params_, const unsigned char *val, unsigned in
205
 
206
     COLOR(statusline, bar_fg);
207
     COLOR(background, bar_bg);
208
+    COLOR(separator, sep_fg);
209
     COLOR(focused_workspace_border, focus_ws_border);
210
     COLOR(focused_workspace_bg, focus_ws_bg);
211
     COLOR(focused_workspace_text, focus_ws_fg);
212
@@ -199,11 +200,32 @@ static int config_boolean_cb(void *params_, int val) {
213
     return 0;
214
 }
215
 
216
+/*
217
+ * Parse an integer value
218
+ *
219
+ */
220
+#if YAJL_MAJOR >= 2
221
+static int config_integer_cb(void *params_, long long val) {
222
+#else
223
+static int config_integer_cb(void *params_, long val) {
224
+#endif
225
+    if (!strcmp(cur_key, "separator_block_width")) {
226
+        uint32_t uval = val < 0 ? 0 : (uint32_t)val;
227
+
228
+        DLOG("separator_block_width = %u\n", uval);
229
+        config.sep_block_width = uval;
230
+        return 1;
231
+    }
232
+
233
+    return 0;
234
+}
235
+
236
+
237
 /* A datastructure to pass all these callbacks to yajl */
238
 static yajl_callbacks outputs_callbacks = {
239
     &config_null_cb,
240
     &config_boolean_cb,
241
-    NULL,
242
+    &config_integer_cb,
243
     NULL,
244
     NULL,
245
     &config_string_cb,
246
@@ -260,6 +282,7 @@ void free_colors(struct xcb_color_strings_t *colors) {
247
     } while (0)
248
     FREE_COLOR(bar_fg);
249
     FREE_COLOR(bar_bg);
250
+    FREE_COLOR(sep_fg);
251
     FREE_COLOR(active_ws_fg);
252
     FREE_COLOR(active_ws_bg);
253
     FREE_COLOR(active_ws_border);

b/i3bar/src/ipc.c

258
@@ -100,6 +100,9 @@ void got_bar_config(char *reply) {
259
     /* Resolve color strings to colorpixels and save them, then free the strings. */
260
     init_colors(&(config.colors));
261
 
262
+    /* Initialize sizes from config */
263
+    init_sizes(config.sep_block_width);
264
+
265
     /* The name of this function is actually misleading. Even if no command is
266
      * specified, this function initiates the watchers to listen on stdin and
267
      * react accordingly */

b/i3bar/src/xcb.c

272
@@ -71,6 +71,10 @@ xcb_gcontext_t   statusline_clear;
273
 xcb_pixmap_t     statusline_pm;
274
 uint32_t         statusline_width;
275
 
276
+/* Sizes obtained through config */
277
+/* 'sep_block_width' -- width of the gap between items (in px) */
278
+static uint32_t sep_block_width;
279
+
280
 /* Event-Watchers, to interact with the user */
281
 ev_prepare *xcb_prep;
282
 ev_check   *xcb_chk;
283
@@ -84,6 +88,7 @@ static mode binding;
284
 struct xcb_colors_t {
285
     uint32_t bar_fg;
286
     uint32_t bar_bg;
287
+    uint32_t sep_fg;
288
     uint32_t active_ws_fg;
289
     uint32_t active_ws_bg;
290
     uint32_t active_ws_border;
291
@@ -149,7 +154,8 @@ void refresh_statusline(void) {
292
 
293
         /* If this is not the last block, add some pixels for a separator. */
294
         if (TAILQ_NEXT(block, blocks) != NULL)
295
-            block->width += 9;
296
+            block->width += sep_block_width;
297
+
298
         statusline_width += block->width + block->x_offset + block->x_append;
299
     }
300
 
301
@@ -174,12 +180,14 @@ void refresh_statusline(void) {
302
         draw_text(block->full_text, statusline_pm, statusline_ctx, x + block->x_offset, 1, block->width);
303
         x += block->width + block->x_offset + block->x_append;
304
 
305
-        if (TAILQ_NEXT(block, blocks) != NULL) {
306
+        if (TAILQ_NEXT(block, blocks) != NULL && !block->no_separator && sep_block_width > 0) {
307
             /* This is not the last block, draw a separator. */
308
-            set_font_colors(statusline_ctx, get_colorpixel("#666666"), colors.bar_bg);
309
+            uint32_t sep_offset = sep_block_width/2 + sep_block_width % 2;
310
+            set_font_colors(statusline_ctx, colors.sep_fg, colors.bar_bg);
311
             xcb_poly_line(xcb_connection, XCB_COORD_MODE_ORIGIN, statusline_pm,
312
                           statusline_ctx, 2,
313
-                          (xcb_point_t[]){ { x - 5, 2 }, { x - 5, font.height - 2 } });
314
+                          (xcb_point_t[]){ { x - sep_offset, 2 },
315
+                                           { x - sep_offset, font.height - 2 } });
316
         }
317
     }
318
 }
319
@@ -259,6 +267,7 @@ void init_colors(const struct xcb_color_strings_t *new_colors) {
320
     } while  (0)
321
     PARSE_COLOR(bar_fg, "#FFFFFF");
322
     PARSE_COLOR(bar_bg, "#000000");
323
+    PARSE_COLOR(sep_fg, "#666666");
324
     PARSE_COLOR(active_ws_fg, "#FFFFFF");
325
     PARSE_COLOR(active_ws_bg, "#333333");
326
     PARSE_COLOR(active_ws_border, "#333333");
327
@@ -277,6 +286,10 @@ void init_colors(const struct xcb_color_strings_t *new_colors) {
328
     xcb_flush(xcb_connection);
329
 }
330
 
331
+void init_sizes(const uint32_t _sep_block_width) {
332
+    sep_block_width = _sep_block_width;
333
+}
334
+
335
 /*
336
  * Handle a button-press-event (i.e. a mouse click on one of our bars).
337
  * We determine, whether the click occured on a ws-button or if the scroll-

b/include/config.h

342
@@ -256,6 +256,9 @@ struct Barconfig {
343
     /** Font specification for all text rendered on the bar. */
344
     char *font;
345
 
346
+    /** Width of the gap between the items (in px). */
347
+    int32_t separator_block_width;
348
+
349
     /** Hide workspace buttons? Configuration option is 'workspace_buttons no'
350
      * but we invert the bool to get the correct default when initializing with
351
      * zero. */
352
@@ -267,6 +270,7 @@ struct Barconfig {
353
     struct bar_colors {
354
         char *background;
355
         char *statusline;
356
+        char *separator;
357
 
358
         char *focused_workspace_border;
359
         char *focused_workspace_bg;

b/include/config_directives.h

364
@@ -61,6 +61,7 @@ CFGFUN(enter_mode, const char *mode);
365
 CFGFUN(mode_binding, const char *bindtype, const char *modifiers, const char *key, const char *release, const char *command);
366
 
367
 CFGFUN(bar_font, const char *font);
368
+CFGFUN(bar_sep_block_width, const long width);
369
 CFGFUN(bar_mode, const char *mode);
370
 CFGFUN(bar_output, const char *output);
371
 CFGFUN(bar_verbose, const char *verbose);

b/parser-specs/config.spec

376
@@ -345,18 +345,19 @@ state BAR:
377
   error ->
378
   '#' -> BAR_IGNORE_LINE
379
   'set' -> BAR_IGNORE_LINE
380
-  'i3bar_command'     -> BAR_BAR_COMMAND
381
-  'status_command'    -> BAR_STATUS_COMMAND
382
-  'socket_path'       -> BAR_SOCKET_PATH
383
-  'mode'              -> BAR_MODE
384
-  'modifier'          -> BAR_MODIFIER
385
-  'position'          -> BAR_POSITION
386
-  'output'            -> BAR_OUTPUT
387
-  'tray_output'       -> BAR_TRAY_OUTPUT
388
-  'font'              -> BAR_FONT
389
-  'workspace_buttons' -> BAR_WORKSPACE_BUTTONS
390
-  'verbose'           -> BAR_VERBOSE
391
-  'colors'            -> BAR_COLORS_BRACE
392
+  'i3bar_command'           -> BAR_BAR_COMMAND
393
+  'status_command'          -> BAR_STATUS_COMMAND
394
+  'socket_path'             -> BAR_SOCKET_PATH
395
+  'mode'                    -> BAR_MODE
396
+  'modifier'                -> BAR_MODIFIER
397
+  'position'                -> BAR_POSITION
398
+  'output'                  -> BAR_OUTPUT
399
+  'tray_output'             -> BAR_TRAY_OUTPUT
400
+  'font'                    -> BAR_FONT
401
+  'separator_block_width'   -> BAR_SEP_BLOCK_WIDTH
402
+  'workspace_buttons'       -> BAR_WORKSPACE_BUTTONS
403
+  'verbose'                 -> BAR_VERBOSE
404
+  'colors'                  -> BAR_COLORS_BRACE
405
   '}'
406
       -> call cfg_bar_finish(); INITIAL
407
 
408
@@ -401,6 +402,11 @@ state BAR_FONT:
409
   font = string
410
       -> call cfg_bar_font($font); BAR
411
 
412
+state BAR_SEP_BLOCK_WIDTH:
413
+  width = number
414
+      -> call cfg_bar_sep_block_width(&width); BAR
415
+
416
+
417
 state BAR_WORKSPACE_BUTTONS:
418
   value = word
419
       -> call cfg_bar_workspace_buttons($value); BAR
420
@@ -419,7 +425,7 @@ state BAR_COLORS:
421
   end ->
422
   '#' -> BAR_COLORS_IGNORE_LINE
423
   'set' -> BAR_COLORS_IGNORE_LINE
424
-  colorclass = 'background', 'statusline'
425
+  colorclass = 'background', 'statusline', 'separator'
426
       -> BAR_COLORS_SINGLE
427
   colorclass = 'focused_workspace', 'active_workspace', 'inactive_workspace', 'urgent_workspace'
428
       -> BAR_COLORS_BORDER

b/src/config_directives.c

433
@@ -439,13 +439,24 @@ CFGFUN(assign, const char *workspace) {
434
  * Bar configuration (i3bar)
435
  ******************************************************************************/
436
 
437
-static Barconfig current_bar;
438
+/* Initialize members that might not be set via config,
439
+   but which need default values. */
440
+#define default_bar_config \
441
+{ \
442
+	.separator_block_width = 9 \
443
+}
444
+
445
+static Barconfig current_bar = default_bar_config;
446
 
447
 CFGFUN(bar_font, const char *font) {
448
     FREE(current_bar.font);
449
     current_bar.font = sstrdup(font);
450
 }
451
 
452
+CFGFUN(bar_sep_block_width, const long width) {
453
+    current_bar.separator_block_width = width;
454
+}
455
+
456
 CFGFUN(bar_mode, const char *mode) {
457
     current_bar.mode = (strcmp(mode, "hide") == 0 ? M_HIDE : M_DOCK);
458
 }
459
@@ -526,7 +537,10 @@ CFGFUN(bar_tray_output, const char *output) {
460
 CFGFUN(bar_color_single, const char *colorclass, const char *color) {
461
     if (strcmp(colorclass, "background") == 0)
462
         current_bar.colors.background = sstrdup(color);
463
-    else current_bar.colors.statusline = sstrdup(color);
464
+    else if (strcmp(colorclass, "separator") == 0)
465
+        current_bar.colors.separator = sstrdup(color);
466
+    else
467
+        current_bar.colors.statusline = sstrdup(color);
468
 }
469
 
470
 CFGFUN(bar_status_command, const char *command) {
471
@@ -561,4 +575,5 @@ CFGFUN(bar_finish) {
472
     TAILQ_INSERT_TAIL(&barconfigs, bar_config, configs);
473
 
474
     memset(&current_bar, '\0', sizeof(Barconfig));
475
+    current_bar = (Barconfig) default_bar_config;
476
 }

b/src/ipc.c

481
@@ -658,6 +658,9 @@ IPC_HANDLER(get_bar_config) {
482
         YSTR_IF_SET(status_command);
483
         YSTR_IF_SET(font);
484
 
485
+        ystr("separator_block_width");
486
+        y(integer, config->separator_block_width);
487
+
488
         ystr("workspace_buttons");
489
         y(bool, !config->hide_workspace_buttons);
490
 
491
@@ -677,6 +680,7 @@ IPC_HANDLER(get_bar_config) {
492
         y(map_open);
493
         YSTR_IF_SET(background);
494
         YSTR_IF_SET(statusline);
495
+        YSTR_IF_SET(separator);
496
         YSTR_IF_SET(focused_workspace_border);
497
         YSTR_IF_SET(focused_workspace_bg);
498
         YSTR_IF_SET(focused_workspace_text);

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

503
@@ -591,7 +591,7 @@ EOT
504
 
505
 $expected = <<'EOT';
506
 cfg_bar_output(LVDS-1)
507
-ERROR: CONFIG: Expected one of these tokens: <end>, '#', 'set', 'i3bar_command', 'status_command', 'socket_path', 'mode', 'modifier', 'position', 'output', 'tray_output', 'font', 'workspace_buttons', 'verbose', 'colors', '}'
508
+ERROR: CONFIG: Expected one of these tokens: <end>, '#', 'set', 'i3bar_command', 'status_command', 'socket_path', 'mode', 'modifier', 'position', 'output', 'tray_output', 'font', 'separator_block_width', 'workspace_buttons', 'verbose', 'colors', '}'
509
 ERROR: CONFIG: (in file <stdin>)
510
 ERROR: CONFIG: Line   1: bar {
511
 ERROR: CONFIG: Line   2:     output LVDS-1