]> git.sur5r.net Git - i3/i3/commitdiff
introduced i3 command for changing the hidden state and the mode of i3bar
authorhaptix@web.de <haptix@web.de>
Sat, 25 May 2013 12:30:00 +0000 (14:30 +0200)
committerMichael Stapelberg <michael@stapelberg.de>
Sat, 25 May 2013 21:00:36 +0000 (23:00 +0200)
The hidden_state and mode of each i3bar instance can now be controlled from within i3.
Therefore, two new i3 command were introduced:
    _
    bar hidden_state show|hide|toggle [<bar_id>]

    show: always show the bar
    hide: normal hide mode
    toggle: toggle between show and hide (individually for each bar)
    _
    bar mode dock|hide|invisible|toggle [<bar_id>]

    hide,dock: like before
    invisible: always keep the bar hidden
    toggle: toggle between dock and hide (individually for each bar)

This patch introduces a hidden_state ("hidden_state hide|show") in the
barconfig, which indicates the current hidden_state of each i3bar
instance. It only affects the bar when in hide mode. Additionally, a new
invisible mode was introduced. In order to change the hidden_state or
mode of the bar from i3, a barconfig-update event was introduced, for
which a bar can subscribe and the bar then gets notified about the
currently set hidden_state and mode in its barconfig.

For convenience, an id field ("id <bar_id>") was added to the barconfig, where one can
set the desired id for the corresponding bar. If the id is not specified, i3 will
deterministically choose an id; otherwise, with the previous random approach for finding
a new id, which is actually not shared with i3bar, as it would determine its id on
startup, the event-subscription would be destroyed on reload. Still, this issue remains
when manually changing the bar_id in the config and then reloading.

fixes #833, #651

18 files changed:
docs/ipc
docs/userguide
i3bar/include/config.h
i3bar/src/config.c
i3bar/src/ipc.c
i3bar/src/xcb.c
include/commands.h
include/config.h
include/config_directives.h
include/i3/ipc.h
parser-specs/commands.spec
parser-specs/config.spec
src/commands.c
src/config.c
src/config_directives.c
src/ipc.c
testcases/t/187-commands-parser.t
testcases/t/201-config-parser.t

index 8cfb21d035659d2089a4c59a0a3809b205192c7f..99bc5852c3e8fd2864b1fe6bb489554e3ffbc412 100644 (file)
--- 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]]
index bc105b346ad53900ae3b1b3a32cc411cf7ff0347..09cd72bf3905d0f9a92e32bd9e410074052f66b5 100644 (file)
@@ -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 <dock|hide>
+mode <dock|hide|invisible>
+hidden_state <hide|show>
 modifier <Modifier>
 ----------------
 
@@ -1017,12 +1036,31 @@ modifier <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 <bar_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_id>]
+
+bar mode dock|hide|invisible|toggle [<bar_id>]
+---------------
+
+*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
index 4f6e8858f2f8798809fb17e3198d22e4f59cef56..4c01d68cec4d8aeb03be106081dd4f9576f034ab 100644 (file)
@@ -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;
index 6c7286c4afd62d5094de71b7c4e76bf56c3579e9..f5a2a342f48d756adca0ac79a238addac134fc7e 100644 (file)
@@ -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;
     }
 
index faab8e102890a84422b6fc8c5231d71c58c245b3..ca5b404a221d005501b614a746dd465bfa2d1e0d 100644 (file)
@@ -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\" ]");
     }
 }
index 981b0f5eeffa4a6d99e4ea18fb9308efbe37bf8e..a4f75e624f20ca4fa8c47e05e7424ec28fe56c1e 100644 (file)
@@ -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();
         }
index a517d83e09a04b71921993f3792c60325e0e0715..87f0ac1ac775f7bee549378b283c0d65d859072d 100644 (file)
@@ -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) [<bar_id>]'
+ *
+ */
+void cmd_bar(I3_CMD, char *bar_type, char *bar_value, char *bar_id);
+
 #endif
index 4bdcdbadfd9f157c05a134ab485b83dc2f5b6473..c7479b3afeabec83ac8d3a32096f6a300c762e98 100644 (file)
@@ -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.
index 1faaa9734f135c7d06fff46c721f935f3f958549..f9b7a47fdf2493c5b2af6101750b35f772b05932 100644 (file)
@@ -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);
index 2a3321b50302727281a6838baf893a5c345669d0..6a50ccc8701fffe7fb68084de093929bed9f5553 100644 (file)
@@ -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
index a4a01a88d0668385bb5c154d4c058b016771953a..12737a22838a010021a99cf67ffaae5b415ab352 100644 (file)
@@ -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) [<bar_id>]
+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)
index c6328a0eae0e1b70f063d2c46ce52962f8d56af9..fd13797b15d22710d12f8c5001b2463e6a6a0519 100644 (file)
@@ -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
index d817d630b63f84b472677fe009135a1f96f24056..f361f8670eae220889813dbadd234eedeba6162b 100644 (file)
@@ -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 [<bar_id>]'
+ *
+ */
+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 [<bar_id>]'
+ *
+ */
+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) [<bar_id>]'
+ *
+ */
+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();
+}
index 595aa435da66805e4baa9ea1f7f2b51ff0eace9c..337b83a84886bbbef4df14a16b90a60be9da3915 100644 (file)
@@ -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,
index a7fa3500526e356321edde1db5429658e1756a6d..0fac7006b807c4ae1b549e0fa0b2a4fe381cb276 100644 (file)
@@ -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(&current_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)
index 8161b1d27603e940e82a6bf213f9d15d2cc37159..4c41465b7636aa0766429747ecf1cc41462c58af 100644 (file)
--- 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) {
index 52070d56b1090b55b31e4d87b11dcf7e21767bab..65c72125861388a4ad80b0a39ad274320278ebe9 100644 (file)
@@ -144,7 +144,7 @@ is(parser_calls("\nworkspace test"),
 ################################################################################
 
 is(parser_calls('unknown_literal'),
-   "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" .
+   "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" .
    "ERROR: Your command: unknown_literal\n" .
    "ERROR:               ^^^^^^^^^^^^^^^",
    'error for unknown literal ok');
index 151103d7503d1f4770c46436445a66edc46de924..06588b111a617b10bdab7382eeadeca1bef692ac 100644 (file)
@@ -627,7 +627,7 @@ EOT
 
 $expected = <<'EOT';
 cfg_bar_output(LVDS-1)
-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', '}'
+ERROR: CONFIG: Expected one of these tokens: <end>, '#', 'set', 'i3bar_command', 'status_command', 'socket_path', 'mode', 'hidden_state', 'id', 'modifier', 'position', 'output', 'tray_output', 'font', 'workspace_buttons', 'verbose', 'colors', '}'
 ERROR: CONFIG: (in file <stdin>)
 ERROR: CONFIG: Line   1: bar {
 ERROR: CONFIG: Line   2:     output LVDS-1