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