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/33/raw.patch | git am

b/docs/i3bar-protocol

45
@@ -154,7 +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
-
50
+separator::
51
+	The boolean value false disables drawing of a separating line after the
52
+	block.  If the field is not present then the separator will be still
53
+	drawn.  Keep in mind that absence of a separator is only responsible for
54
+        the line itself, the gap between the items would be still present.
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
57
 them with an underscore makes it clear in every script that they are not part

b/docs/ipc

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

b/docs/userguide

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

b/i3bar/include/common.h

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

b/i3bar/include/config.h

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

b/i3bar/include/xcb.h

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

b/i3bar/src/child.c

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

b/i3bar/src/config.c

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

b/i3bar/src/ipc.c

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

b/i3bar/src/xcb.c

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

b/include/config.h

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

b/include/config_directives.h

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

b/parser-specs/config.spec

379
@@ -354,6 +354,7 @@ state BAR:
380
   'output'            -> BAR_OUTPUT
381
   'tray_output'       -> BAR_TRAY_OUTPUT
382
   'font'              -> BAR_FONT
383
+  'separator_block_width'   -> BAR_SEP_BLOCK_WIDTH
384
   'workspace_buttons' -> BAR_WORKSPACE_BUTTONS
385
   'verbose'           -> BAR_VERBOSE
386
   'colors'            -> BAR_COLORS_BRACE
387
@@ -401,6 +402,11 @@ state BAR_FONT:
388
   font = string
389
       -> call cfg_bar_font($font); BAR
390
 
391
+state BAR_SEP_BLOCK_WIDTH:
392
+  width = number
393
+      -> call cfg_bar_sep_block_width(&width); BAR
394
+
395
+
396
 state BAR_WORKSPACE_BUTTONS:
397
   value = word
398
       -> call cfg_bar_workspace_buttons($value); BAR
399
@@ -419,7 +425,7 @@ state BAR_COLORS:
400
   end ->
401
   '#' -> BAR_COLORS_IGNORE_LINE
402
   'set' -> BAR_COLORS_IGNORE_LINE
403
-  colorclass = 'background', 'statusline'
404
+  colorclass = 'background', 'statusline', 'separator'
405
       -> BAR_COLORS_SINGLE
406
   colorclass = 'focused_workspace', 'active_workspace', 'inactive_workspace', 'urgent_workspace'
407
       -> BAR_COLORS_BORDER

b/src/config_directives.c

412
@@ -439,13 +439,24 @@ CFGFUN(assign, const char *workspace) {
413
  * Bar configuration (i3bar)
414
  ******************************************************************************/
415
 
416
-static Barconfig current_bar;
417
+/* Initialize members that might not be set via config,
418
+   but which need default values. */
419
+#define default_bar_config \
420
+{ \
421
+	.separator_block_width = 9 \
422
+}
423
+
424
+static Barconfig current_bar = default_bar_config;
425
 
426
 CFGFUN(bar_font, const char *font) {
427
     FREE(current_bar.font);
428
     current_bar.font = sstrdup(font);
429
 }
430
 
431
+CFGFUN(bar_sep_block_width, const long width) {
432
+    current_bar.separator_block_width = width;
433
+}
434
+
435
 CFGFUN(bar_mode, const char *mode) {
436
     current_bar.mode = (strcmp(mode, "hide") == 0 ? M_HIDE : M_DOCK);
437
 }
438
@@ -526,7 +537,10 @@ CFGFUN(bar_tray_output, const char *output) {
439
 CFGFUN(bar_color_single, const char *colorclass, const char *color) {
440
     if (strcmp(colorclass, "background") == 0)
441
         current_bar.colors.background = sstrdup(color);
442
-    else current_bar.colors.statusline = sstrdup(color);
443
+    else if (strcmp(colorclass, "separator") == 0)
444
+        current_bar.colors.separator = sstrdup(color);
445
+    else
446
+        current_bar.colors.statusline = sstrdup(color);
447
 }
448
 
449
 CFGFUN(bar_status_command, const char *command) {
450
@@ -561,4 +575,5 @@ CFGFUN(bar_finish) {
451
     TAILQ_INSERT_TAIL(&barconfigs, bar_config, configs);
452
 
453
     memset(&current_bar, '\0', sizeof(Barconfig));
454
+    current_bar = (Barconfig) default_bar_config;
455
 }

b/src/ipc.c

460
@@ -658,6 +658,9 @@ IPC_HANDLER(get_bar_config) {
461
         YSTR_IF_SET(status_command);
462
         YSTR_IF_SET(font);
463
 
464
+        ystr("separator_block_width");
465
+        y(integer, config->separator_block_width);
466
+
467
         ystr("workspace_buttons");
468
         y(bool, !config->hide_workspace_buttons);
469
 
470
@@ -677,6 +680,7 @@ IPC_HANDLER(get_bar_config) {
471
         y(map_open);
472
         YSTR_IF_SET(background);
473
         YSTR_IF_SET(statusline);
474
+        YSTR_IF_SET(separator);
475
         YSTR_IF_SET(focused_workspace_border);
476
         YSTR_IF_SET(focused_workspace_bg);
477
         YSTR_IF_SET(focused_workspace_text);

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

482
@@ -591,7 +591,7 @@ EOT
483
 
484
 $expected = <<'EOT';
485
 cfg_bar_output(LVDS-1)
486
-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', '}'
487
+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', '}'
488
 ERROR: CONFIG: (in file <stdin>)
489
 ERROR: CONFIG: Line   1: bar {
490
 ERROR: CONFIG: Line   2:     output LVDS-1