JasonWoof Got questions, comments, patches, etc.? Contact Jason Woofenden
Fix toggle_bar not working on empty workspaces.
[spectrwm.git] / spectrwm.c
index 0abdd65..423db92 100644 (file)
@@ -279,7 +279,6 @@ volatile sig_atomic_t   restart_wm = 0;
 xcb_timestamp_t                last_event_time = 0;
 int                    outputs = 0;
 int                    other_wm;
-int                    ss_enabled = 0;
 int                    xrandr_support;
 int                    xrandr_eventbase;
 unsigned int           numlockmask = 0;
@@ -349,10 +348,9 @@ double                     dialog_ratio = 0.6;
 char           *bar_argv[] = { NULL, NULL };
 int             bar_pipe[2];
 char            bar_ext[SWM_BAR_MAX];
+char            bar_ext_buf[SWM_BAR_MAX];
 char            bar_vertext[SWM_BAR_MAX];
 int             bar_version = 0;
-sig_atomic_t    bar_alarm = 0;
-int             bar_delay = 30;
 int             bar_enabled = 1;
 int             bar_border_width = 1;
 int             bar_at_bottom = 0;
@@ -494,6 +492,7 @@ struct workspace {
        int                     idx;            /* workspace index */
        char                    *name;          /* workspace name */
        int                     always_raise;   /* raise windows on focus */
+       int                     bar_enabled;    /* bar visibility */
        struct layout           *cur_layout;    /* current layout handlers */
        struct ws_win           *focus;         /* may be NULL */
        struct ws_win           *focus_prev;    /* may be NULL */
@@ -587,6 +586,8 @@ union arg {
 #define SWM_ARG_ID_MOVEDOWN    (101)
 #define SWM_ARG_ID_MOVELEFT    (102)
 #define SWM_ARG_ID_MOVERIGHT   (103)
+#define SWM_ARG_ID_BAR_TOGGLE  (110)
+#define SWM_ARG_ID_BAR_TOGGLE_WS       (111)
        char                    **argv;
 };
 
@@ -712,6 +713,7 @@ struct spawn_list           spawns = TAILQ_HEAD_INITIALIZER(spawns);
 /* user/key callable function IDs */
 enum keyfuncid {
        KF_BAR_TOGGLE,
+       KF_BAR_TOGGLE_WS,
        KF_BUTTON2,
        KF_CYCLE_LAYOUT,
        KF_FLIP_LAYOUT,
@@ -818,22 +820,21 @@ void       adjust_font(struct ws_win *);
 void    bar_class_name(char *, size_t, struct swm_region *);
 void    bar_class_title_name(char *, size_t, struct swm_region *);
 void    bar_cleanup(struct swm_region *);
+void    bar_extra_setup(void);
 void    bar_extra_stop(void);
+void    bar_extra_update(void);
 void    bar_fmt(const char *, char *, struct swm_region *, size_t);
 void    bar_fmt_expand(char *, size_t);
-void    bar_fmt_print(void);
+void    bar_draw(void);
 void    bar_print(struct swm_region *, const char *);
 void    bar_print_legacy(struct swm_region *, const char *);
-void    bar_refresh(void);
 void    bar_replace(char *, char *, struct swm_region *, size_t);
 void    bar_replace_pad(char *, int *, size_t);
 char *  bar_replace_seq(char *, char *, struct swm_region *, size_t *,
             size_t);
 void    bar_setup(struct swm_region *);
-void    bar_signal(int);
 void    bar_title_name(char *, size_t, struct swm_region *);
 void    bar_toggle(struct swm_region *, union arg *);
-void    bar_update(void);
 void    bar_urgent(char *, size_t);
 void    bar_window_float(char *, size_t, struct swm_region *);
 void    bar_window_name(char *, size_t, struct swm_region *);
@@ -2159,8 +2160,9 @@ bar_fmt_expand(char *fmtexp, size_t sz)
 #endif
 }
 
+/* Redraws the bar; need to follow with xcb_flush() or focus_flush(). */
 void
-bar_fmt_print(void)
+bar_draw(void)
 {
        char                    fmtexp[SWM_BAR_MAX], fmtnew[SWM_BAR_MAX];
        char                    fmtrep[SWM_BAR_MAX];
@@ -2175,6 +2177,14 @@ bar_fmt_print(void)
                TAILQ_FOREACH(r, &screens[i].rl, entry) {
                        if (r->bar == NULL)
                                continue;
+
+                       if (bar_enabled && r->ws->bar_enabled)
+                               xcb_map_window(conn, r->bar->id);
+                       else {
+                               xcb_unmap_window(conn, r->bar->id);
+                               continue;
+                       }
+
                        bar_fmt(fmtexp, fmtnew, r, sizeof fmtnew);
                        bar_replace(fmtnew, fmtrep, r, sizeof fmtrep);
                        if (bar_font_legacy)
@@ -2185,39 +2195,63 @@ bar_fmt_print(void)
        }
 }
 
+/* Reads external script output; call when stdin is readable. */
 void
-bar_update(void)
+bar_extra_update(void)
 {
-       size_t                  len;
-       char                    *b;
+       size_t          len;
+       char            b[SWM_BAR_MAX];
+       int             redraw = 0;
 
-       if (!bar_enabled)
-               return;
-       if (bar_extra && bar_extra_running) {
-               /* ignore short reads; it'll correct itself */
-               while ((b = fgetln(stdin, &len)) != NULL)
-                       if (b && b[len - 1] == '\n') {
-                               b[len - 1] = '\0';
-                               strlcpy(bar_ext, b, sizeof bar_ext);
+       if (bar_enabled && bar_extra && bar_extra_running) {
+               while (fgets(b, sizeof(b), stdin) != NULL) {
+                       len = strlen(b);
+                       if (b[len - 1] == '\n') {
+                               /* Remove newline. */
+                               b[--len] = '\0';
+
+                               /* "Clear" bar_ext. */
+                               bar_ext[0] = '\0';
+
+                               /* Flush buffered output. */
+                               strlcpy(bar_ext, bar_ext_buf, sizeof(bar_ext));
+                               bar_ext_buf[0] = '\0';
+
+                               /* Append new output to bar. */
+                               strlcat(bar_ext, b, sizeof(bar_ext));
+
+                               redraw = 1;
+                       } else {
+                               /* Buffer output. */
+                               strlcat(bar_ext_buf, b, sizeof(bar_ext_buf));
                        }
-               if (b == NULL && errno != EAGAIN) {
-                       warn("bar_update: bar_extra failed");
+               }
+
+               if (errno != EAGAIN) {
+                       warn("bar_action failed");
                        bar_extra_stop();
                }
-       } else
-               strlcpy(bar_ext, "", sizeof bar_ext);
+       } else {
+               /*
+                * Attempt to drain stdin, so it doesn't cause the main loop to
+                * call us as fast as it can.
+                */
+               while (fgets(b, sizeof(b), stdin) != NULL);
 
-       bar_fmt_print();
-       alarm(bar_delay);
-}
+               if (!bar_enabled)
+                       return;
 
-void
-bar_signal(int sig)
-{
-       /* suppress unused warning since var is needed */
-       (void)sig;
+               /* Clear bar script output if bar script is not running. */
+               if (bar_ext[0] != '\0') {
+                       bar_ext[0] = '\0';
+                       redraw = 1;
+               }
+       }
 
-       bar_alarm = 1;
+       if (redraw) {
+               bar_draw();
+               xcb_flush(conn);
+       }
 }
 
 void
@@ -2232,31 +2266,40 @@ bar_toggle(struct swm_region *r, union arg *args)
 
        DNPRINTF(SWM_D_BAR, "bar_toggle\n");
 
-       num_screens = xcb_setup_roots_length(xcb_get_setup(conn));
-       if (bar_enabled) {
-               for (i = 0; i < num_screens; i++)
-                       TAILQ_FOREACH(tmpr, &screens[i].rl, entry)
-                               if (tmpr->bar)
-                                       xcb_unmap_window(conn, tmpr->bar->id);
-       } else {
-               for (i = 0; i < num_screens; i++)
-                       TAILQ_FOREACH(tmpr, &screens[i].rl, entry)
-                               if (tmpr->bar)
-                                       xcb_map_window(conn, tmpr->bar->id);
+       switch (args->id) {
+       case SWM_ARG_ID_BAR_TOGGLE_WS:
+               /* Only change if master switch is enabled. */
+               if (bar_enabled)
+                       r->ws->bar_enabled = !r->ws->bar_enabled;
+               else
+                       bar_enabled = r->ws->bar_enabled = 1;
+               break;
+       case SWM_ARG_ID_BAR_TOGGLE:
+               bar_enabled = !bar_enabled;
+               break;
        }
 
-       bar_enabled = !bar_enabled;
+       /* update bars as necessary */
+       num_screens = xcb_setup_roots_length(xcb_get_setup(conn));
+       for (i = 0; i < num_screens; i++)
+               TAILQ_FOREACH(tmpr, &screens[i].rl, entry)
+                       if (tmpr->bar) {
+                               if (bar_enabled && tmpr->ws->bar_enabled)
+                                       xcb_map_window(conn, tmpr->bar->id);
+                               else
+                                       xcb_unmap_window(conn, tmpr->bar->id);
+                       }
 
        stack();
 
        /* must be after stack */
-       bar_update();
+       bar_draw();
 
        focus_flush();
 }
 
 void
-bar_refresh(void)
+bar_extra_setup(void)
 {
        struct swm_region       *r;
        uint32_t                wa[2];
@@ -2270,10 +2313,15 @@ bar_refresh(void)
                        err(1, "pipe error");
                socket_setnonblock(bar_pipe[0]);
                socket_setnonblock(bar_pipe[1]); /* XXX hmmm, really? */
+
+               /* Set stdin to read from the pipe. */
                if (dup2(bar_pipe[0], 0) == -1)
                        err(1, "dup2");
+
+               /* Set stdout to write to the pipe. */
                if (dup2(bar_pipe[1], 1) == -1)
                        err(1, "dup2");
+
                if (signal(SIGPIPE, SIG_IGN) == SIG_ERR)
                        err(1, "could not disable SIGPIPE");
                switch (bar_pid = fork()) {
@@ -2301,7 +2349,9 @@ bar_refresh(void)
                        xcb_change_window_attributes(conn, r->bar->id,
                            XCB_CW_BACK_PIXEL | XCB_CW_BORDER_PIXEL, wa);
                }
-       bar_update();
+
+       bar_draw();
+       xcb_flush(conn);
 }
 
 int
@@ -2467,9 +2517,7 @@ bar_setup(struct swm_region *r)
            "%d x %d\n", WINID(r->bar), X(r->bar), Y(r->bar), WIDTH(r->bar),
            HEIGHT(r->bar));
 
-       if (signal(SIGALRM, bar_signal) == SIG_ERR)
-               err(1, "could not install bar_signal");
-       bar_refresh();
+       bar_extra_setup();
 }
 
 void
@@ -2532,8 +2580,8 @@ version(struct swm_region *r, union arg *args)
                    "Version: %s Build: %s", SPECTRWM_VERSION, buildstr);
        else
                strlcpy(bar_vertext, "", sizeof bar_vertext);
-       bar_update();
 
+       bar_draw();
        xcb_flush(conn);
 }
 
@@ -3073,6 +3121,7 @@ unfocus_win(struct ws_win *win)
                kill_refs(win->ws->focus);
                win->ws->focus = NULL;
        }
+
        if (validate_win(win->ws->focus_prev)) {
                kill_refs(win->ws->focus_prev);
                win->ws->focus_prev = NULL;
@@ -3102,6 +3151,9 @@ focus_win(struct ws_win *win)
        if (win->ws == NULL)
                goto out;
 
+       if (!win->mapped)
+               goto out;
+
        ws = win->ws;
 
        if (validate_ws(ws))
@@ -3187,7 +3239,7 @@ focus_win(struct ws_win *win)
        }
 
 out:
-       bar_update();
+       bar_draw();
 
        DNPRINTF(SWM_D_FOCUS, "focus_win: done.\n");
 }
@@ -3309,7 +3361,7 @@ switchws(struct swm_region *r, union arg *args)
 
        /* Clear bar if new ws is empty. */
        if (new_ws->focus_pending == NULL)
-               bar_update();
+               bar_draw();
 
        focus_flush();
 
@@ -3418,7 +3470,7 @@ cyclescr(struct swm_region *r, union arg *args)
                                rr->s[i].root, XCB_CURRENT_TIME);
 
                /* Clear bar since empty. */
-               bar_update();
+               bar_draw();
        }
 
        focus_flush();
@@ -3570,37 +3622,70 @@ get_focus_prev(struct ws_win *win)
        if (winfocus == NULL || winfocus == win) {
                switch (focus_close) {
                case SWM_STACK_BOTTOM:
-                       winfocus = TAILQ_FIRST(wl);
+                       TAILQ_FOREACH(winfocus, wl, entry)
+                               if (!winfocus->iconic && winfocus != cur_focus)
+                                       break;
                        break;
                case SWM_STACK_TOP:
-                       winfocus = TAILQ_LAST(wl, ws_win_list);
+                       TAILQ_FOREACH_REVERSE(winfocus, wl, ws_win_list, entry)
+                               if (!winfocus->iconic && winfocus != cur_focus)
+                                       break;
                        break;
                case SWM_STACK_ABOVE:
-                       if ((winfocus = TAILQ_NEXT(cur_focus, entry)) == NULL) {
-                               if (focus_close_wrap)
-                                       winfocus = TAILQ_FIRST(wl);
-                               else
-                                       winfocus = TAILQ_PREV(cur_focus,
-                                           ws_win_list, entry);
+                       winfocus = TAILQ_NEXT(cur_focus, entry);
+                       while (winfocus && winfocus->iconic)
+                               winfocus = TAILQ_NEXT(winfocus, entry);
+
+                       if (winfocus == NULL) {
+                               if (focus_close_wrap) {
+                                       TAILQ_FOREACH(winfocus, wl, entry)
+                                               if (!winfocus->iconic &&
+                                                   winfocus != cur_focus)
+                                                       break;
+                               } else {
+                                       TAILQ_FOREACH_REVERSE(winfocus, wl,
+                                           ws_win_list, entry)
+                                               if (!winfocus->iconic &&
+                                                   winfocus != cur_focus)
+                                                       break;
+                               }
                        }
                        break;
                case SWM_STACK_BELOW:
-                       if ((winfocus = TAILQ_PREV(cur_focus, ws_win_list,
-                           entry)) == NULL) {
-                               if (focus_close_wrap)
-                                       winfocus = TAILQ_LAST(wl, ws_win_list);
-                               else
-                                       winfocus = TAILQ_NEXT(cur_focus, entry);
+                       winfocus = TAILQ_PREV(cur_focus, ws_win_list, entry);
+                       while (winfocus && winfocus->iconic)
+                               winfocus = TAILQ_PREV(winfocus, ws_win_list,
+                                   entry);
+
+                       if (winfocus == NULL) {
+                               if (focus_close_wrap) {
+                                       TAILQ_FOREACH_REVERSE(winfocus, wl,
+                                           ws_win_list, entry)
+                                               if (!winfocus->iconic &&
+                                                   winfocus != cur_focus)
+                                                       break;
+                               } else {
+                                       TAILQ_FOREACH(winfocus, wl, entry)
+                                               if (!winfocus->iconic &&
+                                                   winfocus != cur_focus)
+                                                       break;
+                               }
                        }
                        break;
                }
        }
 done:
-       if (winfocus == NULL) {
-               if (focus_default == SWM_STACK_TOP)
-                       winfocus = TAILQ_LAST(wl, ws_win_list);
-               else
-                       winfocus = TAILQ_FIRST(wl);
+       if (winfocus == NULL ||
+           (winfocus && (winfocus->iconic || winfocus == cur_focus))) {
+               if (focus_default == SWM_STACK_TOP) {
+                       TAILQ_FOREACH_REVERSE(winfocus, wl, ws_win_list, entry)
+                               if (!winfocus->iconic && winfocus != cur_focus)
+                                       break;
+               } else {
+                       TAILQ_FOREACH(winfocus, wl, entry)
+                               if (!winfocus->iconic && winfocus != cur_focus)
+                                       break;
+               }
        }
 
        kill_refs(win);
@@ -3731,7 +3816,7 @@ cycle_layout(struct swm_region *r, union arg *args)
                ws->cur_layout = &layouts[0];
 
        stack();
-       bar_update();
+       bar_draw();
 
        focus_win(get_region_focus(r));
 
@@ -3751,7 +3836,7 @@ stack_config(struct swm_region *r, union arg *args)
 
        if (args->id != SWM_ARG_ID_STACKINIT)
                stack();
-       bar_update();
+       bar_draw();
 
        focus_flush();
 }
@@ -3780,7 +3865,7 @@ stack(void) {
                        g = r->g;
                        g.w -= 2 * border_width;
                        g.h -= 2 * border_width;
-                       if (bar_enabled) {
+                       if (bar_enabled && r->ws->bar_enabled) {
                                if (!bar_at_bottom)
                                        g.y += bar_height;
                                g.h -= bar_height;
@@ -4073,7 +4158,8 @@ stack_master(struct workspace *ws, struct swm_geometry *g, int rot, int flip)
                else
                        win_g.y += last_h + 2 * border_width;
 
-               if (disable_border && !bar_enabled && winno == 1){
+               if (disable_border && !(bar_enabled && ws->bar_enabled) &&
+                   winno == 1){
                        bordered = 0;
                        win_g.w += 2 * border_width;
                        win_g.h += 2 * border_width;
@@ -4290,7 +4376,7 @@ max_stack(struct workspace *ws, struct swm_geometry *g)
                if (X(w) != gg.x || Y(w) != gg.y || WIDTH(w) != gg.w ||
                    HEIGHT(w) != gg.h) {
                        w->g = gg;
-                       if (bar_enabled){
+                       if (bar_enabled && ws->bar_enabled){
                                w->bordered = 1;
                        } else {
                                w->bordered = 0;
@@ -4302,8 +4388,8 @@ max_stack(struct workspace *ws, struct swm_geometry *g)
                }
 
                /* Unmap unwanted windows if not multi-screen. */
-               if (!(num_screens > 1 || outputs > 1) && (w != win ||
-                   w != parent || w->transient != win->id))
+               if (num_screens <= 1 && outputs <= 1 && w != win &&
+                   w != parent && w->transient != win->id)
                        unmap_window(w);
        }
 
@@ -5381,7 +5467,8 @@ struct keyfunc {
        union arg               args;
 } keyfuncs[KF_INVALID + 1] = {
        /* name                 function        argument */
-       { "bar_toggle",         bar_toggle,     {0} },
+       { "bar_toggle",         bar_toggle,     {.id = SWM_ARG_ID_BAR_TOGGLE} },
+       { "bar_toggle_ws",      bar_toggle,     {.id = SWM_ARG_ID_BAR_TOGGLE_WS} },
        { "button2",            pressbutton,    {2} },
        { "cycle_layout",       cycle_layout,   {0} },
        { "flip_layout",        stack_config,   {.id = SWM_ARG_ID_FLIPLAYOUT} },
@@ -6080,6 +6167,7 @@ setup_keys(void)
        setkeybinding(MODKEY_SHIFT,     XK_F11,         KF_MVWS_21,     NULL);
        setkeybinding(MODKEY_SHIFT,     XK_F12,         KF_MVWS_22,     NULL);
        setkeybinding(MODKEY,           XK_b,           KF_BAR_TOGGLE,  NULL);
+       setkeybinding(MODKEY_SHIFT,     XK_b,           KF_BAR_TOGGLE_WS,NULL);
        setkeybinding(MODKEY,           XK_Tab,         KF_FOCUS_NEXT,  NULL);
        setkeybinding(MODKEY_SHIFT,     XK_Tab,         KF_FOCUS_PREV,  NULL);
        setkeybinding(MODKEY_SHIFT,     XK_x,           KF_WIND_KILL,   NULL);
@@ -6182,7 +6270,7 @@ grabkeys(void)
 {
        struct key              *kp;
        int                     num_screens, k, j;
-       unsigned int            modifiers[3];
+       unsigned int            modifiers[4];
        xcb_keycode_t           *code;
 
        DNPRINTF(SWM_D_MISC, "grabkeys\n");
@@ -6190,7 +6278,8 @@ grabkeys(void)
 
        modifiers[0] = 0;
        modifiers[1] = numlockmask;
-       modifiers[2] = numlockmask | XCB_MOD_MASK_LOCK;
+       modifiers[2] = XCB_MOD_MASK_LOCK;
+       modifiers[3] = numlockmask | XCB_MOD_MASK_LOCK;
 
        num_screens = xcb_setup_roots_length(xcb_get_setup(conn));
        for (k = 0; k < num_screens; k++) {
@@ -6206,7 +6295,7 @@ grabkeys(void)
                                            screens[k].root,
                                            kp->mod | modifiers[j],
                                            *code, XCB_GRAB_MODE_SYNC,
-                                           XCB_GRAB_MODE_ASYNC);
+                                           XCB_GRAB_MODE_SYNC);
                                free(code);
                        }
                }
@@ -6216,16 +6305,25 @@ grabkeys(void)
 void
 grabbuttons(struct ws_win *win)
 {
-       int             i;
+       unsigned int    modifiers[4];
+       int             i, j;
 
        DNPRINTF(SWM_D_MOUSE, "grabbuttons: win 0x%x\n", win->id);
+       updatenumlockmask();
+
+       modifiers[0] = 0;
+       modifiers[1] = numlockmask;
+       modifiers[2] = XCB_MOD_MASK_LOCK;
+       modifiers[3] = numlockmask | XCB_MOD_MASK_LOCK;
 
        for (i = 0; i < LENGTH(buttons); i++)
                if (buttons[i].action == client_click)
-                       xcb_grab_button(conn, 0, win->id, BUTTONMASK,
-                           XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC,
-                           XCB_WINDOW_NONE, XCB_CURSOR_NONE,
-                           buttons[i].button, buttons[i].mask);
+                       for (j = 0; j < LENGTH(modifiers); ++j)
+                               xcb_grab_button(conn, 0, win->id, BUTTONMASK,
+                                   XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC,
+                                   XCB_WINDOW_NONE, XCB_CURSOR_NONE,
+                                   buttons[i].button, buttons[i].mask |
+                                   modifiers[j]);
 }
 
 const char *quirkname[] = {
@@ -6400,6 +6498,7 @@ enum {
        SWM_S_BAR_BORDER_WIDTH,
        SWM_S_BAR_DELAY,
        SWM_S_BAR_ENABLED,
+       SWM_S_BAR_ENABLED_WS,
        SWM_S_BAR_FONT,
        SWM_S_BAR_FORMAT,
        SWM_S_BAR_JUSTIFY,
@@ -6431,8 +6530,9 @@ enum {
 int
 setconfvalue(char *selector, char *value, int flags)
 {
-       int     i;
-       char    *b;
+       struct workspace        *ws;
+       int                     i, ws_id, num_screens;
+       char                    *b;
 
        /* suppress unused warning since var is needed */
        (void)selector;
@@ -6452,11 +6552,23 @@ setconfvalue(char *selector, char *value, int flags)
                        bar_border_width = 0;
                break;
        case SWM_S_BAR_DELAY:
-               bar_delay = atoi(value);
+               /* No longer needed; leave to not break old conf files. */
                break;
        case SWM_S_BAR_ENABLED:
                bar_enabled = atoi(value);
                break;
+       case SWM_S_BAR_ENABLED_WS:
+               ws_id = atoi(selector) - 1;
+               if (ws_id < 0 || ws_id >= workspace_limit)
+                       errx(1, "setconfvalue: bar_enabled_ws: invalid "
+                           "workspace %d.", ws_id + 1);
+
+               num_screens = xcb_setup_roots_length(xcb_get_setup(conn));
+               for (i = 0; i < num_screens; i++) {
+                       ws = (struct workspace *)&screens[i].ws;
+                       ws[ws_id].bar_enabled = atoi(value);
+               }
+               break;
        case SWM_S_BAR_FONT:
                b = bar_fonts;
                if (asprintf(&bar_fonts, "%s,%s", value, bar_fonts) == -1)
@@ -6573,9 +6685,10 @@ setconfvalue(char *selector, char *value, int flags)
                setconfspawn("spawn_term", value, 0);
                break;
        case SWM_S_SS_APP:
+               /* No longer needed; leave to not break old conf files. */
                break;
        case SWM_S_SS_ENABLED:
-               ss_enabled = atoi(value);
+               /* No longer needed; leave to not break old conf files. */
                break;
        case SWM_S_STACK_ENABLED:
                stack_enabled = atoi(value);
@@ -6821,50 +6934,51 @@ struct config_option {
        int                     funcflags;
 };
 struct config_option configopt[] = {
-       { "bar_enabled",                setconfvalue,   SWM_S_BAR_ENABLED },
+       { "autorun",                    setautorun,     0 },
+       { "bar_action",                 setconfvalue,   SWM_S_BAR_ACTION },
        { "bar_at_bottom",              setconfvalue,   SWM_S_BAR_AT_BOTTOM },
        { "bar_border",                 setconfcolor,   SWM_S_COLOR_BAR_BORDER },
        { "bar_border_width",           setconfvalue,   SWM_S_BAR_BORDER_WIDTH },
        { "bar_color",                  setconfcolor,   SWM_S_COLOR_BAR },
-       { "bar_font_color",             setconfcolor,   SWM_S_COLOR_BAR_FONT },
-       { "bar_font",                   setconfvalue,   SWM_S_BAR_FONT },
-       { "bar_action",                 setconfvalue,   SWM_S_BAR_ACTION },
        { "bar_delay",                  setconfvalue,   SWM_S_BAR_DELAY },
-       { "bar_justify",                setconfvalue,   SWM_S_BAR_JUSTIFY },
+       { "bar_enabled",                setconfvalue,   SWM_S_BAR_ENABLED },
+       { "bar_enabled_ws",             setconfvalue,   SWM_S_BAR_ENABLED_WS },
+       { "bar_font",                   setconfvalue,   SWM_S_BAR_FONT },
+       { "bar_font_color",             setconfcolor,   SWM_S_COLOR_BAR_FONT },
        { "bar_format",                 setconfvalue,   SWM_S_BAR_FORMAT },
-       { "keyboard_mapping",           setkeymapping,  0 },
+       { "bar_justify",                setconfvalue,   SWM_S_BAR_JUSTIFY },
        { "bind",                       setconfbinding, 0 },
-       { "stack_enabled",              setconfvalue,   SWM_S_STACK_ENABLED },
+       { "border_width",               setconfvalue,   SWM_S_BORDER_WIDTH },
        { "clock_enabled",              setconfvalue,   SWM_S_CLOCK_ENABLED },
        { "clock_format",               setconfvalue,   SWM_S_CLOCK_FORMAT },
        { "color_focus",                setconfcolor,   SWM_S_COLOR_FOCUS },
        { "color_unfocus",              setconfcolor,   SWM_S_COLOR_UNFOCUS },
        { "cycle_empty",                setconfvalue,   SWM_S_CYCLE_EMPTY },
        { "cycle_visible",              setconfvalue,   SWM_S_CYCLE_VISIBLE },
-       { "workspace_limit",            setconfvalue,   SWM_S_WORKSPACE_LIMIT },
        { "dialog_ratio",               setconfvalue,   SWM_S_DIALOG_RATIO },
-       { "verbose_layout",             setconfvalue,   SWM_S_VERBOSE_LAYOUT },
+       { "disable_border",             setconfvalue,   SWM_S_DISABLE_BORDER },
+       { "focus_close",                setconfvalue,   SWM_S_FOCUS_CLOSE },
+       { "focus_close_wrap",           setconfvalue,   SWM_S_FOCUS_CLOSE_WRAP },
+       { "focus_default",              setconfvalue,   SWM_S_FOCUS_DEFAULT },
+       { "focus_mode",                 setconfvalue,   SWM_S_FOCUS_MODE },
+       { "keyboard_mapping",           setkeymapping,  0 },
+       { "layout",                     setlayout,      0 },
        { "modkey",                     setconfmodkey,  0 },
        { "program",                    setconfspawn,   0 },
        { "quirk",                      setconfquirk,   0 },
        { "region",                     setconfregion,  0 },
-       { "spawn_term",                 setconfvalue,   SWM_S_SPAWN_TERM },
-       { "screenshot_enabled",         setconfvalue,   SWM_S_SS_ENABLED },
        { "screenshot_app",             setconfvalue,   SWM_S_SS_APP },
-       { "window_name_enabled",        setconfvalue,   SWM_S_WINDOW_NAME_ENABLED },
-       { "urgent_enabled",             setconfvalue,   SWM_S_URGENT_ENABLED },
+       { "screenshot_enabled",         setconfvalue,   SWM_S_SS_ENABLED },
+       { "spawn_position",             setconfvalue,   SWM_S_SPAWN_ORDER },
+       { "spawn_term",                 setconfvalue,   SWM_S_SPAWN_TERM },
+       { "stack_enabled",              setconfvalue,   SWM_S_STACK_ENABLED },
        { "term_width",                 setconfvalue,   SWM_S_TERM_WIDTH },
        { "title_class_enabled",        setconfvalue,   SWM_S_TITLE_CLASS_ENABLED },
        { "title_name_enabled",         setconfvalue,   SWM_S_TITLE_NAME_ENABLED },
-       { "focus_mode",                 setconfvalue,   SWM_S_FOCUS_MODE },
-       { "focus_close",                setconfvalue,   SWM_S_FOCUS_CLOSE },
-       { "focus_close_wrap",           setconfvalue,   SWM_S_FOCUS_CLOSE_WRAP },
-       { "focus_default",              setconfvalue,   SWM_S_FOCUS_DEFAULT },
-       { "spawn_position",             setconfvalue,   SWM_S_SPAWN_ORDER },
-       { "disable_border",             setconfvalue,   SWM_S_DISABLE_BORDER },
-       { "border_width",               setconfvalue,   SWM_S_BORDER_WIDTH },
-       { "autorun",                    setautorun,     0 },
-       { "layout",                     setlayout,      0 },
+       { "urgent_enabled",             setconfvalue,   SWM_S_URGENT_ENABLED },
+       { "verbose_layout",             setconfvalue,   SWM_S_VERBOSE_LAYOUT },
+       { "window_name_enabled",        setconfvalue,   SWM_S_WINDOW_NAME_ENABLED },
+       { "workspace_limit",            setconfvalue,   SWM_S_WORKSPACE_LIMIT },
 };
 
 int
@@ -7377,7 +7491,7 @@ expose(xcb_expose_event_t *e)
        for (i = 0; i < num_screens; i++)
                TAILQ_FOREACH(r, &screens[i].rl, entry)
                        if (e->window == WINID(r->bar))
-                               bar_update();
+                               bar_draw();
 
        xcb_flush(conn);
 }
@@ -7408,10 +7522,14 @@ keypress(xcb_key_press_event_t *e)
 
        keysym = xcb_key_press_lookup_keysym(syms, e, 0);
 
-       DNPRINTF(SWM_D_EVENT, "keypress: %u %u\n", e->detail, keysym);
+       DNPRINTF(SWM_D_EVENT, "keypress: keysym: %u, win (x,y): 0x%x (%d,%d), "
+           "detail: %u, time: %u, root (x,y): 0x%x (%d,%d), child: 0x%x, "
+           "state: %u, same_screen: %s\n", keysym, e->event, e->event_x,
+           e->event_y, e->detail, e->time, e->root, e->root_x, e->root_y,
+           e->child, e->state, YESNO(e->same_screen));
 
        if ((kp = key_lookup(CLEANMASK(e->state), keysym)) == NULL)
-               return;
+               goto out;
 
        last_event_time = e->time;
 
@@ -7421,6 +7539,13 @@ keypress(xcb_key_press_event_t *e)
        else if (keyfuncs[kp->funcid].func)
                keyfuncs[kp->funcid].func(root_to_region(e->root, SWM_CK_ALL),
                    &(keyfuncs[kp->funcid].args));
+
+out:
+       /* Unfreeze grab events. */
+       xcb_allow_events(conn, XCB_ALLOW_ASYNC_KEYBOARD, e->time);
+       xcb_flush(conn);
+
+       DNPRINTF(SWM_D_EVENT, "keypress: done.\n");
 }
 
 void
@@ -7462,7 +7587,7 @@ buttonpress(xcb_button_press_event_t *e)
                                    XCB_INPUT_FOCUS_PARENT, e->root, e->time);
 
                                /* Clear bar since empty. */
-                               bar_update();
+                               bar_draw();
 
                                handled = 1;
                                goto out;
@@ -7473,7 +7598,7 @@ buttonpress(xcb_button_press_event_t *e)
        }
 
        if (win == NULL)
-               return;
+               goto out;
 
        last_event_time = e->time;
 
@@ -7843,7 +7968,7 @@ enternotify(xcb_enter_notify_event_t *e)
                                    XCB_INPUT_FOCUS_PARENT, e->root, e->time);
 
                                /* Clear bar since empty. */
-                               bar_update();
+                               bar_draw();
 
                                focus_flush();
                        }
@@ -8041,7 +8166,7 @@ propertynotify(xcb_property_notify_event_t *e)
                }
        } else if (e->atom == XCB_ATOM_WM_CLASS ||
            e->atom == XCB_ATOM_WM_NAME) {
-               bar_update();
+               bar_draw();
        }
 
        xcb_flush(conn);
@@ -8594,6 +8719,7 @@ setup_screens(void)
                        ws = &screens[i].ws[j];
                        ws->idx = j;
                        ws->name = NULL;
+                       ws->bar_enabled = 1;
                        ws->focus = NULL;
                        ws->focus_prev = NULL;
                        ws->focus_pending = NULL;
@@ -8787,6 +8913,9 @@ main(int argc, char *argv[])
        xcb_generic_event_t     *evt;
        struct timeval          tv;
        fd_set                  rd;
+       int                     rd_max;
+       int                     stdin_ready = 0;
+       int                     num_readable;
 
        /* suppress unused warning since var is needed */
        (void)argc;
@@ -8917,6 +9046,8 @@ noconfig:
        xcb_ungrab_server(conn);
        xcb_flush(conn);
 
+       rd_max = xfd > STDIN_FILENO ? xfd : STDIN_FILENO;
+
        while (running) {
                while ((evt = xcb_poll_for_event(conn))) {
                        if (!running)
@@ -8941,22 +9072,29 @@ noconfig:
                }
 
                FD_ZERO(&rd);
+               FD_SET(STDIN_FILENO, &rd);
                FD_SET(xfd, &rd);
                tv.tv_sec = 1;
                tv.tv_usec = 0;
-               if (select(xfd + 1, &rd, NULL, NULL, &tv) == -1)
-                       if (errno != EINTR) {
-                               DNPRINTF(SWM_D_MISC, "select failed");
-                       }
+               num_readable = select(rd_max + 1, &rd, NULL, NULL, &tv);
+               if (num_readable == -1 && errno != EINTR) {
+                       DNPRINTF(SWM_D_MISC, "select failed");
+               } else if (num_readable > 0 && FD_ISSET(STDIN_FILENO, &rd)) {
+                       stdin_ready = 1;
+               }
+
                if (restart_wm)
                        restart(NULL, NULL);
+
                if (search_resp)
                        search_do_resp();
+
                if (!running)
                        goto done;
-               if (bar_alarm) {
-                       bar_alarm = 0;
-                       bar_update();
+
+               if (stdin_ready) {
+                       stdin_ready = 0;
+                       bar_extra_update();
                }
        }
 done: