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 |