introduced i3 command for changing the hidden state and the mode of i3bar
Patch status: needinfo
Patch by jj
Long description:
The state and mode of each i3bar instance can now be controlled from within i3. Therefore, two new i3 command were introduced: _ bar state show|hide|toggle [<bar_id>] show: always show the bar hide: normal hide mode toggle: toggle between show and hide (individually for each bar) _ bar mode dock|hide|invisible|toggle [<bar_id>] hide,dock: like before invisible: always keep the bar hidden toggle: toggle between dock and hide (individually for each bar) This patch introduces a state ("state hide|show") in the barconfig, which indicates the current state of each i3bar instance. It only affects the bar when in hide mode. Additionally, a new invisible mode was introduced. In order to change the state or mode of the bar from i3, a barconfig-update event was introduced, for which a bar can subscribe and the bar then gets notified about the currently set state and mode in its barconfig. For convenience, an id field ("id <bar_id>") was added to the barconfig, where one can set the desired id for the corresponding bar. If the id is not specified, i3 will deterministically choose an id; otherwise, with the previous random approach for finding a new id, which is actually not shared with i3bar, as it would determine its id on startup, the event-subscription would be destroyed on reload. Still, this issue remains when manually changing the bar_id in the config and then reloading. fixes #833, #651
To apply this patch, use:
curl http://cr.i3wm.org/patch/146/raw.patch | git am
b/docs/ipc
61 |
@@ -626,6 +626,9 @@ mode (2):: |
62 |
window (3):: |
63 |
Sent when a client's window is successfully reparented (that is when i3 |
64 |
has finished fitting it into a container). |
65 |
+barconfig_update (4):: |
66 |
+ Sent when the state or mode field in the barconfig of any bar instance was |
67 |
+ updated. |
68 |
|
69 |
*Example:* |
70 |
-------------------------------------------------------------------- |
71 |
@@ -723,6 +726,24 @@ window title as "urxvt"). |
72 |
} |
73 |
--------------------------- |
74 |
|
75 |
+=== barconfig_update event |
76 |
+ |
77 |
+This event consists of a single serialized map reporting on options from the |
78 |
+barconfig of the specified bar_id that were updated in i3. The map always |
79 |
+consists of a property +id (string)+, which specifies to which bar instance the |
80 |
+send config update belongs, a property +state (string)+, which indicates the |
81 |
+hidden state of an i3bar instance, and a property +mode (string)+, which |
82 |
+corresponds to current mode. |
83 |
+ |
84 |
+*Example:* |
85 |
+--------------------------- |
86 |
+{ |
87 |
+ "id": "bar-0", |
88 |
+ "state": "hide" |
89 |
+ "mode": "hide" |
90 |
+} |
91 |
+--------------------------- |
92 |
+ |
93 |
== See also (existing libraries) |
94 |
|
95 |
[[libraries]] |
b/docs/userguide
100 |
@@ -997,19 +997,31 @@ bar { |
101 |
=== Display mode |
102 |
|
103 |
You can have i3bar either be visible permanently at one edge of the screen |
104 |
-(+dock+ mode) or make it show up when you press your modifier key (+hide+ |
105 |
+(+dock+ mode) or make it show up when you press your modifier key (+hide+ mode), |
106 |
+i3bar can also be forced to be hidden until the mode changes again (+invisible+ |
107 |
mode). The modifier key can be configured using the +modifier+ option. |
108 |
|
109 |
The hide mode maximizes screen space that can be used for actual windows. Also, |
110 |
i3bar sends the +SIGSTOP+ and +SIGCONT+ signals to the statusline process to |
111 |
save battery power. |
112 |
|
113 |
-The default is dock mode; in hide mode, the default modifier is Mod4 (usually |
114 |
-the windows key). |
115 |
+The state option for i3bar is only relevant for hide mode and has no effect in |
116 |
+dock mode or invisible mode. It indicates the current state of the bar: (1) The |
117 |
+bar acts like in normal hide mode, it is hidden and is only unhidden in case of |
118 |
+urgency hints or by pressing the modifier key (+hide+ state), or (2) it is |
119 |
+drawn on top of the currently visible workspace (+show+ state). |
120 |
+ |
121 |
+Both, the state and the mode option can be changed during runtime through the |
122 |
+corresponding bar commands and will be reverted to the configured values on |
123 |
+reload. |
124 |
+ |
125 |
+The default mode is dock mode; in hide mode, the default modifier is Mod4 (usually |
126 |
+the windows key). The default value for the state is hide. |
127 |
|
128 |
*Syntax*: |
129 |
---------------- |
130 |
-mode <dock|hide> |
131 |
+mode <dock|hide|invisible> |
132 |
+state <hide|show> |
133 |
modifier <Modifier> |
134 |
---------------- |
135 |
|
136 |
@@ -1017,12 +1029,31 @@ modifier <Modifier> |
137 |
---------------- |
138 |
bar { |
139 |
mode hide |
140 |
+ state hide |
141 |
modifier Mod1 |
142 |
} |
143 |
---------------- |
144 |
|
145 |
Available modifiers are Mod1-Mod5, Shift, Control (see +xmodmap(1)+). |
146 |
|
147 |
+=== Bar ID |
148 |
+ |
149 |
+Specifies the bar ID for the configured bar instance. If this option is missing, |
150 |
+the ID is set to 'bar-x', where x corresponds to the position of the embedding |
151 |
+bar block in the config file ('bar-0', 'bar-1', ...). |
152 |
+ |
153 |
+*Syntax*: |
154 |
+--------------------- |
155 |
+id <bar_id> |
156 |
+--------------------- |
157 |
+ |
158 |
+*Example*: |
159 |
+--------------------- |
160 |
+bar { |
161 |
+ id bar-1 |
162 |
+} |
163 |
+--------------------- |
164 |
+ |
165 |
[[i3bar_position]] |
166 |
=== Position |
167 |
|
168 |
@@ -1775,6 +1806,35 @@ bindsym $mod+minus scratchpad show |
169 |
bindsym mod4+s [title="^Sup ::"] scratchpad show |
170 |
------------------------------------------------ |
171 |
|
172 |
+=== i3bar control |
173 |
+ |
174 |
+There are two options in the configuration of each i3bar instance that can be |
175 |
+changed during runtime by invoking a command through i3. The commands +bar |
176 |
+state+ and +bar mode+ allow setting the current state respectively mode option |
177 |
+of each bar. It is also possible to toggle between hide state and show state as |
178 |
+well as between dock mode and hide mode. Each i3bar instance can be controlled |
179 |
+individually by specifying a bar_id, if none is given, the command is executed |
180 |
+for all bar instances. |
181 |
+ |
182 |
+*Syntax*: |
183 |
+--------------- |
184 |
+bar state hide|show|toggle [<bar_id>] |
185 |
+ |
186 |
+bar mode dock|hide|invisible|toggle [<bar_id>] |
187 |
+--------------- |
188 |
+ |
189 |
+*Examples*: |
190 |
+------------------------------------------------ |
191 |
+# Toggle between hide state and show state |
192 |
+bindsym $mod+b bar state toggle |
193 |
+ |
194 |
+# Set the bar instance with id 'bar-1' to stay hidden |
195 |
+bindsym $mod+Shift+b bar mode invisible bar-1 |
196 |
+ |
197 |
+# Toggle between dock mode and hide mode |
198 |
+bindsym $mod+n bar mode toggle |
199 |
+------------------------------------------------ |
200 |
+ |
201 |
[[multi_monitor]] |
202 |
|
203 |
== Multiple monitors |
b/i3bar/include/config.h
208 |
@@ -19,7 +19,6 @@ typedef enum { |
209 |
} position_t; |
210 |
|
211 |
typedef struct config_t { |
212 |
- int hide_on_modifier; |
213 |
int modifier; |
214 |
position_t position; |
215 |
int verbose; |
216 |
@@ -31,6 +30,12 @@ typedef struct config_t { |
217 |
char *tray_output; |
218 |
int num_outputs; |
219 |
char **outputs; |
220 |
+ |
221 |
+ /* Bar display mode (hide unless modifier is pressed or show in dock mode or always hide in invisible mode) */ |
222 |
+ enum { M_DOCK = 0, M_HIDE = 1, M_INVISIBLE = 2 } hide_on_modifier; |
223 |
+ |
224 |
+ /* The current state of the bar, which indicates whether it is hidden or shown */ |
225 |
+ enum { S_HIDE = 0, S_SHOW = 1 } state; |
226 |
} config_t; |
227 |
|
228 |
config_t config; |
b/i3bar/src/config.c
233 |
@@ -73,7 +73,15 @@ static int config_string_cb(void *params_, const unsigned char *val, unsigned in |
234 |
|
235 |
if (!strcmp(cur_key, "mode")) { |
236 |
DLOG("mode = %.*s, len = %d\n", len, val, len); |
237 |
- config.hide_on_modifier = (len == 4 && !strncmp((const char*)val, "hide", strlen("hide"))); |
238 |
+ config.hide_on_modifier = (len == 4 && !strncmp((const char*)val, "dock", strlen("dock")) ? M_DOCK |
239 |
+ : (len == 4 && !strncmp((const char*)val, "hide", strlen("hide")) ? M_HIDE |
240 |
+ : M_INVISIBLE)); |
241 |
+ return 1; |
242 |
+ } |
243 |
+ |
244 |
+ if (!strcmp(cur_key, "state")) { |
245 |
+ DLOG("state = %.*s, len = %d\n", len, val, len); |
246 |
+ config.state = (len == 4 && !strncmp((const char*)val, "hide", strlen("hide")) ? S_HIDE : S_SHOW); |
247 |
return 1; |
248 |
} |
249 |
|
b/i3bar/src/ipc.c
254 |
@@ -149,12 +149,36 @@ void got_mode_event(char *event) { |
255 |
draw_bars(false); |
256 |
} |
257 |
|
258 |
+/* |
259 |
+ * Called, when a barconfig_update event arrives (i.e. i3 changed the bar state) |
260 |
+ * |
261 |
+ */ |
262 |
+void got_bar_config_update(char *event) { |
263 |
+ /* check whether this affect this bar instance by checking the bar_id */ |
264 |
+ char* expected_id; |
265 |
+ sasprintf(&expected_id, "\"id\":\"%s\"", config.bar_id); |
266 |
+ if(strstr(event, expected_id) == NULL) |
267 |
+ return; |
268 |
+ FREE(expected_id); |
269 |
+ |
270 |
+ /* update the configuration with the received settings */ |
271 |
+ DLOG("Received bar config update \"%s\"\n", event); |
272 |
+ int old_mode = config.hide_on_modifier; |
273 |
+ parse_config_json(event); |
274 |
+ if (old_mode != config.hide_on_modifier) { |
275 |
+ reconfig_windows(); |
276 |
+ } |
277 |
|
278 |
-/* Data-structure to easily call the reply-handlers later */ |
279 |
+ draw_bars(false); |
280 |
+} |
281 |
+ |
282 |
+/* Data-structure to easily call the event-handlers later */ |
283 |
handler_t event_handlers[] = { |
284 |
&got_workspace_event, |
285 |
&got_output_event, |
286 |
- &got_mode_event |
287 |
+ &got_mode_event, |
288 |
+ NULL, |
289 |
+ &got_bar_config_update, |
290 |
}; |
291 |
|
292 |
/* |
293 |
@@ -310,8 +334,8 @@ void destroy_connection(void) { |
294 |
*/ |
295 |
void subscribe_events(void) { |
296 |
if (config.disable_ws) { |
297 |
- i3_send_msg(I3_IPC_MESSAGE_TYPE_SUBSCRIBE, "[ \"output\", \"mode\" ]"); |
298 |
+ i3_send_msg(I3_IPC_MESSAGE_TYPE_SUBSCRIBE, "[ \"output\", \"mode\", \"barconfig_update\" ]"); |
299 |
} else { |
300 |
- i3_send_msg(I3_IPC_MESSAGE_TYPE_SUBSCRIBE, "[ \"workspace\", \"output\", \"mode\" ]"); |
301 |
+ i3_send_msg(I3_IPC_MESSAGE_TYPE_SUBSCRIBE, "[ \"workspace\", \"output\", \"mode\", \"barconfig_update\" ]"); |
302 |
} |
303 |
} |
b/i3bar/src/xcb.c
308 |
@@ -198,7 +198,7 @@ void refresh_statusline(void) { |
309 |
* |
310 |
*/ |
311 |
void hide_bars(void) { |
312 |
- if (!config.hide_on_modifier) { |
313 |
+ if ((config.hide_on_modifier == M_DOCK) || (config.state == S_SHOW)) { |
314 |
return; |
315 |
} |
316 |
|
317 |
@@ -217,7 +217,7 @@ void hide_bars(void) { |
318 |
* |
319 |
*/ |
320 |
void unhide_bars(void) { |
321 |
- if (!config.hide_on_modifier) { |
322 |
+ if (config.hide_on_modifier != M_HIDE) { |
323 |
return; |
324 |
} |
325 |
|
326 |
@@ -988,25 +988,13 @@ char *init_xcb_early() { |
327 |
} |
328 |
|
329 |
/* |
330 |
- * Initialization which depends on 'config' being usable. Called after the |
331 |
- * configuration has arrived. |
332 |
+ * Register for xkb keyevents. To grab modifiers without blocking other applications from receiving key-events |
333 |
+ * involving that modifier, we sadly have to use xkb which is not yet fully supported |
334 |
+ * in xcb. |
335 |
* |
336 |
*/ |
337 |
-void init_xcb_late(char *fontname) { |
338 |
- if (fontname == NULL) |
339 |
- fontname = "-misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1"; |
340 |
- |
341 |
- /* Load the font */ |
342 |
- font = load_font(fontname, true); |
343 |
- set_font(&font); |
344 |
- DLOG("Calculated Font-height: %d\n", font.height); |
345 |
- |
346 |
- xcb_flush(xcb_connection); |
347 |
- |
348 |
- /* To grab modifiers without blocking other applications from receiving key-events |
349 |
- * involving that modifier, we sadly have to use xkb which is not yet fully supported |
350 |
- * in xcb */ |
351 |
- if (config.hide_on_modifier) { |
352 |
+void register_xkb_keyevents() { |
353 |
+ if (xkb_dpy == NULL) { |
354 |
int xkb_major, xkb_minor, xkb_errbase, xkb_err; |
355 |
xkb_major = XkbMajorVersion; |
356 |
xkb_minor = XkbMinorVersion; |
357 |
@@ -1047,6 +1035,39 @@ void init_xcb_late(char *fontname) { |
358 |
} |
359 |
|
360 |
/* |
361 |
+ * Deregister from xkb keyevents. |
362 |
+ * |
363 |
+ */ |
364 |
+void deregister_xkb_keyevents() { |
365 |
+ if (xkb_dpy != NULL) { |
366 |
+ ev_io_stop (main_loop, xkb_io); |
367 |
+ close(xkb_io->fd); |
368 |
+ FREE(xkb_io); |
369 |
+ FREE(xkb_dpy); |
370 |
+ } |
371 |
+} |
372 |
+ |
373 |
+/* |
374 |
+ * Initialization which depends on 'config' being usable. Called after the |
375 |
+ * configuration has arrived. |
376 |
+ * |
377 |
+ */ |
378 |
+void init_xcb_late(char *fontname) { |
379 |
+ if (fontname == NULL) |
380 |
+ fontname = "-misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1"; |
381 |
+ |
382 |
+ /* Load the font */ |
383 |
+ font = load_font(fontname, true); |
384 |
+ set_font(&font); |
385 |
+ DLOG("Calculated Font-height: %d\n", font.height); |
386 |
+ |
387 |
+ xcb_flush(xcb_connection); |
388 |
+ |
389 |
+ if (config.hide_on_modifier == M_HIDE) |
390 |
+ register_xkb_keyevents(); |
391 |
+} |
392 |
+ |
393 |
+/* |
394 |
* Inform clients waiting for a new _NET_SYSTEM_TRAY that we took the |
395 |
* selection. |
396 |
* |
397 |
@@ -1368,8 +1389,8 @@ void reconfig_windows(void) { |
398 |
mask = XCB_CW_BACK_PIXEL | XCB_CW_OVERRIDE_REDIRECT | XCB_CW_EVENT_MASK; |
399 |
/* Black background */ |
400 |
values[0] = colors.bar_bg; |
401 |
- /* If hide_on_modifier is set, i3 is not supposed to manage our bar-windows */ |
402 |
- values[1] = config.hide_on_modifier; |
403 |
+ /* If hide_on_modifier is set to hide or invisible mode, i3 is not supposed to manage our bar-windows */ |
404 |
+ values[1] = (config.hide_on_modifier == M_DOCK ? 0 : 1); |
405 |
/* We enable the following EventMask fields: |
406 |
* EXPOSURE, to get expose events (we have to re-draw then) |
407 |
* SUBSTRUCTURE_REDIRECT, to get ConfigureRequests when the tray |
408 |
@@ -1490,7 +1511,7 @@ void reconfig_windows(void) { |
409 |
|
410 |
/* We finally map the bar (display it on screen), unless the modifier-switch is on */ |
411 |
xcb_void_cookie_t map_cookie; |
412 |
- if (!config.hide_on_modifier) { |
413 |
+ if (config.hide_on_modifier == M_DOCK) { |
414 |
map_cookie = xcb_map_window_checked(xcb_connection, walk->bar); |
415 |
} |
416 |
|
417 |
@@ -1501,7 +1522,7 @@ void reconfig_windows(void) { |
418 |
xcb_request_failed(name_cookie, "Could not set WM_NAME") || |
419 |
xcb_request_failed(strut_cookie, "Could not set strut") || |
420 |
xcb_request_failed(gc_cookie, "Could not create graphical context") || |
421 |
- (!config.hide_on_modifier && xcb_request_failed(map_cookie, "Could not map window"))) { |
422 |
+ ((config.hide_on_modifier == M_DOCK) && xcb_request_failed(map_cookie, "Could not map window"))) { |
423 |
exit(EXIT_FAILURE); |
424 |
} |
425 |
|
426 |
@@ -1533,6 +1554,14 @@ void reconfig_windows(void) { |
427 |
mask, |
428 |
values); |
429 |
|
430 |
+ mask = XCB_CW_OVERRIDE_REDIRECT; |
431 |
+ values[0] = (config.hide_on_modifier == M_DOCK ? 0 : 1); |
432 |
+ DLOG("Changing Window attribute override_redirect for output %s to %d\n", walk->name, values[0]); |
433 |
+ xcb_void_cookie_t chg_cookie = xcb_change_window_attributes(xcb_connection, |
434 |
+ walk->bar, |
435 |
+ mask, |
436 |
+ values); |
437 |
+ |
438 |
DLOG("Recreating buffer for output %s\n", walk->name); |
439 |
xcb_void_cookie_t pm_cookie = xcb_create_pixmap_checked(xcb_connection, |
440 |
root_screen->root_depth, |
441 |
@@ -1541,10 +1570,27 @@ void reconfig_windows(void) { |
442 |
walk->rect.w, |
443 |
walk->rect.h); |
444 |
|
445 |
- if (xcb_request_failed(cfg_cookie, "Could not reconfigure window")) { |
446 |
- exit(EXIT_FAILURE); |
447 |
+ /* Unmap the window, and draw it again when in dock mode */ |
448 |
+ xcb_void_cookie_t umap_cookie = xcb_unmap_window_checked(xcb_connection, walk->bar); |
449 |
+ xcb_void_cookie_t map_cookie; |
450 |
+ if (config.hide_on_modifier == M_DOCK) { |
451 |
+ cont_child(); |
452 |
+ map_cookie = xcb_map_window_checked(xcb_connection, walk->bar); |
453 |
+ } |
454 |
+ |
455 |
+ if (config.hide_on_modifier == M_HIDE) { |
456 |
+ /* Switching to hide mode, register for keyevents */ |
457 |
+ register_xkb_keyevents(); |
458 |
+ } else { |
459 |
+ /* Switching to dock/invisible mode, deregister from keyevents */ |
460 |
+ deregister_xkb_keyevents(); |
461 |
} |
462 |
- if (xcb_request_failed(pm_cookie, "Could not create pixmap")) { |
463 |
+ |
464 |
+ if (xcb_request_failed(cfg_cookie, "Could not reconfigure window") || |
465 |
+ xcb_request_failed(chg_cookie, "Could not change window") || |
466 |
+ xcb_request_failed(pm_cookie, "Could not create pixmap") || |
467 |
+ xcb_request_failed(umap_cookie, "Could not unmap window") || |
468 |
+ ((config.hide_on_modifier == M_DOCK) && xcb_request_failed(map_cookie, "Could not map window"))) { |
469 |
exit(EXIT_FAILURE); |
470 |
} |
471 |
} |
472 |
@@ -1718,11 +1764,14 @@ void draw_bars(bool unhide) { |
473 |
i = 0; |
474 |
} |
475 |
|
476 |
+ /* Assure the bar is hidden/unhidden according to the specified state */ |
477 |
+ bool should_unhide = (config.state == S_SHOW || (unhide && config.state == S_HIDE)); |
478 |
+ bool should_hide = (config.hide_on_modifier == M_INVISIBLE); |
479 |
+ |
480 |
if (!mod_pressed) { |
481 |
- if (unhide) { |
482 |
- /* The urgent-hint should get noticed, so we unhide the bars shortly */ |
483 |
+ if ((unhide || should_unhide) && !should_hide) { |
484 |
unhide_bars(); |
485 |
- } else if (walks_away) { |
486 |
+ } else if (walks_away || should_hide) { |
487 |
FREE(last_urgent_ws); |
488 |
hide_bars(); |
489 |
} |
b/include/commands.h
494 |
@@ -265,4 +265,10 @@ void cmd_scratchpad_show(I3_CMD); |
495 |
*/ |
496 |
void cmd_rename_workspace(I3_CMD, char *old_name, char *new_name); |
497 |
|
498 |
+/** |
499 |
+ * Implementation of 'bar (state hide|show|toggle)|(mode dock|hide|invisible|toggle) [<bar_id>]' |
500 |
+ * |
501 |
+ */ |
502 |
+void cmd_bar(I3_CMD, char *bar_type, char *bar_value, char *bar_id); |
503 |
+ |
504 |
#endif |
b/include/config.h
509 |
@@ -199,6 +199,9 @@ struct Config { |
510 |
/* just ignore the popup, that is, don’t map it */ |
511 |
PDF_IGNORE = 2, |
512 |
} popup_during_fullscreen; |
513 |
+ |
514 |
+ /* The number of currently parsed barconfigs */ |
515 |
+ int number_barconfigs; |
516 |
}; |
517 |
|
518 |
/** |
519 |
@@ -226,8 +229,11 @@ struct Barconfig { |
520 |
* root window! */ |
521 |
char *socket_path; |
522 |
|
523 |
- /** Bar display mode (hide unless modifier is pressed or show in dock mode) */ |
524 |
- enum { M_DOCK = 0, M_HIDE = 1 } mode; |
525 |
+ /** Bar display mode (hide unless modifier is pressed or show in dock mode or always hide in invisible mode) */ |
526 |
+ enum { M_DOCK = 0, M_HIDE = 1, M_INVISIBLE = 2 } mode; |
527 |
+ |
528 |
+ /* The current state of the bar, which indicates whether it is hidden or shown */ |
529 |
+ enum { S_HIDE = 0, S_SHOW = 1 } state; |
530 |
|
531 |
/** Bar modifier (to show bar when in hide mode). */ |
532 |
enum { |
533 |
@@ -324,6 +330,12 @@ void grab_all_keys(xcb_connection_t *conn, bool bind_mode_switch); |
534 |
void switch_mode(const char *new_mode); |
535 |
|
536 |
/** |
537 |
+ * Sends the current bar configuration as an event to all barconfig_update listeners. |
538 |
+ * This update mechnism currently only includes the state and the mode in the config. |
539 |
+ * |
540 |
+ */void update_barconfig(); |
541 |
+ |
542 |
+/** |
543 |
* Returns a pointer to the Binding with the specified modifiers and keycode |
544 |
* or NULL if no such binding exists. |
545 |
* |
b/include/config_directives.h
550 |
@@ -62,6 +62,8 @@ CFGFUN(mode_binding, const char *bindtype, const char *modifiers, const char *ke |
551 |
|
552 |
CFGFUN(bar_font, const char *font); |
553 |
CFGFUN(bar_mode, const char *mode); |
554 |
+CFGFUN(bar_state, const char *state); |
555 |
+CFGFUN(bar_id, const char *bar_id); |
556 |
CFGFUN(bar_output, const char *output); |
557 |
CFGFUN(bar_verbose, const char *verbose); |
558 |
CFGFUN(bar_modifier, const char *modifier); |
b/include/i3/ipc.h
563 |
@@ -99,4 +99,7 @@ typedef struct i3_ipc_header { |
564 |
/* The window event will be triggered upon window changes */ |
565 |
#define I3_IPC_EVENT_WINDOW (I3_IPC_EVENT_MASK | 3) |
566 |
|
567 |
+/** Bar config update will be triggered to update the bar config */ |
568 |
+#define I3_IPC_EVENT_BARCONFIG_UPDATE (I3_IPC_EVENT_MASK | 4) |
569 |
+ |
570 |
#endif |
b/parser-specs/commands.spec
575 |
@@ -35,6 +35,7 @@ state INITIAL: |
576 |
'nop' -> NOP |
577 |
'scratchpad' -> SCRATCHPAD |
578 |
'mode' -> MODE |
579 |
+ 'bar' -> BAR |
580 |
|
581 |
state CRITERIA: |
582 |
ctype = 'class' -> CRITERION |
583 |
@@ -319,3 +320,24 @@ state NOP: |
584 |
state SCRATCHPAD: |
585 |
'show' |
586 |
-> call cmd_scratchpad_show() |
587 |
+ |
588 |
+# bar (state hide|show|toggle)|(mode dock|hide|invisible|toggle) [<bar_id>] |
589 |
+state BAR: |
590 |
+ bar_type = 'state' |
591 |
+ -> BAR_STATE |
592 |
+ bar_type = 'mode' |
593 |
+ -> BAR_MODE |
594 |
+ |
595 |
+state BAR_STATE: |
596 |
+ bar_value = 'hide', 'show', 'toggle' |
597 |
+ -> BAR_W_ID |
598 |
+ |
599 |
+state BAR_MODE: |
600 |
+ bar_value = 'dock', 'hide', 'invisible', 'toggle' |
601 |
+ -> BAR_W_ID |
602 |
+ |
603 |
+state BAR_W_ID: |
604 |
+ bar_id = word |
605 |
+ -> |
606 |
+ end |
607 |
+ -> call cmd_bar($bar_type, $bar_value, $bar_id) |
b/parser-specs/config.spec
612 |
@@ -349,6 +349,8 @@ state BAR: |
613 |
'status_command' -> BAR_STATUS_COMMAND |
614 |
'socket_path' -> BAR_SOCKET_PATH |
615 |
'mode' -> BAR_MODE |
616 |
+ 'state' -> BAR_STATE |
617 |
+ 'id' -> BAR_ID |
618 |
'modifier' -> BAR_MODIFIER |
619 |
'position' -> BAR_POSITION |
620 |
'output' -> BAR_OUTPUT |
621 |
@@ -378,9 +380,17 @@ state BAR_SOCKET_PATH: |
622 |
-> call cfg_bar_socket_path($path); BAR |
623 |
|
624 |
state BAR_MODE: |
625 |
- mode = 'dock', 'hide' |
626 |
+ mode = 'dock', 'hide', 'invisible' |
627 |
-> call cfg_bar_mode($mode); BAR |
628 |
|
629 |
+state BAR_STATE: |
630 |
+ state = 'hide', 'show' |
631 |
+ -> call cfg_bar_state($state); BAR |
632 |
+ |
633 |
+state BAR_ID: |
634 |
+ bar_id = word |
635 |
+ -> call cfg_bar_id($bar_id); BAR |
636 |
+ |
637 |
state BAR_MODIFIER: |
638 |
modifier = 'Mod1', 'Mod2', 'Mod3', 'Mod4', 'Mod5', 'Control', 'Ctrl', 'Shift' |
639 |
-> call cfg_bar_modifier($modifier); BAR |
b/src/commands.c
644 |
@@ -1632,6 +1632,8 @@ void cmd_reload(I3_CMD) { |
645 |
x_set_i3_atoms(); |
646 |
/* Send an IPC event just in case the ws names have changed */ |
647 |
ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, "{\"change\":\"reload\"}"); |
648 |
+ /* Send an update event for the barconfig just in case it has changed */ |
649 |
+ update_barconfig(); |
650 |
|
651 |
// XXX: default reply for now, make this a better reply |
652 |
ysuccess(true); |
653 |
@@ -1915,3 +1917,113 @@ void cmd_rename_workspace(I3_CMD, char *old_name, char *new_name) { |
654 |
|
655 |
ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, "{\"change\":\"rename\"}"); |
656 |
} |
657 |
+ |
658 |
+/* |
659 |
+ * Implementation of 'bar mode dock|hide|invisible|toggle [<bar_id>]' |
660 |
+ * |
661 |
+ */ |
662 |
+bool cmd_bar_mode(char *bar_mode, char *bar_id) { |
663 |
+ int mode; |
664 |
+ bool toggle = false; |
665 |
+ if (strcmp(bar_mode, "dock") == 0) |
666 |
+ mode = M_DOCK; |
667 |
+ else if (strcmp(bar_mode, "hide") == 0) |
668 |
+ mode = M_HIDE; |
669 |
+ else if (strcmp(bar_mode, "invisible") == 0) |
670 |
+ mode = M_INVISIBLE; |
671 |
+ else if (strcmp(bar_mode, "toggle") == 0) |
672 |
+ toggle = true; |
673 |
+ else { |
674 |
+ ELOG("Unknown bar mode \"%s\", this is a mismatch between code and parser spec.\n", bar_mode); |
675 |
+ return false; |
676 |
+ } |
677 |
+ |
678 |
+ bool changed_sth = false; |
679 |
+ Barconfig *current = NULL; |
680 |
+ TAILQ_FOREACH(current, &barconfigs, configs) { |
681 |
+ if (bar_id && strcmp(current->id, bar_id) != 0) |
682 |
+ continue; |
683 |
+ |
684 |
+ if (toggle) |
685 |
+ mode = (current->mode + 1) % 2; |
686 |
+ |
687 |
+ DLOG("Changing bar mode of bar_id '%s' to '%s (%d)'\n", current->id, bar_mode, mode); |
688 |
+ current->mode = mode; |
689 |
+ changed_sth = true; |
690 |
+ |
691 |
+ if (bar_id) |
692 |
+ break; |
693 |
+ } |
694 |
+ |
695 |
+ if (bar_id && !changed_sth) { |
696 |
+ DLOG("Changing bar mode of bar_id %s failed, bar_id not found.\n", bar_id); |
697 |
+ return false; |
698 |
+ } |
699 |
+ |
700 |
+ return true; |
701 |
+} |
702 |
+ |
703 |
+/* |
704 |
+ * Implementation of 'bar state hide|show|toggle [<bar_id>]' |
705 |
+ * |
706 |
+ */ |
707 |
+bool cmd_bar_state(char *bar_state, char *bar_id) { |
708 |
+ int state; |
709 |
+ bool toggle = false; |
710 |
+ if (strcmp(bar_state, "hide") == 0) |
711 |
+ state = S_HIDE; |
712 |
+ else if (strcmp(bar_state, "show") == 0) |
713 |
+ state = S_SHOW; |
714 |
+ else if (strcmp(bar_state, "toggle") == 0) |
715 |
+ toggle = true; |
716 |
+ else { |
717 |
+ ELOG("Unknown bar state \"%s\", this is a mismatch between code and parser spec.\n", bar_state); |
718 |
+ return false; |
719 |
+ } |
720 |
+ |
721 |
+ bool changed_sth = false; |
722 |
+ Barconfig *current = NULL; |
723 |
+ TAILQ_FOREACH(current, &barconfigs, configs) { |
724 |
+ if (bar_id && strcmp(current->id, bar_id) != 0) |
725 |
+ continue; |
726 |
+ |
727 |
+ if (toggle) |
728 |
+ state = (current->state + 1) % 2; |
729 |
+ |
730 |
+ DLOG("Changing bar state of bar_id '%s' to '%s (%d)'\n", current->id, bar_state, state); |
731 |
+ current->state = state; |
732 |
+ changed_sth = true; |
733 |
+ |
734 |
+ if (bar_id) |
735 |
+ break; |
736 |
+ } |
737 |
+ |
738 |
+ if (bar_id && !changed_sth) { |
739 |
+ DLOG("Changing bar state of bar_id %s failed, bar_id not found.\n", bar_id); |
740 |
+ return false; |
741 |
+ } |
742 |
+ |
743 |
+ return true; |
744 |
+} |
745 |
+ |
746 |
+/* |
747 |
+ * Implementation of 'bar (state hide|show|toggle)|(mode dock|hide|invisible|toggle) [<bar_id>]' |
748 |
+ * |
749 |
+ */ |
750 |
+void cmd_bar(I3_CMD, char *bar_type, char *bar_value, char *bar_id) { |
751 |
+ bool ret; |
752 |
+ if (strcmp(bar_type, "mode") == 0) |
753 |
+ ret = cmd_bar_mode(bar_value, bar_id); |
754 |
+ else if (strcmp(bar_type, "state") == 0) |
755 |
+ ret = cmd_bar_state(bar_value, bar_id); |
756 |
+ else { |
757 |
+ ELOG("Unknown bar option type \"%s\", this is a mismatch between code and parser spec.\n", bar_type); |
758 |
+ ret = false; |
759 |
+ } |
760 |
+ |
761 |
+ ysuccess(ret); |
762 |
+ if (!ret) |
763 |
+ return; |
764 |
+ |
765 |
+ update_barconfig(); |
766 |
+} |
b/src/config.c
771 |
@@ -211,6 +211,49 @@ void switch_mode(const char *new_mode) { |
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 state and the mode in the config. |
777 |
+ * |
778 |
+ */ |
779 |
+void update_barconfig() { |
780 |
+ Barconfig *current; |
781 |
+ TAILQ_FOREACH(current, &barconfigs, configs) { |
782 |
+ /* Build json message */ |
783 |
+ char *state; |
784 |
+ switch (current->state) { |
785 |
+ case S_SHOW: |
786 |
+ state ="show"; |
787 |
+ break; |
788 |
+ case S_HIDE: |
789 |
+ default: |
790 |
+ state = "hide"; |
791 |
+ break; |
792 |
+ } |
793 |
+ |
794 |
+ char *mode; |
795 |
+ switch (current->mode) { |
796 |
+ case M_HIDE: |
797 |
+ mode ="hide"; |
798 |
+ break; |
799 |
+ case M_INVISIBLE: |
800 |
+ mode ="invisible"; |
801 |
+ break; |
802 |
+ case M_DOCK: |
803 |
+ default: |
804 |
+ mode = "dock"; |
805 |
+ break; |
806 |
+ } |
807 |
+ |
808 |
+ /* Send an event to all barconfig listeners*/ |
809 |
+ char *event_msg; |
810 |
+ sasprintf(&event_msg, "{ \"id\":\"%s\", \"state\":\"%s\", \"mode\":\"%s\" }", current->id, state, mode); |
811 |
+ |
812 |
+ ipc_send_event("barconfig_update", I3_IPC_EVENT_BARCONFIG_UPDATE, event_msg); |
813 |
+ FREE(event_msg); |
814 |
+ } |
815 |
+} |
816 |
+ |
817 |
+/* |
818 |
* Get the path of the first configuration file found. If override_configpath |
819 |
* is specified, that path is returned and saved for further calls. Otherwise, |
820 |
* checks the home directory first, then the system directory first, always |
b/src/config_directives.c
825 |
@@ -452,7 +452,15 @@ CFGFUN(bar_font, const char *font) { |
826 |
} |
827 |
|
828 |
CFGFUN(bar_mode, const char *mode) { |
829 |
- current_bar.mode = (strcmp(mode, "hide") == 0 ? M_HIDE : M_DOCK); |
830 |
+ current_bar.mode = (strcmp(mode, "dock") == 0 ? M_DOCK : (strcmp(mode, "hide") == 0 ? M_HIDE : M_INVISIBLE)); |
831 |
+} |
832 |
+ |
833 |
+CFGFUN(bar_state, const char *state) { |
834 |
+ current_bar.state = (strcmp(state, "hide") == 0 ? S_HIDE : S_SHOW); |
835 |
+} |
836 |
+ |
837 |
+CFGFUN(bar_id, const char *bar_id) { |
838 |
+ current_bar.id = sstrdup(bar_id); |
839 |
} |
840 |
|
841 |
CFGFUN(bar_output, const char *output) { |
842 |
@@ -548,15 +556,11 @@ CFGFUN(bar_workspace_buttons, const char *value) { |
843 |
|
844 |
CFGFUN(bar_finish) { |
845 |
DLOG("\t new bar configuration finished, saving.\n"); |
846 |
- /* Generate a unique ID for this bar */ |
847 |
- current_bar.id = sstrdup("bar-XXXXXX"); |
848 |
- /* This works similar to mktemp in that it replaces the last six X with |
849 |
- * random letters, but without the restriction that the given buffer |
850 |
- * has to contain a valid path name. */ |
851 |
- char *x = current_bar.id + strlen("bar-"); |
852 |
- while (*x != '\0') { |
853 |
- *(x++) = (rand() % 26) + 'a'; |
854 |
- } |
855 |
+ /* Generate a unique ID for this bar if not already configured */ |
856 |
+ if (!current_bar.id) |
857 |
+ sasprintf(¤t_bar.id, "bar-%d", config.number_barconfigs); |
858 |
+ |
859 |
+ config.number_barconfigs++; |
860 |
|
861 |
/* If no font was explicitly set, we use the i3 font as default */ |
862 |
if (!current_bar.font && font_pattern) |
b/src/ipc.c
867 |
@@ -621,9 +621,29 @@ IPC_HANDLER(get_bar_config) { |
868 |
YSTR_IF_SET(socket_path); |
869 |
|
870 |
ystr("mode"); |
871 |
- if (config->mode == M_HIDE) |
872 |
- ystr("hide"); |
873 |
- else ystr("dock"); |
874 |
+ switch (config->mode) { |
875 |
+ case M_HIDE: |
876 |
+ ystr("hide"); |
877 |
+ break; |
878 |
+ case M_INVISIBLE: |
879 |
+ ystr("invisible"); |
880 |
+ break; |
881 |
+ case M_DOCK: |
882 |
+ default: |
883 |
+ ystr("dock"); |
884 |
+ break; |
885 |
+ } |
886 |
+ |
887 |
+ ystr("state"); |
888 |
+ switch (config->state) { |
889 |
+ case S_SHOW: |
890 |
+ ystr("show"); |
891 |
+ break; |
892 |
+ case S_HIDE: |
893 |
+ default: |
894 |
+ ystr("hide"); |
895 |
+ break; |
896 |
+ } |
897 |
|
898 |
ystr("modifier"); |
899 |
switch (config->modifier) { |
b/testcases/t/187-commands-parser.t
904 |
@@ -144,7 +144,7 @@ is(parser_calls("\nworkspace test"), |
905 |
################################################################################ |
906 |
|
907 |
is(parser_calls('unknown_literal'), |
908 |
- "ERROR: Expected one of these tokens: <end>, '[', 'move', 'exec', 'exit', 'restart', 'reload', 'border', 'layout', 'append_layout', 'workspace', 'focus', 'kill', 'open', 'fullscreen', 'split', 'floating', 'mark', 'resize', 'rename', 'nop', 'scratchpad', 'mode'\n" . |
909 |
+ "ERROR: Expected one of these tokens: <end>, '[', 'move', 'exec', 'exit', 'restart', 'reload', 'border', 'layout', 'append_layout', 'workspace', 'focus', 'kill', 'open', 'fullscreen', 'split', 'floating', 'mark', 'resize', 'rename', 'nop', 'scratchpad', 'mode', 'bar'\n" . |
910 |
"ERROR: Your command: unknown_literal\n" . |
911 |
"ERROR: ^^^^^^^^^^^^^^^", |
912 |
'error for unknown literal ok'); |
b/testcases/t/201-config-parser.t
917 |
@@ -627,7 +627,7 @@ EOT |
918 |
|
919 |
$expected = <<'EOT'; |
920 |
cfg_bar_output(LVDS-1) |
921 |
-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', '}' |
922 |
+ERROR: CONFIG: Expected one of these tokens: <end>, '#', 'set', 'i3bar_command', 'status_command', 'socket_path', 'mode', 'state', 'id', 'modifier', 'position', 'output', 'tray_output', 'font', 'workspace_buttons', 'verbose', 'colors', '}' |
923 |
ERROR: CONFIG: (in file <stdin>) |
924 |
ERROR: CONFIG: Line 1: bar { |
925 |
ERROR: CONFIG: Line 2: output LVDS-1 |