From e3913093b6b233ec30ce7fc8504d095f55c5e1db Mon Sep 17 00:00:00 2001 From: "haptix@web.de" Date: Sat, 25 May 2013 14:30:00 +0200 Subject: [PATCH] introduced i3 command for changing the hidden state and the mode of i3bar 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 [] 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 [] 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 ") 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 --- docs/ipc | 21 ++++++ docs/userguide | 80 +++++++++++++++++++-- i3bar/include/config.h | 7 +- i3bar/src/config.c | 10 ++- i3bar/src/ipc.c | 33 +++++++-- i3bar/src/xcb.c | 107 ++++++++++++++++++++-------- include/commands.h | 6 ++ include/config.h | 16 ++++- include/config_directives.h | 2 + include/i3/ipc.h | 3 + parser-specs/commands.spec | 22 ++++++ parser-specs/config.spec | 12 +++- src/commands.c | 112 ++++++++++++++++++++++++++++++ src/config.c | 43 ++++++++++++ src/config_directives.c | 24 ++++--- src/ipc.c | 26 ++++++- testcases/t/187-commands-parser.t | 2 +- testcases/t/201-config-parser.t | 2 +- 18 files changed, 470 insertions(+), 58 deletions(-) diff --git a/docs/ipc b/docs/ipc index 8cfb21d0..99bc5852 100644 --- a/docs/ipc +++ b/docs/ipc @@ -626,6 +626,9 @@ mode (2):: window (3):: Sent when a client's window is successfully reparented (that is when i3 has finished fitting it into a container). +barconfig_update (4):: + Sent when the hidden_state or mode field in the barconfig of any bar + instance was updated. *Example:* -------------------------------------------------------------------- @@ -723,6 +726,24 @@ window title as "urxvt"). } --------------------------- +=== barconfig_update event + +This event consists of a single serialized map reporting on options from the +barconfig of the specified bar_id that were updated in i3. The map always +consists of a property +id (string)+, which specifies to which bar instance the +sent config update belongs, a property +hidden_state (string)+, which indicates +the hidden_state of an i3bar instance, and a property +mode (string)+, which +corresponds to the current mode. + +*Example:* +--------------------------- +{ + "id": "bar-0", + "hidden_state": "hide" + "mode": "hide" +} +--------------------------- + == See also (existing libraries) [[libraries]] diff --git a/docs/userguide b/docs/userguide index bc105b34..09cd72bf 100644 --- a/docs/userguide +++ b/docs/userguide @@ -996,20 +996,39 @@ bar { === Display mode -You can have i3bar either be visible permanently at one edge of the screen -(+dock+ mode) or make it show up when you press your modifier key (+hide+ +You can either have i3bar be visible permanently at one edge of the screen +(+dock+ mode) or make it show up when you press your modifier key (+hide+ mode). +It is also possible to force i3bar to always stay hidden (+invisible+ mode). The modifier key can be configured using the +modifier+ option. +The mode option can be changed during runtime through the +bar mode+ command. +On reload the mode will be reverted to its configured value. + The hide mode maximizes screen space that can be used for actual windows. Also, i3bar sends the +SIGSTOP+ and +SIGCONT+ signals to the statusline process to save battery power. -The default is dock mode; in hide mode, the default modifier is Mod4 (usually -the windows key). +Invisible mode allows to permanently maximize screen space, as the bar is never +shown. Thus, you can configure i3bar to not disturb you by popping up because +of an urgency hint or because the modifier key is pressed. + +In order to control whether i3bar is hidden or shown in hide mode, there exists +the hidden_state option, which has no effect in dock mode or invisible mode. It +indicates the current hidden_state of the bar: (1) The bar acts like in normal +hide mode, it is hidden and is only unhidden in case of urgency hints or by +pressing the modifier key (+hide+ state), or (2) it is drawn on top of the +currently visible workspace (+show+ state). + +Like the mode, the hidden_state can also be controlled through i3, this can be +done by using the +bar hidden_state+ command. + +The default mode is dock mode; in hide mode, the default modifier is Mod4 (usually +the windows key). The default value for the hidden_state is hide. *Syntax*: ---------------- -mode +mode +hidden_state modifier ---------------- @@ -1017,12 +1036,31 @@ modifier ---------------- bar { mode hide + hidden_state hide modifier Mod1 } ---------------- Available modifiers are Mod1-Mod5, Shift, Control (see +xmodmap(1)+). +=== Bar ID + +Specifies the bar ID for the configured bar instance. If this option is missing, +the ID is set to 'bar-x', where x corresponds to the position of the embedding +bar block in the config file ('bar-0', 'bar-1', ...). + +*Syntax*: +--------------------- +id +--------------------- + +*Example*: +--------------------- +bar { + id bar-1 +} +--------------------- + [[i3bar_position]] === Position @@ -1775,6 +1813,38 @@ bindsym $mod+minus scratchpad show bindsym mod4+s [title="^Sup ::"] scratchpad show ------------------------------------------------ +=== i3bar control + +There are two options in the configuration of each i3bar instance that can be +changed during runtime by invoking a command through i3. The commands +bar +hidden_state+ and +bar mode+ allow setting the current hidden_state +respectively mode option of each bar. It is also possible to toggle between +hide state and show state as well as between dock mode and hide mode. Each +i3bar instance can be controlled individually by specifying a bar_id, if none +is given, the command is executed for all bar instances. + +*Syntax*: +--------------- +bar hidden_state hide|show|toggle [] + +bar mode dock|hide|invisible|toggle [] +--------------- + +*Examples*: +------------------------------------------------ +# Toggle between hide state and show state +bindsym $mod+m bar hidden_state toggle + +# Toggle between dock mode and hide mode +bindsym $mod+n bar mode toggle + +# Set the bar instance with id 'bar-1' to switch to hide mode +bindsym $mod+b bar mode hide bar-1 + +# Set the bar instance with id 'bar-1' to always stay hidden +bindsym $mod+Shift+b bar mode invisible bar-1 +------------------------------------------------ + [[multi_monitor]] == Multiple monitors diff --git a/i3bar/include/config.h b/i3bar/include/config.h index 4f6e8858..4c01d68c 100644 --- a/i3bar/include/config.h +++ b/i3bar/include/config.h @@ -19,7 +19,6 @@ typedef enum { } position_t; typedef struct config_t { - int hide_on_modifier; int modifier; position_t position; int verbose; @@ -31,6 +30,12 @@ typedef struct config_t { char *tray_output; int num_outputs; char **outputs; + + /* Bar display mode (hide unless modifier is pressed or show in dock mode or always hide in invisible mode) */ + enum { M_DOCK = 0, M_HIDE = 1, M_INVISIBLE = 2 } hide_on_modifier; + + /* The current hidden_state of the bar, which indicates whether it is hidden or shown */ + enum { S_HIDE = 0, S_SHOW = 1 } hidden_state; } config_t; config_t config; diff --git a/i3bar/src/config.c b/i3bar/src/config.c index 6c7286c4..f5a2a342 100644 --- a/i3bar/src/config.c +++ b/i3bar/src/config.c @@ -73,7 +73,15 @@ static int config_string_cb(void *params_, const unsigned char *val, unsigned in if (!strcmp(cur_key, "mode")) { DLOG("mode = %.*s, len = %d\n", len, val, len); - config.hide_on_modifier = (len == 4 && !strncmp((const char*)val, "hide", strlen("hide"))); + config.hide_on_modifier = (len == 4 && !strncmp((const char*)val, "dock", strlen("dock")) ? M_DOCK + : (len == 4 && !strncmp((const char*)val, "hide", strlen("hide")) ? M_HIDE + : M_INVISIBLE)); + return 1; + } + + if (!strcmp(cur_key, "hidden_state")) { + DLOG("hidden_state = %.*s, len = %d\n", len, val, len); + config.hidden_state = (len == 4 && !strncmp((const char*)val, "hide", strlen("hide")) ? S_HIDE : S_SHOW); return 1; } diff --git a/i3bar/src/ipc.c b/i3bar/src/ipc.c index faab8e10..ca5b404a 100644 --- a/i3bar/src/ipc.c +++ b/i3bar/src/ipc.c @@ -149,12 +149,37 @@ void got_mode_event(char *event) { draw_bars(false); } +/* + * Called, when a barconfig_update event arrives (i.e. i3 changed the bar hidden_state or mode) + * + */ +void got_bar_config_update(char *event) { + /* check whether this affect this bar instance by checking the bar_id */ + char *expected_id; + sasprintf(&expected_id, "\"id\":\"%s\"", config.bar_id); + char *found_id = strstr(event, expected_id); + FREE(expected_id); + if (found_id == NULL) + return; + + /* update the configuration with the received settings */ + DLOG("Received bar config update \"%s\"\n", event); + int old_mode = config.hide_on_modifier; + parse_config_json(event); + if (old_mode != config.hide_on_modifier) { + reconfig_windows(); + } -/* Data-structure to easily call the reply-handlers later */ + draw_bars(false); +} + +/* Data-structure to easily call the event-handlers later */ handler_t event_handlers[] = { &got_workspace_event, &got_output_event, - &got_mode_event + &got_mode_event, + NULL, + &got_bar_config_update, }; /* @@ -310,8 +335,8 @@ void destroy_connection(void) { */ void subscribe_events(void) { if (config.disable_ws) { - i3_send_msg(I3_IPC_MESSAGE_TYPE_SUBSCRIBE, "[ \"output\", \"mode\" ]"); + i3_send_msg(I3_IPC_MESSAGE_TYPE_SUBSCRIBE, "[ \"output\", \"mode\", \"barconfig_update\" ]"); } else { - i3_send_msg(I3_IPC_MESSAGE_TYPE_SUBSCRIBE, "[ \"workspace\", \"output\", \"mode\" ]"); + i3_send_msg(I3_IPC_MESSAGE_TYPE_SUBSCRIBE, "[ \"workspace\", \"output\", \"mode\", \"barconfig_update\" ]"); } } diff --git a/i3bar/src/xcb.c b/i3bar/src/xcb.c index 981b0f5e..a4f75e62 100644 --- a/i3bar/src/xcb.c +++ b/i3bar/src/xcb.c @@ -198,7 +198,7 @@ void refresh_statusline(void) { * */ void hide_bars(void) { - if (!config.hide_on_modifier) { + if ((config.hide_on_modifier == M_DOCK) || (config.hidden_state == S_SHOW)) { return; } @@ -217,7 +217,7 @@ void hide_bars(void) { * */ void unhide_bars(void) { - if (!config.hide_on_modifier) { + if (config.hide_on_modifier != M_HIDE) { return; } @@ -988,25 +988,13 @@ char *init_xcb_early() { } /* - * Initialization which depends on 'config' being usable. Called after the - * configuration has arrived. + * Register for xkb keyevents. To grab modifiers without blocking other applications from receiving key-events + * involving that modifier, we sadly have to use xkb which is not yet fully supported + * in xcb. * */ -void init_xcb_late(char *fontname) { - if (fontname == NULL) - fontname = "-misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1"; - - /* Load the font */ - font = load_font(fontname, true); - set_font(&font); - DLOG("Calculated Font-height: %d\n", font.height); - - xcb_flush(xcb_connection); - - /* To grab modifiers without blocking other applications from receiving key-events - * involving that modifier, we sadly have to use xkb which is not yet fully supported - * in xcb */ - if (config.hide_on_modifier) { +void register_xkb_keyevents() { + if (xkb_dpy == NULL) { int xkb_major, xkb_minor, xkb_errbase, xkb_err; xkb_major = XkbMajorVersion; xkb_minor = XkbMinorVersion; @@ -1046,6 +1034,39 @@ void init_xcb_late(char *fontname) { } } +/* + * Deregister from xkb keyevents. + * + */ +void deregister_xkb_keyevents() { + if (xkb_dpy != NULL) { + ev_io_stop (main_loop, xkb_io); + close(xkb_io->fd); + FREE(xkb_io); + FREE(xkb_dpy); + } +} + +/* + * Initialization which depends on 'config' being usable. Called after the + * configuration has arrived. + * + */ +void init_xcb_late(char *fontname) { + if (fontname == NULL) + fontname = "-misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1"; + + /* Load the font */ + font = load_font(fontname, true); + set_font(&font); + DLOG("Calculated Font-height: %d\n", font.height); + + xcb_flush(xcb_connection); + + if (config.hide_on_modifier == M_HIDE) + register_xkb_keyevents(); +} + /* * Inform clients waiting for a new _NET_SYSTEM_TRAY that we took the * selection. @@ -1368,8 +1389,8 @@ void reconfig_windows(void) { mask = XCB_CW_BACK_PIXEL | XCB_CW_OVERRIDE_REDIRECT | XCB_CW_EVENT_MASK; /* Black background */ values[0] = colors.bar_bg; - /* If hide_on_modifier is set, i3 is not supposed to manage our bar-windows */ - values[1] = config.hide_on_modifier; + /* If hide_on_modifier is set to hide or invisible mode, i3 is not supposed to manage our bar-windows */ + values[1] = (config.hide_on_modifier == M_DOCK ? 0 : 1); /* We enable the following EventMask fields: * EXPOSURE, to get expose events (we have to re-draw then) * SUBSTRUCTURE_REDIRECT, to get ConfigureRequests when the tray @@ -1490,7 +1511,7 @@ void reconfig_windows(void) { /* We finally map the bar (display it on screen), unless the modifier-switch is on */ xcb_void_cookie_t map_cookie; - if (!config.hide_on_modifier) { + if (config.hide_on_modifier == M_DOCK) { map_cookie = xcb_map_window_checked(xcb_connection, walk->bar); } @@ -1501,7 +1522,7 @@ void reconfig_windows(void) { xcb_request_failed(name_cookie, "Could not set WM_NAME") || xcb_request_failed(strut_cookie, "Could not set strut") || xcb_request_failed(gc_cookie, "Could not create graphical context") || - (!config.hide_on_modifier && xcb_request_failed(map_cookie, "Could not map window"))) { + ((config.hide_on_modifier == M_DOCK) && xcb_request_failed(map_cookie, "Could not map window"))) { exit(EXIT_FAILURE); } @@ -1533,6 +1554,14 @@ void reconfig_windows(void) { mask, values); + mask = XCB_CW_OVERRIDE_REDIRECT; + values[0] = (config.hide_on_modifier == M_DOCK ? 0 : 1); + DLOG("Changing Window attribute override_redirect for output %s to %d\n", walk->name, values[0]); + xcb_void_cookie_t chg_cookie = xcb_change_window_attributes(xcb_connection, + walk->bar, + mask, + values); + DLOG("Recreating buffer for output %s\n", walk->name); xcb_void_cookie_t pm_cookie = xcb_create_pixmap_checked(xcb_connection, root_screen->root_depth, @@ -1541,10 +1570,27 @@ void reconfig_windows(void) { walk->rect.w, walk->rect.h); - if (xcb_request_failed(cfg_cookie, "Could not reconfigure window")) { - exit(EXIT_FAILURE); + /* Unmap the window, and draw it again when in dock mode */ + xcb_void_cookie_t umap_cookie = xcb_unmap_window_checked(xcb_connection, walk->bar); + xcb_void_cookie_t map_cookie; + if (config.hide_on_modifier == M_DOCK) { + cont_child(); + map_cookie = xcb_map_window_checked(xcb_connection, walk->bar); + } + + if (config.hide_on_modifier == M_HIDE) { + /* Switching to hide mode, register for keyevents */ + register_xkb_keyevents(); + } else { + /* Switching to dock/invisible mode, deregister from keyevents */ + deregister_xkb_keyevents(); } - if (xcb_request_failed(pm_cookie, "Could not create pixmap")) { + + if (xcb_request_failed(cfg_cookie, "Could not reconfigure window") || + xcb_request_failed(chg_cookie, "Could not change window") || + xcb_request_failed(pm_cookie, "Could not create pixmap") || + xcb_request_failed(umap_cookie, "Could not unmap window") || + ((config.hide_on_modifier == M_DOCK) && xcb_request_failed(map_cookie, "Could not map window"))) { exit(EXIT_FAILURE); } } @@ -1718,11 +1764,14 @@ void draw_bars(bool unhide) { i = 1; } + /* Assure the bar is hidden/unhidden according to the specified hidden_state and mode */ + bool should_unhide = (config.hidden_state == S_SHOW || (unhide && config.hidden_state == S_HIDE)); + bool should_hide = (config.hide_on_modifier == M_INVISIBLE); + if (!mod_pressed) { - if (unhide) { - /* The urgent-hint should get noticed, so we unhide the bars shortly */ + if ((unhide || should_unhide) && !should_hide) { unhide_bars(); - } else if (walks_away) { + } else if (walks_away || should_hide) { FREE(last_urgent_ws); hide_bars(); } diff --git a/include/commands.h b/include/commands.h index a517d83e..87f0ac1a 100644 --- a/include/commands.h +++ b/include/commands.h @@ -265,4 +265,10 @@ void cmd_scratchpad_show(I3_CMD); */ void cmd_rename_workspace(I3_CMD, char *old_name, char *new_name); +/** + * Implementation of 'bar (hidden_state hide|show|toggle)|(mode dock|hide|invisible|toggle) []' + * + */ +void cmd_bar(I3_CMD, char *bar_type, char *bar_value, char *bar_id); + #endif diff --git a/include/config.h b/include/config.h index 4bdcdbad..c7479b3a 100644 --- a/include/config.h +++ b/include/config.h @@ -199,6 +199,9 @@ struct Config { /* just ignore the popup, that is, don’t map it */ PDF_IGNORE = 2, } popup_during_fullscreen; + + /* The number of currently parsed barconfigs */ + int number_barconfigs; }; /** @@ -226,8 +229,11 @@ struct Barconfig { * root window! */ char *socket_path; - /** Bar display mode (hide unless modifier is pressed or show in dock mode) */ - enum { M_DOCK = 0, M_HIDE = 1 } mode; + /** Bar display mode (hide unless modifier is pressed or show in dock mode or always hide in invisible mode) */ + enum { M_DOCK = 0, M_HIDE = 1, M_INVISIBLE = 2 } mode; + + /* The current hidden_state of the bar, which indicates whether it is hidden or shown */ + enum { S_HIDE = 0, S_SHOW = 1 } hidden_state; /** Bar modifier (to show bar when in hide mode). */ enum { @@ -323,6 +329,12 @@ void grab_all_keys(xcb_connection_t *conn, bool bind_mode_switch); */ void switch_mode(const char *new_mode); +/** + * Sends the current bar configuration as an event to all barconfig_update listeners. + * This update mechnism currently only includes the hidden_state and the mode in the config. + * + */void update_barconfig(); + /** * Returns a pointer to the Binding with the specified modifiers and keycode * or NULL if no such binding exists. diff --git a/include/config_directives.h b/include/config_directives.h index 1faaa973..f9b7a47f 100644 --- a/include/config_directives.h +++ b/include/config_directives.h @@ -62,6 +62,8 @@ CFGFUN(mode_binding, const char *bindtype, const char *modifiers, const char *ke CFGFUN(bar_font, const char *font); CFGFUN(bar_mode, const char *mode); +CFGFUN(bar_hidden_state, const char *hidden_state); +CFGFUN(bar_id, const char *bar_id); CFGFUN(bar_output, const char *output); CFGFUN(bar_verbose, const char *verbose); CFGFUN(bar_modifier, const char *modifier); diff --git a/include/i3/ipc.h b/include/i3/ipc.h index 2a3321b5..6a50ccc8 100644 --- a/include/i3/ipc.h +++ b/include/i3/ipc.h @@ -99,4 +99,7 @@ typedef struct i3_ipc_header { /* The window event will be triggered upon window changes */ #define I3_IPC_EVENT_WINDOW (I3_IPC_EVENT_MASK | 3) +/** Bar config update will be triggered to update the bar config */ +#define I3_IPC_EVENT_BARCONFIG_UPDATE (I3_IPC_EVENT_MASK | 4) + #endif diff --git a/parser-specs/commands.spec b/parser-specs/commands.spec index a4a01a88..12737a22 100644 --- a/parser-specs/commands.spec +++ b/parser-specs/commands.spec @@ -35,6 +35,7 @@ state INITIAL: 'nop' -> NOP 'scratchpad' -> SCRATCHPAD 'mode' -> MODE + 'bar' -> BAR state CRITERIA: ctype = 'class' -> CRITERION @@ -319,3 +320,24 @@ state NOP: state SCRATCHPAD: 'show' -> call cmd_scratchpad_show() + +# bar (hidden_state hide|show|toggle)|(mode dock|hide|invisible|toggle) [] +state BAR: + bar_type = 'hidden_state' + -> BAR_HIDDEN_STATE + bar_type = 'mode' + -> BAR_MODE + +state BAR_HIDDEN_STATE: + bar_value = 'hide', 'show', 'toggle' + -> BAR_W_ID + +state BAR_MODE: + bar_value = 'dock', 'hide', 'invisible', 'toggle' + -> BAR_W_ID + +state BAR_W_ID: + bar_id = word + -> + end + -> call cmd_bar($bar_type, $bar_value, $bar_id) diff --git a/parser-specs/config.spec b/parser-specs/config.spec index c6328a0e..fd13797b 100644 --- a/parser-specs/config.spec +++ b/parser-specs/config.spec @@ -349,6 +349,8 @@ state BAR: 'status_command' -> BAR_STATUS_COMMAND 'socket_path' -> BAR_SOCKET_PATH 'mode' -> BAR_MODE + 'hidden_state' -> BAR_HIDDEN_STATE + 'id' -> BAR_ID 'modifier' -> BAR_MODIFIER 'position' -> BAR_POSITION 'output' -> BAR_OUTPUT @@ -378,9 +380,17 @@ state BAR_SOCKET_PATH: -> call cfg_bar_socket_path($path); BAR state BAR_MODE: - mode = 'dock', 'hide' + mode = 'dock', 'hide', 'invisible' -> call cfg_bar_mode($mode); BAR +state BAR_HIDDEN_STATE: + hidden_state = 'hide', 'show' + -> call cfg_bar_hidden_state($hidden_state); BAR + +state BAR_ID: + bar_id = word + -> call cfg_bar_id($bar_id); BAR + state BAR_MODIFIER: modifier = 'Mod1', 'Mod2', 'Mod3', 'Mod4', 'Mod5', 'Control', 'Ctrl', 'Shift' -> call cfg_bar_modifier($modifier); BAR diff --git a/src/commands.c b/src/commands.c index d817d630..f361f867 100644 --- a/src/commands.c +++ b/src/commands.c @@ -1632,6 +1632,8 @@ void cmd_reload(I3_CMD) { x_set_i3_atoms(); /* Send an IPC event just in case the ws names have changed */ ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, "{\"change\":\"reload\"}"); + /* Send an update event for the barconfig just in case it has changed */ + update_barconfig(); // XXX: default reply for now, make this a better reply ysuccess(true); @@ -1915,3 +1917,113 @@ void cmd_rename_workspace(I3_CMD, char *old_name, char *new_name) { ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, "{\"change\":\"rename\"}"); } + +/* + * Implementation of 'bar mode dock|hide|invisible|toggle []' + * + */ +bool cmd_bar_mode(char *bar_mode, char *bar_id) { + int mode; + bool toggle = false; + if (strcmp(bar_mode, "dock") == 0) + mode = M_DOCK; + else if (strcmp(bar_mode, "hide") == 0) + mode = M_HIDE; + else if (strcmp(bar_mode, "invisible") == 0) + mode = M_INVISIBLE; + else if (strcmp(bar_mode, "toggle") == 0) + toggle = true; + else { + ELOG("Unknown bar mode \"%s\", this is a mismatch between code and parser spec.\n", bar_mode); + return false; + } + + bool changed_sth = false; + Barconfig *current = NULL; + TAILQ_FOREACH(current, &barconfigs, configs) { + if (bar_id && strcmp(current->id, bar_id) != 0) + continue; + + if (toggle) + mode = (current->mode + 1) % 2; + + DLOG("Changing bar mode of bar_id '%s' to '%s (%d)'\n", current->id, bar_mode, mode); + current->mode = mode; + changed_sth = true; + + if (bar_id) + break; + } + + if (bar_id && !changed_sth) { + DLOG("Changing bar mode of bar_id %s failed, bar_id not found.\n", bar_id); + return false; + } + + return true; +} + +/* + * Implementation of 'bar hidden_state hide|show|toggle []' + * + */ +bool cmd_bar_hidden_state(char *bar_hidden_state, char *bar_id) { + int hidden_state; + bool toggle = false; + if (strcmp(bar_hidden_state, "hide") == 0) + hidden_state = S_HIDE; + else if (strcmp(bar_hidden_state, "show") == 0) + hidden_state = S_SHOW; + else if (strcmp(bar_hidden_state, "toggle") == 0) + toggle = true; + else { + ELOG("Unknown bar state \"%s\", this is a mismatch between code and parser spec.\n", bar_hidden_state); + return false; + } + + bool changed_sth = false; + Barconfig *current = NULL; + TAILQ_FOREACH(current, &barconfigs, configs) { + if (bar_id && strcmp(current->id, bar_id) != 0) + continue; + + if (toggle) + hidden_state = (current->hidden_state + 1) % 2; + + DLOG("Changing bar hidden_state of bar_id '%s' to '%s (%d)'\n", current->id, bar_hidden_state, hidden_state); + current->hidden_state = hidden_state; + changed_sth = true; + + if (bar_id) + break; + } + + if (bar_id && !changed_sth) { + DLOG("Changing bar hidden_state of bar_id %s failed, bar_id not found.\n", bar_id); + return false; + } + + return true; +} + +/* + * Implementation of 'bar (hidden_state hide|show|toggle)|(mode dock|hide|invisible|toggle) []' + * + */ +void cmd_bar(I3_CMD, char *bar_type, char *bar_value, char *bar_id) { + bool ret; + if (strcmp(bar_type, "mode") == 0) + ret = cmd_bar_mode(bar_value, bar_id); + else if (strcmp(bar_type, "hidden_state") == 0) + ret = cmd_bar_hidden_state(bar_value, bar_id); + else { + ELOG("Unknown bar option type \"%s\", this is a mismatch between code and parser spec.\n", bar_type); + ret = false; + } + + ysuccess(ret); + if (!ret) + return; + + update_barconfig(); +} diff --git a/src/config.c b/src/config.c index 595aa435..337b83a8 100644 --- a/src/config.c +++ b/src/config.c @@ -210,6 +210,49 @@ void switch_mode(const char *new_mode) { ELOG("ERROR: Mode not found\n"); } +/* + * Sends the current bar configuration as an event to all barconfig_update listeners. + * This update mechnism currently only includes the hidden_state and the mode in the config. + * + */ +void update_barconfig() { + Barconfig *current; + TAILQ_FOREACH(current, &barconfigs, configs) { + /* Build json message */ + char *hidden_state; + switch (current->hidden_state) { + case S_SHOW: + hidden_state ="show"; + break; + case S_HIDE: + default: + hidden_state = "hide"; + break; + } + + char *mode; + switch (current->mode) { + case M_HIDE: + mode ="hide"; + break; + case M_INVISIBLE: + mode ="invisible"; + break; + case M_DOCK: + default: + mode = "dock"; + break; + } + + /* Send an event to all barconfig listeners*/ + char *event_msg; + sasprintf(&event_msg, "{ \"id\":\"%s\", \"hidden_state\":\"%s\", \"mode\":\"%s\" }", current->id, hidden_state, mode); + + ipc_send_event("barconfig_update", I3_IPC_EVENT_BARCONFIG_UPDATE, event_msg); + FREE(event_msg); + } +} + /* * Get the path of the first configuration file found. If override_configpath * is specified, that path is returned and saved for further calls. Otherwise, diff --git a/src/config_directives.c b/src/config_directives.c index a7fa3500..0fac7006 100644 --- a/src/config_directives.c +++ b/src/config_directives.c @@ -452,7 +452,15 @@ CFGFUN(bar_font, const char *font) { } CFGFUN(bar_mode, const char *mode) { - current_bar.mode = (strcmp(mode, "hide") == 0 ? M_HIDE : M_DOCK); + current_bar.mode = (strcmp(mode, "dock") == 0 ? M_DOCK : (strcmp(mode, "hide") == 0 ? M_HIDE : M_INVISIBLE)); +} + +CFGFUN(bar_hidden_state, const char *hidden_state) { + current_bar.hidden_state = (strcmp(hidden_state, "hide") == 0 ? S_HIDE : S_SHOW); +} + +CFGFUN(bar_id, const char *bar_id) { + current_bar.id = sstrdup(bar_id); } CFGFUN(bar_output, const char *output) { @@ -548,15 +556,11 @@ CFGFUN(bar_workspace_buttons, const char *value) { CFGFUN(bar_finish) { DLOG("\t new bar configuration finished, saving.\n"); - /* Generate a unique ID for this bar */ - current_bar.id = sstrdup("bar-XXXXXX"); - /* This works similar to mktemp in that it replaces the last six X with - * random letters, but without the restriction that the given buffer - * has to contain a valid path name. */ - char *x = current_bar.id + strlen("bar-"); - while (*x != '\0') { - *(x++) = (rand() % 26) + 'a'; - } + /* Generate a unique ID for this bar if not already configured */ + if (!current_bar.id) + sasprintf(¤t_bar.id, "bar-%d", config.number_barconfigs); + + config.number_barconfigs++; /* If no font was explicitly set, we use the i3 font as default */ if (!current_bar.font && font_pattern) diff --git a/src/ipc.c b/src/ipc.c index 8161b1d2..4c41465b 100644 --- a/src/ipc.c +++ b/src/ipc.c @@ -621,9 +621,29 @@ IPC_HANDLER(get_bar_config) { YSTR_IF_SET(socket_path); ystr("mode"); - if (config->mode == M_HIDE) - ystr("hide"); - else ystr("dock"); + switch (config->mode) { + case M_HIDE: + ystr("hide"); + break; + case M_INVISIBLE: + ystr("invisible"); + break; + case M_DOCK: + default: + ystr("dock"); + break; + } + + ystr("hidden_state"); + switch (config->hidden_state) { + case S_SHOW: + ystr("show"); + break; + case S_HIDE: + default: + ystr("hide"); + break; + } ystr("modifier"); switch (config->modifier) { diff --git a/testcases/t/187-commands-parser.t b/testcases/t/187-commands-parser.t index 52070d56..65c72125 100644 --- a/testcases/t/187-commands-parser.t +++ b/testcases/t/187-commands-parser.t @@ -144,7 +144,7 @@ is(parser_calls("\nworkspace test"), ################################################################################ is(parser_calls('unknown_literal'), - "ERROR: Expected one of these tokens: , '[', 'move', 'exec', 'exit', 'restart', 'reload', 'border', 'layout', 'append_layout', 'workspace', 'focus', 'kill', 'open', 'fullscreen', 'split', 'floating', 'mark', 'resize', 'rename', 'nop', 'scratchpad', 'mode'\n" . + "ERROR: Expected one of these tokens: , '[', 'move', 'exec', 'exit', 'restart', 'reload', 'border', 'layout', 'append_layout', 'workspace', 'focus', 'kill', 'open', 'fullscreen', 'split', 'floating', 'mark', 'resize', 'rename', 'nop', 'scratchpad', 'mode', 'bar'\n" . "ERROR: Your command: unknown_literal\n" . "ERROR: ^^^^^^^^^^^^^^^", 'error for unknown literal ok'); diff --git a/testcases/t/201-config-parser.t b/testcases/t/201-config-parser.t index 151103d7..06588b11 100644 --- a/testcases/t/201-config-parser.t +++ b/testcases/t/201-config-parser.t @@ -627,7 +627,7 @@ EOT $expected = <<'EOT'; cfg_bar_output(LVDS-1) -ERROR: CONFIG: Expected one of these tokens: , '#', 'set', 'i3bar_command', 'status_command', 'socket_path', 'mode', 'modifier', 'position', 'output', 'tray_output', 'font', 'workspace_buttons', 'verbose', 'colors', '}' +ERROR: CONFIG: Expected one of these tokens: , '#', 'set', 'i3bar_command', 'status_command', 'socket_path', 'mode', 'hidden_state', 'id', 'modifier', 'position', 'output', 'tray_output', 'font', 'workspace_buttons', 'verbose', 'colors', '}' ERROR: CONFIG: (in file ) ERROR: CONFIG: Line 1: bar { ERROR: CONFIG: Line 2: output LVDS-1 -- 2.39.5