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; -/* Data-structure to easily call the reply-handlers later */ + /* 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(); + } + + 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 (xcb_request_failed(pm_cookie, "Could not create pixmap")) { + + 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(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