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 |