]> code.delx.au - spectrwm/blobdiff - spectrwm.c
Prepare for release 2.1.1.
[spectrwm] / spectrwm.c
index 9219f762690287fb9b36c2e00a901d73f98e828e..606dcbe4ed9cfd11c72bfd872e837aa0350f3f74 100644 (file)
@@ -251,10 +251,11 @@ u_int32_t         swm_debug = 0
 #define SWM_FOCUS_MANUAL       (2)
 
 #define SWM_CK_NONE            0
-#define SWM_CK_ALL             0x7
+#define SWM_CK_ALL             0xf
 #define SWM_CK_FOCUS           0x1
 #define SWM_CK_POINTER         0x2
 #define SWM_CK_FALLBACK                0x4
+#define SWM_CK_REGION          0x8
 
 #define SWM_CONF_DEFAULT       (0)
 #define SWM_CONF_KEYMAPPING    (1)
@@ -348,13 +349,13 @@ 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;
 int             bar_enabled = 1;
 int             bar_border_width = 1;
 int             bar_at_bottom = 0;
-int             bar_extra = 1;
-int             bar_extra_running = 0;
+int             bar_extra = 0;
 int             bar_verbose = 1;
 int             bar_height = 0;
 int             bar_justify = SWM_BAR_JUSTIFY_LEFT;
@@ -373,6 +374,8 @@ int          focus_default = SWM_STACK_TOP;
 int             spawn_position = SWM_STACK_TOP;
 int             disable_border = 0;
 int             border_width = 1;
+int             region_padding = 0;
+int             tile_gap = 0;
 int             verbose_layout = 0;
 time_t          time_started;
 pid_t           bar_pid;
@@ -383,6 +386,8 @@ int          bar_font_legacy = 1;
 char           *bar_fonts;
 XftColor        bar_font_color;
 struct passwd  *pwd;
+char           *startup_exception;
+unsigned int    nr_exceptions = 0;
 
 /* layout manager data */
 struct swm_geometry {
@@ -404,6 +409,7 @@ struct swm_bar {
 /* virtual "screens" */
 struct swm_region {
        TAILQ_ENTRY(swm_region) entry;
+       xcb_window_t            id;
        struct swm_geometry     g;
        struct workspace        *ws;    /* current workspace on this region */
        struct workspace        *ws_prior; /* prior workspace on this region */
@@ -518,6 +524,7 @@ struct workspace {
 enum {
        SWM_S_COLOR_BAR,
        SWM_S_COLOR_BAR_BORDER,
+       SWM_S_COLOR_BAR_BORDER_UNFOCUS,
        SWM_S_COLOR_BAR_FONT,
        SWM_S_COLOR_FOCUS,
        SWM_S_COLOR_UNFOCUS,
@@ -534,6 +541,7 @@ struct swm_screen {
        struct swm_region_list  orl;    /* list of old regions */
        xcb_window_t            root;
        struct workspace        ws[SWM_WS_MAX];
+       struct swm_region       *r_focus;
 
        /* colors */
        struct {
@@ -565,8 +573,8 @@ union arg {
 #define SWM_ARG_ID_STACKINIT   (31)
 #define SWM_ARG_ID_CYCLEWS_UP  (40)
 #define SWM_ARG_ID_CYCLEWS_DOWN        (41)
-#define SWM_ARG_ID_CYCLESC_UP  (42)
-#define SWM_ARG_ID_CYCLESC_DOWN        (43)
+#define SWM_ARG_ID_CYCLERG_UP  (42)
+#define SWM_ARG_ID_CYCLERG_DOWN        (43)
 #define SWM_ARG_ID_CYCLEWS_UP_ALL      (44)
 #define SWM_ARG_ID_CYCLEWS_DOWN_ALL    (45)
 #define SWM_ARG_ID_STACKINC    (50)
@@ -699,12 +707,15 @@ struct cursors {
        {"top_right_corner", XC_top_right_corner, XCB_CURSOR_NONE},
 };
 
+#define        SWM_SPAWN_OPTIONAL              0x1
+
 /* spawn */
 struct spawn_prog {
        TAILQ_ENTRY(spawn_prog) entry;
        char                    *name;
        int                     argc;
        char                    **argv;
+       int                     flags;
 };
 TAILQ_HEAD(spawn_list, spawn_prog);
 struct spawn_list              spawns = TAILQ_HEAD_INITIALIZER(spawns);
@@ -731,6 +742,15 @@ enum keyfuncid {
        KF_MOVE_LEFT,
        KF_MOVE_RIGHT,
        KF_MOVE_UP,
+       KF_MVRG_1,
+       KF_MVRG_2,
+       KF_MVRG_3,
+       KF_MVRG_4,
+       KF_MVRG_5,
+       KF_MVRG_6,
+       KF_MVRG_7,
+       KF_MVRG_8,
+       KF_MVRG_9,
        KF_MVWS_1,
        KF_MVWS_2,
        KF_MVWS_3,
@@ -757,6 +777,17 @@ enum keyfuncid {
        KF_QUIT,
        KF_RAISE_TOGGLE,
        KF_RESTART,
+       KF_RG_1,
+       KF_RG_2,
+       KF_RG_3,
+       KF_RG_4,
+       KF_RG_5,
+       KF_RG_6,
+       KF_RG_7,
+       KF_RG_8,
+       KF_RG_9,
+       KF_RG_NEXT,
+       KF_RG_PREV,
        KF_SCREEN_NEXT,
        KF_SCREEN_PREV,
        KF_SEARCH_WIN,
@@ -819,21 +850,20 @@ 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);
+int     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);
+char   *bar_replace_seq(char *, char *, struct swm_region *, size_t *, size_t);
 void    bar_setup(struct swm_region *);
 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 *);
@@ -852,7 +882,7 @@ int  count_win(struct workspace *, int);
 void    cursors_cleanup(void);
 void    cursors_load(void);
 void    custom_region(char *);
-void    cyclescr(struct swm_region *, union arg *);
+void    cyclerg(struct swm_region *, union arg *);
 void    cyclews(struct swm_region *, union arg *);
 void    cycle_layout(struct swm_region *, union arg *);
 void    destroynotify(xcb_destroy_notify_event_t *);
@@ -881,6 +911,8 @@ void         focusin(xcb_focus_in_event_t *);
 void    focusout(xcb_focus_out_event_t *);
 #endif
 void    focus_flush(void);
+void    focus_region(struct swm_region *);
+void    focusrg(struct swm_region *, union arg *);
 void    focus_win(struct ws_win *);
 void    fontset_init(void);
 void    free_window(struct ws_win *);
@@ -896,6 +928,7 @@ char        *get_notify_mode_label(uint8_t);
 #endif
 struct ws_win  *get_pointer_win(xcb_window_t);
 struct ws_win  *get_region_focus(struct swm_region *);
+int     get_region_index(struct swm_region *);
 xcb_screen_t   *get_screen(int);
 #ifdef SWM_DEBUG
 char   *get_stack_mode_name(uint8_t);
@@ -916,6 +949,7 @@ struct key  *key_lookup(unsigned int, KeySym);
 void    key_remove(struct key *);
 void    key_replace(struct key *, unsigned int, KeySym, enum keyfuncid,
             const char *);
+void    kill_bar_extra_atexit(void);
 void    kill_refs(struct ws_win *);
 #ifdef SWM_DEBUG
 void    leavenotify(xcb_leave_notify_event_t *);
@@ -926,6 +960,7 @@ void         map_window(struct ws_win *);
 void    mapnotify(xcb_map_notify_event_t *);
 void    mappingnotify(xcb_mapping_notify_event_t *);
 void    maprequest(xcb_map_request_event_t *);
+void    motionnotify(xcb_motion_notify_event_t *);
 void    move(struct ws_win *, union arg *);
 void    move_step(struct swm_region *, union arg *);
 uint32_t name_to_pixel(const char *);
@@ -960,7 +995,9 @@ void         search_resp_uniconify(const char *, unsigned long);
 void    search_win(struct swm_region *, union arg *);
 void    search_win_cleanup(void);
 void    search_workspace(struct swm_region *, union arg *);
+void    send_to_rg(struct swm_region *, union arg *);
 void    send_to_ws(struct swm_region *, union arg *);
+void    set_region(struct swm_region *);
 int     setautorun(char *, char *, int);
 int     setconfbinding(char *, char *, int);
 int     setconfcolor(char *, char *, int);
@@ -974,7 +1011,7 @@ int         setkeymapping(char *, char *, int);
 int     setlayout(char *, char *, int);
 void    setquirk(const char *, const char *, unsigned long);
 void    setscreencolor(char *, int, int);
-void    setspawn(const char *, const char *);
+void    setspawn(const char *, const char *, int);
 void    setup_ewmh(void);
 void    setup_globals(void);
 void    setup_keys(void);
@@ -991,15 +1028,15 @@ void      sort_windows(struct ws_win_list *);
 void    spawn(int, union arg *, int);
 void    spawn_custom(struct swm_region *, union arg *, const char *);
 int     spawn_expand(struct swm_region *, union arg *, const char *, char ***);
-void    spawn_insert(const char *, const char *);
+void    spawn_insert(const char *, const char *, int);
 void    spawn_remove(struct spawn_prog *);
-void    spawn_replace(struct spawn_prog *, const char *, const char *);
+void    spawn_replace(struct spawn_prog *, const char *, const char *, int);
 void    spawn_select(struct swm_region *, union arg *, const char *, int *);
 void    stack_config(struct swm_region *, union arg *);
 void    stack_floater(struct ws_win *, struct swm_region *);
 void    stack_master(struct workspace *, struct swm_geometry *, int, int);
 void    store_float_geom(struct ws_win *, struct swm_region *);
-char *  strdupsafe(const char *);
+char   *strdupsafe(const char *);
 void    swapwin(struct swm_region *, union arg *);
 void    switchws(struct swm_region *, union arg *);
 void    teardown_ewmh(void);
@@ -1012,6 +1049,7 @@ void       unmap_window(struct ws_win *);
 void    updatenumlockmask(void);
 void    update_modkey(unsigned int);
 void    update_window(struct ws_win *);
+void    validate_spawns(void);
 int     validate_win(struct ws_win *);
 int     validate_ws(struct workspace *);
 /*void  visibilitynotify(xcb_visibility_notify_event_t *);*/
@@ -1020,6 +1058,8 @@ pid_t      window_get_pid(xcb_window_t);
 void    wkill(struct swm_region *, union arg *);
 void    workaround(void);
 void    xft_init(struct swm_region *);
+void    _add_startup_exception(const char *, va_list);
+void    add_startup_exception(const char *, ...);
 
 RB_PROTOTYPE(key_tree, key, entry, key_cmp);
 RB_GENERATE(key_tree, key, entry, key_cmp);
@@ -1144,6 +1184,27 @@ get_screen(int screen)
        return (NULL);
 }
 
+int
+get_region_index(struct swm_region *r)
+{
+       struct swm_region       *rr;
+       int                      ridx = 0;
+
+       if (r == NULL)
+               return -1;
+
+       TAILQ_FOREACH(rr, &r->s->rl, entry) {
+               if (rr == r)
+                       break;
+               ++ridx;
+       }
+
+       if (rr == NULL)
+               return -1;
+
+       return ridx;
+}
+
 void
 focus_flush(void)
 {
@@ -2159,8 +2220,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];
@@ -2176,15 +2238,22 @@ bar_fmt_print(void)
                        if (r->bar == NULL)
                                continue;
 
-                       if (r->ws->bar_enabled)
+                       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 (startup_exception)
+                               snprintf(fmtrep, sizeof fmtrep, "total "
+                                   "exceptions: %d, first exception: %s",
+                                   nr_exceptions,
+                                   startup_exception);
+                       else {
+                               bar_fmt(fmtexp, fmtnew, r, sizeof fmtnew);
+                               bar_replace(fmtnew, fmtrep, r, sizeof fmtrep);
+                       }
                        if (bar_font_legacy)
                                bar_print_legacy(r, fmtrep);
                        else
@@ -2193,37 +2262,52 @@ bar_fmt_print(void)
        }
 }
 
-void
-bar_update(void)
+/*
+ * Reads external script output; call when stdin is readable.
+ * Returns 1 if bar_ext was updated; otherwise 0.
+ */
+int
+bar_extra_update(void)
 {
-       size_t                  len;
-       char                    *b;
+       size_t          len;
+       char            b[SWM_BAR_MAX];
+       int             changed = 0;
+
+       if (!bar_extra)
+               return changed;
 
-       if (bar_enabled && 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);
+       while (fgets(b, sizeof(b), stdin) != NULL) {
+               if (bar_enabled) {
+                       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));
+
+                               changed = 1;
+                       } else {
+                               /* Buffer output. */
+                               strlcat(bar_ext_buf, b, sizeof(bar_ext_buf));
                        }
-               if (b == NULL && errno != EAGAIN) {
-                       warn("bar_update: bar_extra failed");
-                       bar_extra_stop();
                }
-       } else {
-               /*
-                * Attempt to drain stdin, so it doesn't cause the main loop to
-                * call us as fast as it can.
-                */
-               fgetln(stdin, &len);
-
-               if (!bar_enabled)
-                       return;
+       }
 
-               bar_ext[0] = '\0';
+       if (errno != EAGAIN) {
+               warn("bar_action failed");
+               bar_extra_stop();
+               changed = 1;
        }
 
-       bar_fmt_print();
+       return changed;
 }
 
 void
@@ -2265,33 +2349,29 @@ bar_toggle(struct swm_region *r, union arg *args)
        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];
-       int                     i, num_screens;
-
        /* do this here because the conf file is in memory */
-       if (bar_extra && !bar_extra_running && bar_argv[0]) {
+       if (!bar_extra && bar_argv[0]) {
                /* launch external status app */
-               bar_extra_running = 1;
+               bar_extra = 1;
                if (pipe(bar_pipe) == -1)
                        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)
+               if (dup2(bar_pipe[0], STDIN_FILENO) == -1)
                        err(1, "dup2");
 
                /* Set stdout to write to the pipe. */
-               if (dup2(bar_pipe[1], 1) == -1)
+               if (dup2(bar_pipe[1], STDOUT_FILENO) == -1)
                        err(1, "dup2");
 
                if (signal(SIGPIPE, SIG_IGN) == SIG_ERR)
@@ -2309,21 +2389,16 @@ bar_refresh(void)
                        close(bar_pipe[1]);
                        break;
                }
-       }
 
-       num_screens = xcb_setup_roots_length(xcb_get_setup(conn));
-       for (i = 0; i < num_screens; i++)
-               TAILQ_FOREACH(r, &screens[i].rl, entry) {
-                       if (r->bar == NULL)
-                               continue;
-                       wa[0] = screens[i].c[SWM_S_COLOR_BAR].pixel;
-                       wa[1] = screens[i].c[SWM_S_COLOR_BAR_BORDER].pixel;
-                       xcb_change_window_attributes(conn, r->bar->id,
-                           XCB_CW_BACK_PIXEL | XCB_CW_BORDER_PIXEL, wa);
-               }
+               atexit(kill_bar_extra_atexit);
+       }
+}
 
-       bar_update();
-       xcb_flush(conn);
+void
+kill_bar_extra_atexit(void)
+{
+       if (bar_pid)
+               kill(bar_pid, SIGTERM);
 }
 
 int
@@ -2463,10 +2538,12 @@ bar_setup(struct swm_region *r)
        WIDTH(r->bar) = WIDTH(r) - 2 * bar_border_width;
        HEIGHT(r->bar) = bar_height - 2 * bar_border_width;
 
+       /* Assume region is unfocused when we create the bar. */
        r->bar->id = xcb_generate_id(conn);
        wa[0] = r->s->c[SWM_S_COLOR_BAR].pixel;
-       wa[1] = r->s->c[SWM_S_COLOR_BAR_BORDER].pixel;
-       wa[2] = XCB_EVENT_MASK_EXPOSURE;
+       wa[1] = r->s->c[SWM_S_COLOR_BAR_BORDER_UNFOCUS].pixel;
+       wa[2] = XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_POINTER_MOTION |
+           XCB_EVENT_MASK_POINTER_MOTION_HINT;
 
        xcb_create_window(conn, XCB_COPY_FROM_PARENT, r->bar->id, r->s->root,
            X(r->bar), Y(r->bar), WIDTH(r->bar), HEIGHT(r->bar),
@@ -2489,7 +2566,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));
 
-       bar_refresh();
+       bar_extra_setup();
 }
 
 void
@@ -2552,8 +2629,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);
 }
 
@@ -2844,7 +2921,10 @@ root_to_region(xcb_window_t root, int check)
                if (screens[i].root == root)
                        break;
 
-       if (check & SWM_CK_FOCUS) {
+       if (check & SWM_CK_REGION)
+               r = screens[i].r_focus;
+
+       if (r == NULL && check & SWM_CK_FOCUS) {
                /* Try to find an actively focused window */
                gifr = xcb_get_input_focus_reply(conn,
                    xcb_get_input_focus(conn), NULL);
@@ -3123,6 +3203,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))
@@ -3199,16 +3282,17 @@ focus_win(struct ws_win *win)
                        TAILQ_FOREACH(w, &ws->winlist, entry)
                                if (w->transient == win->id && !w->iconic)
                                        map_window(w);
-
                }
 
+               set_region(ws->r);
+
                xcb_change_property(conn, XCB_PROP_MODE_REPLACE, win->s->root,
                    ewmh[_NET_ACTIVE_WINDOW].atom, XCB_ATOM_WINDOW, 32, 1,
                    &win->id);
        }
 
 out:
-       bar_update();
+       bar_draw();
 
        DNPRINTF(SWM_D_FOCUS, "focus_win: done.\n");
 }
@@ -3262,6 +3346,60 @@ event_drain(uint8_t rt)
        }
 }
 
+void
+set_region(struct swm_region *r)
+{
+       struct swm_region       *rf;
+
+       if (r == NULL)
+               return;
+
+       rf = r->s->r_focus;
+       /* Unfocus old region bar. */
+       if (rf) {
+               if (rf == r)
+                       return;
+
+               xcb_change_window_attributes(conn, rf->bar->id,
+                   XCB_CW_BORDER_PIXEL,
+                   &r->s->c[SWM_S_COLOR_BAR_BORDER_UNFOCUS].pixel);
+       }
+
+       /* Set region bar border to focus_color. */
+       xcb_change_window_attributes(conn, r->bar->id,
+           XCB_CW_BORDER_PIXEL, &r->s->c[SWM_S_COLOR_BAR_BORDER].pixel);
+
+       r->s->r_focus = r;
+}
+
+void
+focus_region(struct swm_region *r)
+{
+       struct ws_win           *nfw;
+       struct swm_region       *old_r;
+
+       if (r == NULL)
+               return;
+
+       old_r = r->s->r_focus;
+       set_region(r);
+
+       nfw = get_region_focus(r);
+       if (nfw) {
+               focus_win(nfw);
+       } else {
+               /* New region is empty; need to manually unfocus win. */
+               if (old_r)
+                       unfocus_win(old_r->ws->focus);
+
+               xcb_set_input_focus(conn, XCB_INPUT_FOCUS_PARENT, r->s->root,
+                   XCB_CURRENT_TIME);
+
+               /* Clear bar since empty. */
+               bar_draw();
+       }
+}
+
 void
 switchws(struct swm_region *r, union arg *args)
 {
@@ -3330,7 +3468,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();
 
@@ -3400,9 +3538,32 @@ priorws(struct swm_region *r, union arg *args)
 }
 
 void
-cyclescr(struct swm_region *r, union arg *args)
+focusrg(struct swm_region *r, union arg *args)
+{
+       int                     ridx = args->id, i, num_screens;
+       struct swm_region       *rr = NULL;
+
+       num_screens = xcb_setup_roots_length(xcb_get_setup(conn));
+       /* do nothing if we don't have more than one screen */
+       if (!(num_screens > 1 || outputs > 1))
+               return;
+
+       DNPRINTF(SWM_D_FOCUS, "focusrg: id: %d\n", ridx);
+
+       rr = TAILQ_FIRST(&r->s->rl);
+       for (i = 0; i < ridx; ++i)
+               rr = TAILQ_NEXT(rr, entry);
+
+       if (rr == NULL)
+               return;
+
+       focus_region(rr);
+       focus_flush();
+}
+
+void
+cyclerg(struct swm_region *r, union arg *args)
 {
-       struct ws_win           *nfw;
        struct swm_region       *rr = NULL;
        int                     i, num_screens;
 
@@ -3413,12 +3574,12 @@ cyclescr(struct swm_region *r, union arg *args)
 
        i = r->s->idx;
        switch (args->id) {
-       case SWM_ARG_ID_CYCLESC_UP:
+       case SWM_ARG_ID_CYCLERG_UP:
                rr = TAILQ_NEXT(r, entry);
                if (rr == NULL)
                        rr = TAILQ_FIRST(&screens[i].rl);
                break;
-       case SWM_ARG_ID_CYCLESC_DOWN:
+       case SWM_ARG_ID_CYCLERG_DOWN:
                rr = TAILQ_PREV(r, swm_region_list, entry);
                if (rr == NULL)
                        rr = TAILQ_LAST(&screens[i].rl, swm_region_list);
@@ -3429,19 +3590,7 @@ cyclescr(struct swm_region *r, union arg *args)
        if (rr == NULL)
                return;
 
-       nfw = get_region_focus(rr);
-       if (nfw) {
-               focus_win(nfw);
-       } else {
-               /* New region is empty; unfocus old region and warp pointer. */
-               unfocus_win(r->ws->focus);
-               xcb_set_input_focus(conn, XCB_INPUT_FOCUS_PARENT,
-                               rr->s[i].root, XCB_CURRENT_TIME);
-
-               /* Clear bar since empty. */
-               bar_update();
-       }
-
+       focus_region(rr);
        focus_flush();
 }
 
@@ -3591,37 +3740,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);
@@ -3752,7 +3934,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));
 
@@ -3772,7 +3954,7 @@ stack_config(struct swm_region *r, union arg *args)
 
        if (args->id != SWM_ARG_ID_STACKINIT)
                stack();
-       bar_update();
+       bar_draw();
 
        focus_flush();
 }
@@ -3797,10 +3979,12 @@ stack(void) {
                        DNPRINTF(SWM_D_STACK, "stack: workspace: %d "
                            "(screen: %d, region: %d)\n", r->ws->idx, i, j++);
 
-                       /* start with screen geometry, adjust for bar */
+                       /* Adjust stack area for region bar and padding. */
                        g = r->g;
-                       g.w -= 2 * border_width;
-                       g.h -= 2 * border_width;
+                       g.x += region_padding;
+                       g.y += region_padding;
+                       g.w -= 2 * border_width + 2 * region_padding;
+                       g.h -= 2 * border_width + 2 * region_padding;
                        if (bar_enabled && r->ws->bar_enabled) {
                                if (!bar_at_bottom)
                                        g.y += bar_height;
@@ -4036,9 +4220,7 @@ stack_master(struct workspace *ws, struct swm_geometry *g, int rot, int flip)
        /*  stack all the tiled windows */
        i = j = 0, s = stacks;
        TAILQ_FOREACH(win, &ws->winlist, entry) {
-               if (win->transient || win->floating)
-                       continue;
-               if (win->iconic)
+               if (win->transient || win->floating || win->iconic)
                        continue;
 
                if (win->ewmh_flags & EWMH_F_FULLSCREEN) {
@@ -4056,16 +4238,20 @@ stack_master(struct workspace *ws, struct swm_geometry *g, int rot, int flip)
                        if (flip)
                                win_g.x = r_g.x;
                        else
-                               win_g.x += win_g.w + 2 * border_width;
+                               win_g.x += win_g.w + 2 * border_width +
+                                   tile_gap;
                        win_g.w = (r_g.w - msize -
-                           (stacks * 2 * border_width)) / stacks;
+                           (stacks * (2 * border_width + tile_gap))) / stacks;
                        if (s == 1)
                                win_g.w += (r_g.w - msize -
-                                   (stacks * 2 * border_width)) % stacks;
+                                   (stacks * (2 * border_width + tile_gap))) %
+                                   stacks;
                        s--;
                        j = 0;
                }
-               win_g.h = hrh - 2 * border_width;
+
+               win_g.h = hrh - 2 * border_width - tile_gap;
+
                if (rot) {
                        h_inc = win->sh.width_inc;
                        h_base = win->sh.base_width;
@@ -4073,6 +4259,7 @@ stack_master(struct workspace *ws, struct swm_geometry *g, int rot, int flip)
                        h_inc = win->sh.height_inc;
                        h_base = win->sh.base_height;
                }
+
                if (j == colno - 1) {
                        win_g.h = hrh + extra;
                } else if (h_inc > 1 && h_inc < h_slice) {
@@ -4092,7 +4279,7 @@ stack_master(struct workspace *ws, struct swm_geometry *g, int rot, int flip)
                if (j == 0)
                        win_g.y = r_g.y;
                else
-                       win_g.y += last_h + 2 * border_width;
+                       win_g.y += last_h + 2 * border_width + tile_gap;
 
                if (disable_border && !(bar_enabled && ws->bar_enabled) &&
                    winno == 1){
@@ -4102,6 +4289,7 @@ stack_master(struct workspace *ws, struct swm_geometry *g, int rot, int flip)
                } else {
                        bordered = 1;
                }
+
                if (rot) {
                        if (X(win) != win_g.y || Y(win) != win_g.x ||
                            WIDTH(win) != win_g.h || HEIGHT(win) != win_g.w) {
@@ -4269,7 +4457,7 @@ max_stack(struct workspace *ws, struct swm_geometry *g)
 {
        struct swm_geometry     gg = *g;
        struct ws_win           *w, *win = NULL, *parent = NULL;
-       int                     winno, num_screens;
+       int                     winno;
 
        DNPRINTF(SWM_D_STACK, "max_stack: workspace: %d\n", ws->idx);
 
@@ -4294,11 +4482,13 @@ max_stack(struct workspace *ws, struct swm_geometry *g)
        DNPRINTF(SWM_D_STACK, "max_stack: win: 0x%x\n", win->id);
 
        /* maximize all top level windows */
-       num_screens = xcb_setup_roots_length(xcb_get_setup(conn));
        TAILQ_FOREACH(w, &ws->winlist, entry) {
                if (w->transient || w->iconic)
                        continue;
 
+               if (!w->mapped && w != win)
+                       map_window(w);
+
                if (w->floating && !w->floatmaxed) {
                        /*
                         * retain geometry for retrieval on exit from
@@ -4322,14 +4512,9 @@ max_stack(struct workspace *ws, struct swm_geometry *g)
 
                        update_window(w);
                }
-
-               /* Unmap unwanted windows if not multi-screen. */
-               if (!(num_screens > 1 || outputs > 1) && (w != win ||
-                   w != parent || w->transient != win->id))
-                       unmap_window(w);
        }
 
-       /* If a parent exists, map it first. */
+       /* If a parent exists, map/raise it first. */
        if (parent) {
                map_window(parent);
 
@@ -4342,10 +4527,10 @@ max_stack(struct workspace *ws, struct swm_geometry *g)
                        }
        }
 
-       /* Map focused window. */
+       /* Map/raise focused window. */
        map_window(win);
 
-       /* Finally, map children of focus window. */
+       /* Finally, map/raise children of focus window. */
        TAILQ_FOREACH(w, &ws->winlist, entry)
                if (w->transient == win->id && !w->iconic) {
                        stack_floater(w, ws->r);
@@ -4353,6 +4538,32 @@ max_stack(struct workspace *ws, struct swm_geometry *g)
                }
 }
 
+void
+send_to_rg(struct swm_region *r, union arg *args)
+{
+       int                     ridx = args->id, i, num_screens;
+       struct swm_region       *rr = NULL;
+       union arg               a;
+
+       num_screens = xcb_setup_roots_length(xcb_get_setup(conn));
+       /* do nothing if we don't have more than one screen */
+       if (!(num_screens > 1 || outputs > 1))
+               return;
+
+       DNPRINTF(SWM_D_FOCUS, "send_to_rg: id: %d\n", ridx);
+
+       rr = TAILQ_FIRST(&r->s->rl);
+       for (i = 0; i < ridx; ++i)
+               rr = TAILQ_NEXT(rr, entry);
+
+       if (rr == NULL)
+               return;
+
+       a.id = rr->ws->idx;
+
+       send_to_ws(r, &a);
+}
+
 void
 send_to_ws(struct swm_region *r, union arg *args)
 {
@@ -4516,7 +4727,7 @@ get_win_name(xcb_window_t win)
                        free(r);
                        /* Use WM_NAME instead; no UTF-8. */
                        c = xcb_get_property(conn, 0, win, XCB_ATOM_WM_NAME,
-                               XCB_GET_PROPERTY_TYPE_ANY, 0, UINT_MAX);
+                           XCB_GET_PROPERTY_TYPE_ANY, 0, UINT_MAX);
                        r = xcb_get_property_reply(conn, c, NULL);
 
                        if (!r)
@@ -4528,7 +4739,7 @@ get_win_name(xcb_window_t win)
                }
                if (r->length > 0)
                        name = strndup(xcb_get_property_value(r),
-                                  xcb_get_property_value_length(r));
+                           xcb_get_property_value_length(r));
 
                free(r);
        }
@@ -5114,6 +5325,12 @@ resize(struct ws_win *win, union arg *args)
 
        focus_flush();
 
+       /* It's possible for win to have been freed during focus_flush(). */
+       if (validate_win(win)) {
+               DNPRINTF(SWM_D_EVENT, "move: invalid win.\n");
+               goto out;
+       }
+
        switch (args->id) {
        case SWM_ARG_ID_WIDTHSHRINK:
                WIDTH(win) -= SWM_RESIZE_STEPS;
@@ -5170,7 +5387,7 @@ resize(struct ws_win *win, union arg *args)
 
        xcb_flush(conn);
        resizing = 1;
-       while ((evt = xcb_wait_for_event(conn)) && resizing) {
+       while (resizing && (evt = xcb_wait_for_event(conn))) {
                switch (XCB_EVENT_RESPONSE_TYPE(evt)) {
                case XCB_BUTTON_RELEASE:
                        DNPRINTF(SWM_D_EVENT, "resize: BUTTON_RELEASE\n");
@@ -5233,6 +5450,12 @@ resize(struct ws_win *win, union arg *args)
                        break;
                default:
                        event_handle(evt);
+
+                       /* It's possible for win to have been freed above. */
+                       if (validate_win(win)) {
+                               DNPRINTF(SWM_D_EVENT, "move: invalid win.\n");
+                               goto out;
+                       }
                        break;
                }
                free(evt);
@@ -5242,7 +5465,7 @@ resize(struct ws_win *win, union arg *args)
                xcb_flush(conn);
        }
        store_float_geom(win,r);
-
+out:
        xcb_ungrab_pointer(conn, XCB_CURRENT_TIME);
        free(xpr);
        DNPRINTF(SWM_D_EVENT, "resize: done.\n");
@@ -5301,6 +5524,12 @@ move(struct ws_win *win, union arg *args)
 
        focus_flush();
 
+       /* It's possible for win to have been freed during focus_flush(). */
+       if (validate_win(win)) {
+               DNPRINTF(SWM_D_EVENT, "move: invalid win.\n");
+               goto out;
+       }
+
        move_stp = 0;
        switch (args->id) {
        case SWM_ARG_ID_MOVELEFT:
@@ -5343,7 +5572,7 @@ move(struct ws_win *win, union arg *args)
 
        xcb_flush(conn);
        moving = 1;
-       while ((evt = xcb_wait_for_event(conn)) && moving) {
+       while (moving && (evt = xcb_wait_for_event(conn))) {
                switch (XCB_EVENT_RESPONSE_TYPE(evt)) {
                case XCB_BUTTON_RELEASE:
                        DNPRINTF(SWM_D_EVENT, "move: BUTTON_RELEASE\n");
@@ -5365,6 +5594,12 @@ move(struct ws_win *win, union arg *args)
                        break;
                default:
                        event_handle(evt);
+
+                       /* It's possible for win to have been freed above. */
+                       if (validate_win(win)) {
+                               DNPRINTF(SWM_D_EVENT, "move: invalid win.\n");
+                               goto out;
+                       }
                        break;
                }
                free(evt);
@@ -5374,6 +5609,7 @@ move(struct ws_win *win, union arg *args)
                xcb_flush(conn);
        }
        store_float_geom(win, r);
+out:
        free(qpr);
        xcb_ungrab_pointer(conn, XCB_CURRENT_TIME);
        DNPRINTF(SWM_D_EVENT, "move: done.\n");
@@ -5423,6 +5659,15 @@ struct keyfunc {
        { "move_left",          move_step,      {.id = SWM_ARG_ID_MOVELEFT} },
        { "move_right",         move_step,      {.id = SWM_ARG_ID_MOVERIGHT} },
        { "move_up",            move_step,      {.id = SWM_ARG_ID_MOVEUP} },
+       { "mvrg_1",             send_to_rg,     {.id = 0} },
+       { "mvrg_2",             send_to_rg,     {.id = 1} },
+       { "mvrg_3",             send_to_rg,     {.id = 2} },
+       { "mvrg_4",             send_to_rg,     {.id = 3} },
+       { "mvrg_5",             send_to_rg,     {.id = 4} },
+       { "mvrg_6",             send_to_rg,     {.id = 5} },
+       { "mvrg_7",             send_to_rg,     {.id = 6} },
+       { "mvrg_8",             send_to_rg,     {.id = 7} },
+       { "mvrg_9",             send_to_rg,     {.id = 8} },
        { "mvws_1",             send_to_ws,     {.id = 0} },
        { "mvws_2",             send_to_ws,     {.id = 1} },
        { "mvws_3",             send_to_ws,     {.id = 2} },
@@ -5449,8 +5694,19 @@ struct keyfunc {
        { "quit",               quit,           {0} },
        { "raise_toggle",       raise_toggle,   {0} },
        { "restart",            restart,        {0} },
-       { "screen_next",        cyclescr,       {.id = SWM_ARG_ID_CYCLESC_UP} },
-       { "screen_prev",        cyclescr,       {.id = SWM_ARG_ID_CYCLESC_DOWN} },
+       { "rg_1",               focusrg,        {.id = 0} },
+       { "rg_2",               focusrg,        {.id = 1} },
+       { "rg_3",               focusrg,        {.id = 2} },
+       { "rg_4",               focusrg,        {.id = 3} },
+       { "rg_5",               focusrg,        {.id = 4} },
+       { "rg_6",               focusrg,        {.id = 5} },
+       { "rg_7",               focusrg,        {.id = 6} },
+       { "rg_8",               focusrg,        {.id = 7} },
+       { "rg_9",               focusrg,        {.id = 8} },
+       { "rg_next",            cyclerg,        {.id = SWM_ARG_ID_CYCLERG_UP} },
+       { "rg_prev",            cyclerg,        {.id = SWM_ARG_ID_CYCLERG_DOWN} },
+       { "screen_next",        cyclerg,        {.id = SWM_ARG_ID_CYCLERG_UP} },
+       { "screen_prev",        cyclerg,        {.id = SWM_ARG_ID_CYCLERG_DOWN} },
        { "search_win",         search_win,     {0} },
        { "search_workspace",   search_workspace,       {0} },
        { "spawn_custom",       NULL,           {0} },
@@ -5610,6 +5866,13 @@ spawn_expand(struct swm_region *r, union arg *args, const char *spawn_name,
                            strdup(r->s->c[SWM_S_COLOR_UNFOCUS].name))
                            == NULL)
                                err(1, "spawn_custom color unfocus");
+               } else if (!strcasecmp(ap, "$region_index")) {
+                       if (asprintf(&real_args[i], "%d",
+                           get_region_index(r) + 1) < 1)
+                               err(1, "spawn_custom region index");
+               } else if (!strcasecmp(ap, "$workspace_index")) {
+                       if (asprintf(&real_args[i], "%d", r->ws->idx + 1) < 1)
+                               err(1, "spawn_custom workspace index");
                } else {
                        /* no match --> copy as is */
                        if ((real_args[i] = strdup(ap)) == NULL)
@@ -5671,9 +5934,9 @@ spawn_select(struct swm_region *r, union arg *args, const char *spawn_name,
                err(1, "cannot fork");
                break;
        case 0: /* child */
-               if (dup2(select_list_pipe[0], 0) == -1)
+               if (dup2(select_list_pipe[0], STDIN_FILENO) == -1)
                        err(1, "dup2");
-               if (dup2(select_resp_pipe[1], 1) == -1)
+               if (dup2(select_resp_pipe[1], STDOUT_FILENO) == -1)
                        err(1, "dup2");
                close(select_list_pipe[1]);
                close(select_resp_pipe[0]);
@@ -5691,7 +5954,7 @@ spawn_select(struct swm_region *r, union arg *args, const char *spawn_name,
 }
 
 void
-spawn_insert(const char *name, const char *args)
+spawn_insert(const char *name, const char *args, int flags)
 {
        char                    *arg, *cp, *ptr;
        struct spawn_prog       *sp;
@@ -5720,6 +5983,8 @@ spawn_insert(const char *name, const char *args)
        }
        free(cp);
 
+       sp->flags = flags;
+
        TAILQ_INSERT_TAIL(&spawns, sp, entry);
        DNPRINTF(SWM_D_SPAWN, "spawn_insert: leave\n");
 }
@@ -5742,18 +6007,7 @@ spawn_remove(struct spawn_prog *sp)
 }
 
 void
-spawn_replace(struct spawn_prog *sp, const char *name, const char *args)
-{
-       DNPRINTF(SWM_D_SPAWN, "spawn_replace: %s [%s]\n", sp->name, name);
-
-       spawn_remove(sp);
-       spawn_insert(name, args);
-
-       DNPRINTF(SWM_D_SPAWN, "spawn_replace: leave\n");
-}
-
-void
-setspawn(const char *name, const char *args)
+setspawn(const char *name, const char *args, int flags)
 {
        struct spawn_prog       *sp;
 
@@ -5762,59 +6016,89 @@ setspawn(const char *name, const char *args)
        if (name == NULL)
                return;
 
-       TAILQ_FOREACH(sp, &spawns, entry) {
+       /* Remove any old spawn under the same name. */
+       TAILQ_FOREACH(sp, &spawns, entry)
                if (!strcmp(sp->name, name)) {
-                       if (*args == '\0')
-                               spawn_remove(sp);
-                       else
-                               spawn_replace(sp, name, args);
-                       DNPRINTF(SWM_D_SPAWN, "setspawn: leave\n");
-                       return;
+                       spawn_remove(sp);
+                       break;
                }
-       }
-       if (*args == '\0') {
+
+       if (*args != '\0')
+               spawn_insert(name, args, flags);
+       else
                warnx("error: setspawn: cannot find program: %s", name);
-               return;
-       }
 
-       spawn_insert(name, args);
        DNPRINTF(SWM_D_SPAWN, "setspawn: leave\n");
 }
 
 int
 setconfspawn(char *selector, char *value, int flags)
 {
-       char *args;
-
-       /* suppress unused warning since var is needed */
-       (void)flags;
+       char            *args;
 
        args = expand_tilde(value);
 
        DNPRINTF(SWM_D_SPAWN, "setconfspawn: [%s] [%s]\n", selector, args);
 
-       setspawn(selector, args);
+       setspawn(selector, args, flags);
        free(args);
 
        DNPRINTF(SWM_D_SPAWN, "setconfspawn: done.\n");
        return (0);
 }
 
+void
+validate_spawns(void)
+{
+       struct spawn_prog       *sp;
+       char                    which[PATH_MAX];
+       size_t                  i;
+
+       struct key              *kp;
+
+       RB_FOREACH(kp, key_tree, &keys) {
+               if (kp->funcid != KF_SPAWN_CUSTOM)
+                       continue;
+
+               /* find program */
+               TAILQ_FOREACH(sp, &spawns, entry) {
+                       if (!strcasecmp(kp->spawn_name, sp->name))
+                               break;
+               }
+
+               if (sp == NULL || sp->flags & SWM_SPAWN_OPTIONAL)
+                       continue;
+
+               /* verify we have the goods */
+               snprintf(which, sizeof which, "which %s", sp->argv[0]);
+               DNPRINTF(SWM_D_CONF, "validate_spawns: which %s\n",
+                   sp->argv[0]);
+               for (i = strlen("which "); i < strlen(which); i++)
+                       if (which[i] == ' ') {
+                               which[i] = '\0';
+                               break;
+                       }
+               if (system(which) != 0)
+                       add_startup_exception("could not find %s",
+                           &which[strlen("which ")]);
+       }
+}
+
 void
 setup_spawn(void)
 {
+       setconfspawn("lock",            "xlock",                0);
+
        setconfspawn("term",            "xterm",                0);
        setconfspawn("spawn_term",      "xterm",                0);
-       setconfspawn("screenshot_all",  "screenshot.sh full",   0);
-       setconfspawn("screenshot_wind", "screenshot.sh window", 0);
-       setconfspawn("lock",            "xlock",                0);
-       setconfspawn("initscr",         "initscreen.sh",        0);
+
        setconfspawn("menu",            "dmenu_run"
                                        " -fn $bar_font"
                                        " -nb $bar_color"
                                        " -nf $bar_font_color"
                                        " -sb $bar_border"
                                        " -sf $bar_color",      0);
+
        setconfspawn("search",          "dmenu"
                                        " -i"
                                        " -fn $bar_font"
@@ -5822,6 +6106,7 @@ setup_spawn(void)
                                        " -nf $bar_font_color"
                                        " -sb $bar_border"
                                        " -sf $bar_color",      0);
+
        setconfspawn("name_workspace",  "dmenu"
                                        " -p Workspace"
                                        " -fn $bar_font"
@@ -5829,6 +6114,11 @@ setup_spawn(void)
                                        " -nf $bar_font_color"
                                        " -sb $bar_border"
                                        " -sf $bar_color",      0);
+
+        /* These are not verified for existence, even with a binding set. */
+       setconfspawn("screenshot_all",  "screenshot.sh full",   SWM_SPAWN_OPTIONAL);
+       setconfspawn("screenshot_wind", "screenshot.sh window", SWM_SPAWN_OPTIONAL);
+       setconfspawn("initscr",         "initscreen.sh",        SWM_SPAWN_OPTIONAL);
 }
 
 /* key bindings */
@@ -6019,6 +6309,8 @@ setconfbinding(char *selector, char *value, int flags)
                        if (parsekeys(value, mod_key, &mod, &ks) == 0) {
                                setkeybinding(mod, ks, KF_SPAWN_CUSTOM,
                                    sp->name);
+                               /* Custom binding; validate spawn. */
+                               sp->flags ^= SWM_SPAWN_OPTIONAL;
                                return (0);
                        } else
                                return (1);
@@ -6032,54 +6324,37 @@ void
 setup_keys(void)
 {
 #define MODKEY_SHIFT   MODKEY | XCB_MOD_MASK_SHIFT
+       setkeybinding(MODKEY,           XK_b,           KF_BAR_TOGGLE,  NULL);
+       setkeybinding(MODKEY_SHIFT,     XK_b,           KF_BAR_TOGGLE_WS,NULL);
+       setkeybinding(MODKEY,           XK_v,           KF_BUTTON2,     NULL);
        setkeybinding(MODKEY,           XK_space,       KF_CYCLE_LAYOUT,NULL);
        setkeybinding(MODKEY_SHIFT,     XK_backslash,   KF_FLIP_LAYOUT, NULL);
-       setkeybinding(MODKEY_SHIFT,     XK_space,       KF_STACK_RESET, NULL);
+       setkeybinding(MODKEY,           XK_t,           KF_FLOAT_TOGGLE,NULL);
+       setkeybinding(MODKEY,           XK_m,           KF_FOCUS_MAIN,  NULL);
+       setkeybinding(MODKEY,           XK_j,           KF_FOCUS_NEXT,  NULL);
+       setkeybinding(MODKEY,           XK_Tab,         KF_FOCUS_NEXT,  NULL);
+       setkeybinding(MODKEY,           XK_k,           KF_FOCUS_PREV,  NULL);
+       setkeybinding(MODKEY_SHIFT,     XK_Tab,         KF_FOCUS_PREV,  NULL);
+       setkeybinding(MODKEY_SHIFT,     XK_equal,       KF_HEIGHT_GROW,NULL);
+       setkeybinding(MODKEY_SHIFT,     XK_minus,       KF_HEIGHT_SHRINK,NULL);
+       setkeybinding(MODKEY,           XK_w,           KF_ICONIFY,     NULL);
        setkeybinding(MODKEY,           XK_h,           KF_MASTER_SHRINK, NULL);
        setkeybinding(MODKEY,           XK_l,           KF_MASTER_GROW, NULL);
        setkeybinding(MODKEY,           XK_comma,       KF_MASTER_ADD,  NULL);
        setkeybinding(MODKEY,           XK_period,      KF_MASTER_DEL,  NULL);
-       setkeybinding(MODKEY_SHIFT,     XK_comma,       KF_STACK_INC,   NULL);
-       setkeybinding(MODKEY_SHIFT,     XK_period,      KF_STACK_DEC,   NULL);
-       setkeybinding(MODKEY,           XK_Return,      KF_SWAP_MAIN,   NULL);
-       setkeybinding(MODKEY,           XK_j,           KF_FOCUS_NEXT,  NULL);
-       setkeybinding(MODKEY,           XK_k,           KF_FOCUS_PREV,  NULL);
-       setkeybinding(MODKEY_SHIFT,     XK_j,           KF_SWAP_NEXT,   NULL);
-       setkeybinding(MODKEY_SHIFT,     XK_k,           KF_SWAP_PREV,   NULL);
-       setkeybinding(MODKEY_SHIFT,     XK_Return,      KF_SPAWN_CUSTOM,"term");
-       setkeybinding(MODKEY,           XK_p,           KF_SPAWN_CUSTOM,"menu");
-       setkeybinding(MODKEY_SHIFT,     XK_q,           KF_QUIT,        NULL);
-       setkeybinding(MODKEY,           XK_q,           KF_RESTART,     NULL);
-       setkeybinding(MODKEY,           XK_m,           KF_FOCUS_MAIN,  NULL);
-       setkeybinding(MODKEY,           XK_1,           KF_WS_1,        NULL);
-       setkeybinding(MODKEY,           XK_2,           KF_WS_2,        NULL);
-       setkeybinding(MODKEY,           XK_3,           KF_WS_3,        NULL);
-       setkeybinding(MODKEY,           XK_4,           KF_WS_4,        NULL);
-       setkeybinding(MODKEY,           XK_5,           KF_WS_5,        NULL);
-       setkeybinding(MODKEY,           XK_6,           KF_WS_6,        NULL);
-       setkeybinding(MODKEY,           XK_7,           KF_WS_7,        NULL);
-       setkeybinding(MODKEY,           XK_8,           KF_WS_8,        NULL);
-       setkeybinding(MODKEY,           XK_9,           KF_WS_9,        NULL);
-       setkeybinding(MODKEY,           XK_0,           KF_WS_10,       NULL);
-       setkeybinding(MODKEY,           XK_F1,          KF_WS_11,       NULL);
-       setkeybinding(MODKEY,           XK_F2,          KF_WS_12,       NULL);
-       setkeybinding(MODKEY,           XK_F3,          KF_WS_13,       NULL);
-       setkeybinding(MODKEY,           XK_F4,          KF_WS_14,       NULL);
-       setkeybinding(MODKEY,           XK_F5,          KF_WS_15,       NULL);
-       setkeybinding(MODKEY,           XK_F6,          KF_WS_16,       NULL);
-       setkeybinding(MODKEY,           XK_F7,          KF_WS_17,       NULL);
-       setkeybinding(MODKEY,           XK_F8,          KF_WS_18,       NULL);
-       setkeybinding(MODKEY,           XK_F9,          KF_WS_19,       NULL);
-       setkeybinding(MODKEY,           XK_F10,         KF_WS_20,       NULL);
-       setkeybinding(MODKEY,           XK_F11,         KF_WS_21,       NULL);
-       setkeybinding(MODKEY,           XK_F12,         KF_WS_22,       NULL);
-       setkeybinding(MODKEY,           XK_Right,       KF_WS_NEXT,     NULL);
-       setkeybinding(MODKEY,           XK_Left,        KF_WS_PREV,     NULL);
-       setkeybinding(MODKEY,           XK_Up,          KF_WS_NEXT_ALL, NULL);
-       setkeybinding(MODKEY,           XK_Down,        KF_WS_PREV_ALL, NULL);
-       setkeybinding(MODKEY,           XK_a,           KF_WS_PRIOR,    NULL);
-       setkeybinding(MODKEY_SHIFT,     XK_Right,       KF_SCREEN_NEXT, NULL);
-       setkeybinding(MODKEY_SHIFT,     XK_Left,        KF_SCREEN_PREV, NULL);
+       setkeybinding(MODKEY_SHIFT,     XK_bracketright,KF_MOVE_DOWN,NULL);
+       setkeybinding(MODKEY,           XK_bracketleft, KF_MOVE_LEFT,NULL);
+       setkeybinding(MODKEY,           XK_bracketright,KF_MOVE_RIGHT,NULL);
+       setkeybinding(MODKEY_SHIFT,     XK_bracketleft, KF_MOVE_UP,     NULL);
+       setkeybinding(MODKEY_SHIFT,     XK_KP_End,      KF_MVRG_1,      NULL);
+       setkeybinding(MODKEY_SHIFT,     XK_KP_Down,     KF_MVRG_2,      NULL);
+       setkeybinding(MODKEY_SHIFT,     XK_KP_Next,     KF_MVRG_3,      NULL);
+       setkeybinding(MODKEY_SHIFT,     XK_KP_Left,     KF_MVRG_4,      NULL);
+       setkeybinding(MODKEY_SHIFT,     XK_KP_Begin,    KF_MVRG_5,      NULL);
+       setkeybinding(MODKEY_SHIFT,     XK_KP_Right,    KF_MVRG_6,      NULL);
+       setkeybinding(MODKEY_SHIFT,     XK_KP_Home,     KF_MVRG_7,      NULL);
+       setkeybinding(MODKEY_SHIFT,     XK_KP_Up,       KF_MVRG_8,      NULL);
+       setkeybinding(MODKEY_SHIFT,     XK_KP_Prior,    KF_MVRG_9,      NULL);
        setkeybinding(MODKEY_SHIFT,     XK_1,           KF_MVWS_1,      NULL);
        setkeybinding(MODKEY_SHIFT,     XK_2,           KF_MVWS_2,      NULL);
        setkeybinding(MODKEY_SHIFT,     XK_3,           KF_MVWS_3,      NULL);
@@ -6102,33 +6377,68 @@ setup_keys(void)
        setkeybinding(MODKEY_SHIFT,     XK_F10,         KF_MVWS_20,     NULL);
        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);
-       setkeybinding(MODKEY,           XK_x,           KF_WIND_DEL,    NULL);
+       setkeybinding(MODKEY_SHIFT,     XK_slash,       KF_NAME_WORKSPACE,NULL);
+       setkeybinding(MODKEY_SHIFT,     XK_q,           KF_QUIT,        NULL);
+       setkeybinding(MODKEY_SHIFT,     XK_r,           KF_RAISE_TOGGLE,NULL);
+       setkeybinding(MODKEY,           XK_q,           KF_RESTART,     NULL);
+       setkeybinding(MODKEY,           XK_KP_End,      KF_RG_1,        NULL);
+       setkeybinding(MODKEY,           XK_KP_Down,     KF_RG_2,        NULL);
+       setkeybinding(MODKEY,           XK_KP_Next,     KF_RG_3,        NULL);
+       setkeybinding(MODKEY,           XK_KP_Left,     KF_RG_4,        NULL);
+       setkeybinding(MODKEY,           XK_KP_Begin,    KF_RG_5,        NULL);
+       setkeybinding(MODKEY,           XK_KP_Right,    KF_RG_6,        NULL);
+       setkeybinding(MODKEY,           XK_KP_Home,     KF_RG_7,        NULL);
+       setkeybinding(MODKEY,           XK_KP_Up,       KF_RG_8,        NULL);
+       setkeybinding(MODKEY,           XK_KP_Prior,    KF_RG_9,        NULL);
+       setkeybinding(MODKEY_SHIFT,     XK_Right,       KF_RG_NEXT,     NULL);
+       setkeybinding(MODKEY_SHIFT,     XK_Left,        KF_RG_PREV,     NULL);
+       setkeybinding(MODKEY,           XK_f,           KF_SEARCH_WIN,  NULL);
+       setkeybinding(MODKEY,           XK_slash,       KF_SEARCH_WORKSPACE,NULL);
+       setkeybinding(MODKEY_SHIFT,     XK_i,           KF_SPAWN_CUSTOM,"initscr");
+       setkeybinding(MODKEY_SHIFT,     XK_Delete,      KF_SPAWN_CUSTOM,"lock");
+       setkeybinding(MODKEY,           XK_p,           KF_SPAWN_CUSTOM,"menu");
        setkeybinding(MODKEY,           XK_s,           KF_SPAWN_CUSTOM,"screenshot_all");
        setkeybinding(MODKEY_SHIFT,     XK_s,           KF_SPAWN_CUSTOM,"screenshot_wind");
-       setkeybinding(MODKEY,           XK_t,           KF_FLOAT_TOGGLE,NULL);
-       setkeybinding(MODKEY_SHIFT,     XK_v,           KF_VERSION,     NULL);
-       setkeybinding(MODKEY_SHIFT,     XK_Delete,      KF_SPAWN_CUSTOM,"lock");
-       setkeybinding(MODKEY_SHIFT,     XK_i,           KF_SPAWN_CUSTOM,"initscr");
-       setkeybinding(MODKEY,           XK_w,           KF_ICONIFY,     NULL);
+       setkeybinding(MODKEY_SHIFT,     XK_Return,      KF_SPAWN_CUSTOM,"term");
+       setkeybinding(MODKEY_SHIFT,     XK_comma,       KF_STACK_INC,   NULL);
+       setkeybinding(MODKEY_SHIFT,     XK_period,      KF_STACK_DEC,   NULL);
+       setkeybinding(MODKEY_SHIFT,     XK_space,       KF_STACK_RESET, NULL);
+       setkeybinding(MODKEY,           XK_Return,      KF_SWAP_MAIN,   NULL);
+       setkeybinding(MODKEY_SHIFT,     XK_j,           KF_SWAP_NEXT,   NULL);
+       setkeybinding(MODKEY_SHIFT,     XK_k,           KF_SWAP_PREV,   NULL);
        setkeybinding(MODKEY_SHIFT,     XK_w,           KF_UNICONIFY,   NULL);
-       setkeybinding(MODKEY_SHIFT,     XK_r,           KF_RAISE_TOGGLE,NULL);
-       setkeybinding(MODKEY,           XK_v,           KF_BUTTON2,     NULL);
+       setkeybinding(MODKEY_SHIFT,     XK_v,           KF_VERSION,     NULL);
        setkeybinding(MODKEY,           XK_equal,       KF_WIDTH_GROW,  NULL);
        setkeybinding(MODKEY,           XK_minus,       KF_WIDTH_SHRINK,NULL);
-       setkeybinding(MODKEY_SHIFT,     XK_equal,       KF_HEIGHT_GROW,NULL);
-       setkeybinding(MODKEY_SHIFT,     XK_minus,       KF_HEIGHT_SHRINK,NULL);
-       setkeybinding(MODKEY,           XK_bracketleft, KF_MOVE_LEFT,NULL);
-       setkeybinding(MODKEY,           XK_bracketright,KF_MOVE_RIGHT,NULL);
-       setkeybinding(MODKEY_SHIFT,     XK_bracketleft, KF_MOVE_UP,     NULL);
-       setkeybinding(MODKEY_SHIFT,     XK_bracketright,KF_MOVE_DOWN,NULL);
-       setkeybinding(MODKEY_SHIFT,     XK_slash,       KF_NAME_WORKSPACE,NULL);
-       setkeybinding(MODKEY,           XK_slash,       KF_SEARCH_WORKSPACE,NULL);
-       setkeybinding(MODKEY,           XK_f,           KF_SEARCH_WIN,  NULL);
+       setkeybinding(MODKEY,           XK_x,           KF_WIND_DEL,    NULL);
+       setkeybinding(MODKEY_SHIFT,     XK_x,           KF_WIND_KILL,   NULL);
+       setkeybinding(MODKEY,           XK_1,           KF_WS_1,        NULL);
+       setkeybinding(MODKEY,           XK_2,           KF_WS_2,        NULL);
+       setkeybinding(MODKEY,           XK_3,           KF_WS_3,        NULL);
+       setkeybinding(MODKEY,           XK_4,           KF_WS_4,        NULL);
+       setkeybinding(MODKEY,           XK_5,           KF_WS_5,        NULL);
+       setkeybinding(MODKEY,           XK_6,           KF_WS_6,        NULL);
+       setkeybinding(MODKEY,           XK_7,           KF_WS_7,        NULL);
+       setkeybinding(MODKEY,           XK_8,           KF_WS_8,        NULL);
+       setkeybinding(MODKEY,           XK_9,           KF_WS_9,        NULL);
+       setkeybinding(MODKEY,           XK_0,           KF_WS_10,       NULL);
+       setkeybinding(MODKEY,           XK_F1,          KF_WS_11,       NULL);
+       setkeybinding(MODKEY,           XK_F2,          KF_WS_12,       NULL);
+       setkeybinding(MODKEY,           XK_F3,          KF_WS_13,       NULL);
+       setkeybinding(MODKEY,           XK_F4,          KF_WS_14,       NULL);
+       setkeybinding(MODKEY,           XK_F5,          KF_WS_15,       NULL);
+       setkeybinding(MODKEY,           XK_F6,          KF_WS_16,       NULL);
+       setkeybinding(MODKEY,           XK_F7,          KF_WS_17,       NULL);
+       setkeybinding(MODKEY,           XK_F8,          KF_WS_18,       NULL);
+       setkeybinding(MODKEY,           XK_F9,          KF_WS_19,       NULL);
+       setkeybinding(MODKEY,           XK_F10,         KF_WS_20,       NULL);
+       setkeybinding(MODKEY,           XK_F11,         KF_WS_21,       NULL);
+       setkeybinding(MODKEY,           XK_F12,         KF_WS_22,       NULL);
+       setkeybinding(MODKEY,           XK_Right,       KF_WS_NEXT,     NULL);
+       setkeybinding(MODKEY,           XK_Left,        KF_WS_PREV,     NULL);
+       setkeybinding(MODKEY,           XK_Up,          KF_WS_NEXT_ALL, NULL);
+       setkeybinding(MODKEY,           XK_Down,        KF_WS_PREV_ALL, NULL);
+       setkeybinding(MODKEY,           XK_a,           KF_WS_PRIOR,    NULL);
 #ifdef SWM_DEBUG
        setkeybinding(MODKEY_SHIFT,     XK_d,           KF_DUMPWINS,    NULL);
 #endif
@@ -6231,7 +6541,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);
                        }
                }
@@ -6449,12 +6759,14 @@ enum {
        SWM_S_FOCUS_CLOSE_WRAP,
        SWM_S_FOCUS_DEFAULT,
        SWM_S_FOCUS_MODE,
+       SWM_S_REGION_PADDING,
        SWM_S_SPAWN_ORDER,
        SWM_S_SPAWN_TERM,
        SWM_S_SS_APP,
        SWM_S_SS_ENABLED,
        SWM_S_STACK_ENABLED,
        SWM_S_TERM_WIDTH,
+       SWM_S_TILE_GAP,
        SWM_S_TITLE_CLASS_ENABLED,
        SWM_S_TITLE_NAME_ENABLED,
        SWM_S_URGENT_ENABLED,
@@ -6604,6 +6916,11 @@ setconfvalue(char *selector, char *value, int flags)
                else
                        errx(1, "focus_mode");
                break;
+       case SWM_S_REGION_PADDING:
+               region_padding = atoi(value);
+               if (region_padding < 0)
+                       region_padding = 0;
+               break;
        case SWM_S_SPAWN_ORDER:
                if (!strcmp(value, "first"))
                        spawn_position = SWM_STACK_BOTTOM;
@@ -6634,6 +6951,11 @@ setconfvalue(char *selector, char *value, int flags)
                if (term_width < 0)
                        term_width = 0;
                break;
+       case SWM_S_TILE_GAP:
+               tile_gap = atoi(value);
+               if (tile_gap < 0)
+                       tile_gap = 0;
+               break;
        case SWM_S_TITLE_CLASS_ENABLED:
                title_class_enabled = atoi(value);
                break;
@@ -6870,58 +7192,86 @@ struct config_option {
        int                     funcflags;
 };
 struct config_option configopt[] = {
-       { "bar_enabled",                setconfvalue,   SWM_S_BAR_ENABLED },
-       { "bar_enabled_ws",             setconfvalue,   SWM_S_BAR_ENABLED_WS },
+       { "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_unfocus",         setconfcolor,   SWM_S_COLOR_BAR_BORDER_UNFOCUS },
        { "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 },
+       { "region_padding",             setconfvalue,   SWM_S_REGION_PADDING },
        { "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 },
+       { "tile_gap",                   setconfvalue,   SWM_S_TILE_GAP },
        { "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 },
 };
 
+void
+_add_startup_exception(const char *fmt, va_list ap)
+{
+       if (vasprintf(&startup_exception, fmt, ap) == -1)
+               warn("%s: asprintf", __func__);
+}
+
+void
+add_startup_exception(const char *fmt, ...)
+{
+       va_list ap;
+
+       nr_exceptions++;
+
+       if (startup_exception)
+               return;
+
+       /* force bar to be enabled due to exception */
+       bar_enabled = 1;
+
+       va_start(ap, fmt);
+       _add_startup_exception(fmt, ap);
+       va_end(ap);
+}
+
 int
 conf_load(const char *filename, int keymapping)
 {
        FILE                    *config;
-       char                    *line, *cp, *optsub, *optval;
+       char                    *line = NULL, *cp, *optsub, *optval = NULL;
        size_t                  linelen, lineno = 0;
        int                     wordlen, i, optidx;
        struct config_option    *opt = NULL;
@@ -6938,8 +7288,11 @@ conf_load(const char *filename, int keymapping)
        }
 
        while (!feof(config)) {
-               if ((line = fparseln(config, &linelen, &lineno, NULL, 0))
-                   == NULL) {
+               if (line)
+                       free(line);
+
+               if ((line = fparseln(config, &linelen, &lineno, NULL,
+                   FPARSELN_UNESCCOMM | FPARSELN_UNESCCONT)) == NULL) {
                        if (ferror(config))
                                err(1, "%s", filename);
                        else
@@ -6949,15 +7302,14 @@ conf_load(const char *filename, int keymapping)
                cp += strspn(cp, " \t\n"); /* eat whitespace */
                if (cp[0] == '\0') {
                        /* empty line */
-                       free(line);
                        continue;
                }
                /* get config option */
                wordlen = strcspn(cp, "=[ \t\n");
                if (wordlen == 0) {
-                       warnx("%s: line %zd: no option found",
+                       add_startup_exception("%s: line %zd: no option found",
                            filename, lineno);
-                       goto out;
+                       continue;
                }
                optidx = -1;
                for (i = 0; i < LENGTH(configopt); i++) {
@@ -6969,17 +7321,20 @@ conf_load(const char *filename, int keymapping)
                        }
                }
                if (optidx == -1) {
-                       warnx("%s: line %zd: unknown option %.*s",
-                           filename, lineno, wordlen, cp);
-                       goto out;
+                       add_startup_exception("%s: line %zd: unknown option "
+                           "%.*s", filename, lineno, wordlen, cp);
+                       continue;
                }
                if (keymapping && opt && strcmp(opt->optname, "bind")) {
-                       warnx("%s: line %zd: invalid option %.*s",
-                           filename, lineno, wordlen, cp);
-                       goto out;
+                       add_startup_exception("%s: line %zd: invalid option "
+                           "%.*s", filename, lineno, wordlen, cp);
+                       continue;
                }
                cp += wordlen;
                cp += strspn(cp, " \t\n"); /* eat whitespace */
+
+               /* from here on out we call goto invalid to continue */
+
                /* get [selector] if any */
                optsub = NULL;
                if (*cp == '[') {
@@ -6987,17 +7342,17 @@ conf_load(const char *filename, int keymapping)
                        wordlen = strcspn(cp, "]");
                        if (*cp != ']') {
                                if (wordlen == 0) {
-                                       warnx("%s: line %zd: syntax error",
-                                           filename, lineno);
-                                       goto out;
+                                       add_startup_exception("%s: line %zd: "
+                                           "syntax error", filename, lineno);
+                                       goto invalid;
                                }
 
                                if (asprintf(&optsub, "%.*s", wordlen, cp) ==
                                    -1) {
-                                       warnx("%s: line %zd: unable to allocate"
-                                           "memory for selector", filename,
-                                           lineno);
-                                       goto out;
+                                       add_startup_exception("%s: line %zd: "
+                                           "unable to allocatememory for "
+                                           "selector", filename, lineno);
+                                       goto invalid;
                                }
                        }
                        cp += wordlen;
@@ -7006,27 +7361,36 @@ conf_load(const char *filename, int keymapping)
                cp += strspn(cp, "= \t\n"); /* eat trailing */
                /* get RHS value */
                optval = strdup(cp);
+               if (strlen(optval) == 0) {
+                       add_startup_exception("%s: line %zd: must supply value "
+                           "to %s", filename, lineno,
+                           configopt[optidx].optname);
+                       goto invalid;
+               }
                /* call function to deal with it all */
                if (configopt[optidx].func(optsub, optval,
-                   configopt[optidx].funcflags) != 0)
-                       errx(1, "%s: line %zd: invalid data for %s",
-                           filename, lineno, configopt[optidx].optname);
-               free(optval);
-               free(optsub);
-               free(line);
+                   configopt[optidx].funcflags) != 0) {
+                       add_startup_exception("%s: line %zd: invalid data for "
+                           "%s", filename, lineno, configopt[optidx].optname);
+                       goto invalid;
+               }
+invalid:
+               if (optval) {
+                       free(optval);
+                       optval = NULL;
+               }
+               if (optsub) {
+                       free(optsub);
+                       optsub = NULL;
+               }
        }
 
+       if (line)
+               free(line);
        fclose(config);
        DNPRINTF(SWM_D_CONF, "conf_load: end\n");
 
        return (0);
-
-out:
-       free(line);
-       fclose(config);
-       DNPRINTF(SWM_D_CONF, "conf_load: end with error.\n");
-
-       return (1);
 }
 
 void
@@ -7427,7 +7791,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);
 }
@@ -7458,10 +7822,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;
 
@@ -7471,6 +7839,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
@@ -7512,7 +7887,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;
@@ -7855,7 +8230,7 @@ void
 enternotify(xcb_enter_notify_event_t *e)
 {
        struct ws_win           *win;
-       struct swm_region       *old_r, *r;
+       struct swm_region       *r;
 
        DNPRINTF(SWM_D_FOCUS, "enternotify: time: %u, win (x,y): 0x%x "
            "(%d,%d), mode: %s(%d), detail: %s(%d), root (x,y): 0x%x (%d,%d), "
@@ -7879,33 +8254,21 @@ enternotify(xcb_enter_notify_event_t *e)
                        /* If no windows on pointer region, then focus root. */
                        r = root_to_region(e->root, SWM_CK_POINTER);
                        if (r == NULL) {
-                               DNPRINTF(SWM_D_EVENT, "enterntoify: "
+                               DNPRINTF(SWM_D_EVENT, "enternotify: "
                                    "NULL region; ignoring.\n");
                                return;
                        }
 
-                       if (TAILQ_EMPTY(&r->ws->winlist)) {
-                               old_r = root_to_region(e->root, SWM_CK_FOCUS);
-                               if (old_r && old_r != r)
-                                       unfocus_win(old_r->ws->focus);
-
-                               xcb_set_input_focus(conn,
-                                   XCB_INPUT_FOCUS_PARENT, e->root, e->time);
-
-                               /* Clear bar since empty. */
-                               bar_update();
-
-                               focus_flush();
-                       }
+                       focus_region(r);
                } else {
                        DNPRINTF(SWM_D_EVENT, "enternotify: window is NULL; "
                            "ignoring\n");
+                       return;
                }
-               return;
+       } else {
+               focus_win(get_focus_magic(win));
        }
 
-       focus_win(get_focus_magic(win));
-
        xcb_flush(conn);
 }
 
@@ -7999,6 +8362,35 @@ out:
        DNPRINTF(SWM_D_EVENT, "maprequest: done.\n");
 }
 
+void
+motionnotify(xcb_motion_notify_event_t *e)
+{
+       struct swm_region       *r;
+       int                     i, num_screens;
+
+       DNPRINTF(SWM_D_FOCUS, "motionnotify: time: %u, win (x,y): 0x%x "
+           "(%d,%d), detail: %s(%d), root (x,y): 0x%x (%d,%d), "
+           "child: 0x%x, same_screen_focus: %s, state: %d\n",
+           e->time, e->event, e->event_x, e->event_y,
+           get_notify_detail_label(e->detail), e->detail,
+           e->root, e->root_x, e->root_y, e->child,
+           YESNO(e->same_screen), e->state);
+
+       last_event_time = e->time;
+
+       num_screens = xcb_setup_roots_length(xcb_get_setup(conn));
+       for (i = 0; i < num_screens; i++)
+               if (screens[i].root == e->root)
+                       break;
+
+       TAILQ_FOREACH(r, &screens[i].rl, entry)
+               if (X(r) <= e->root_x && e->root_x < MAX_X(r) &&
+                   Y(r) <= e->root_y && e->root_y < MAX_Y(r))
+                       break;
+
+       focus_region(r);
+}
+
 #ifdef SWM_DEBUG
 char *
 get_atom_name(xcb_atom_t atom)
@@ -8091,7 +8483,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);
@@ -8107,7 +8499,7 @@ unmapnotify(xcb_unmap_notify_event_t *e)
 
        /* If we aren't managing the window, then ignore. */
        win = find_window(e->window);
-       if (win == NULL)
+       if (win == NULL || win->id != e->window)
                return;
 
        ws = win->ws;
@@ -8308,6 +8700,7 @@ new_region(struct swm_screen *s, int x, int y, int w, int h)
        struct swm_region       *r, *n;
        struct workspace        *ws = NULL;
        int                     i;
+       uint32_t                wa[1];
 
        DNPRINTF(SWM_D_MISC, "new region: screen[%d]:%dx%d+%d+%d\n",
             s->idx, w, h, x, y);
@@ -8317,14 +8710,13 @@ new_region(struct swm_screen *s, int x, int y, int w, int h)
        while (n) {
                r = n;
                n = TAILQ_NEXT(r, entry);
-               if (X(r) < (x + w) &&
-                   (X(r) + WIDTH(r)) > x &&
-                   Y(r) < (y + h) &&
-                   (Y(r) + HEIGHT(r)) > y) {
+               if (X(r) < (x + w) && (X(r) + WIDTH(r)) > x &&
+                   Y(r) < (y + h) && (Y(r) + HEIGHT(r)) > y) {
                        if (r->ws->r != NULL)
                                r->ws->old_r = r->ws->r;
                        r->ws->r = NULL;
                        bar_cleanup(r);
+                       xcb_destroy_window(conn, r->id);
                        TAILQ_REMOVE(&s->rl, r, entry);
                        TAILQ_INSERT_TAIL(&s->orl, r, entry);
                }
@@ -8375,6 +8767,17 @@ new_region(struct swm_screen *s, int x, int y, int w, int h)
        ws->r = r;
        outputs++;
        TAILQ_INSERT_TAIL(&s->rl, r, entry);
+
+       /* Invisible region window to detect pointer events on empty regions. */
+       r->id = xcb_generate_id(conn);
+       wa[0] = XCB_EVENT_MASK_POINTER_MOTION |
+           XCB_EVENT_MASK_POINTER_MOTION_HINT;
+
+       xcb_create_window(conn, XCB_COPY_FROM_PARENT, r->id, r->s->root,
+           X(r), Y(r), WIDTH(r), HEIGHT(r), 0, XCB_WINDOW_CLASS_INPUT_ONLY,
+           XCB_COPY_FROM_PARENT, XCB_CW_EVENT_MASK, wa);
+
+       xcb_map_window(conn, r->id);
 }
 
 void
@@ -8385,6 +8788,7 @@ scan_xrandr(int i)
        int                                             ncrtc = 0;
 #endif /* SWM_XRR_HAS_CRTC */
        struct swm_region                               *r;
+       struct ws_win                                   *win;
        int                                             num_screens;
        xcb_randr_get_screen_resources_current_cookie_t src;
        xcb_randr_get_screen_resources_current_reply_t  *srr;
@@ -8406,6 +8810,7 @@ scan_xrandr(int i)
        while ((r = TAILQ_FIRST(&screens[i].rl)) != NULL) {
                r->ws->old_r = r->ws->r = NULL;
                bar_cleanup(r);
+               xcb_destroy_window(conn, r->id);
                TAILQ_REMOVE(&screens[i].rl, r, entry);
                TAILQ_INSERT_TAIL(&screens[i].orl, r, entry);
        }
@@ -8457,6 +8862,16 @@ scan_xrandr(int i)
                    screen->height_in_pixels);
 
 out:
+       /* Cleanup unused previously visible workspaces. */
+       TAILQ_FOREACH(r, &screens[i].orl, entry) {
+               TAILQ_FOREACH(win, &r->ws->winlist, entry)
+                       unmap_window(win);
+
+               /* The screen shouldn't focus on an unused region. */
+               if (screens[i].r_focus == r)
+                       screens[i].r_focus = NULL;
+       }
+
        DNPRINTF(SWM_D_MISC, "scan_xrandr: done.\n");
 }
 
@@ -8483,15 +8898,30 @@ screenchange(xcb_randr_screen_change_notify_event_t *e)
        print_win_geom(e->root);
 #endif
        /* add bars to all regions */
-       for (i = 0; i < num_screens; i++)
+       for (i = 0; i < num_screens; i++) {
                TAILQ_FOREACH(r, &screens[i].rl, entry)
                        bar_setup(r);
+       }
+
        stack();
+
+       /* Make sure a region has focus on each screen. */
+       for (i = 0; i < num_screens; i++) {
+               if (screens[i].r_focus == NULL) {
+                       r = TAILQ_FIRST(&screens[i].rl);
+                       if (r != NULL)
+                               focus_region(r);
+               }
+       }
+
+       bar_draw();
+       focus_flush();
 }
 
 void
 grab_windows(void)
 {
+       struct swm_region       *r = NULL;
        xcb_window_t            *wins = NULL, trans;
        int                     no;
        int                     i, j, num_screens;
@@ -8499,8 +8929,8 @@ grab_windows(void)
 
        xcb_query_tree_cookie_t                 qtc;
        xcb_query_tree_reply_t                  *qtr;
-       xcb_get_window_attributes_cookie_t      c;
-       xcb_get_window_attributes_reply_t       *r;
+       xcb_get_window_attributes_cookie_t      gac;
+       xcb_get_window_attributes_reply_t       *gar;
        xcb_get_property_cookie_t               pc;
 
        DNPRINTF(SWM_D_INIT, "grab_windows: begin\n");
@@ -8516,17 +8946,30 @@ grab_windows(void)
                /* normal windows */
                DNPRINTF(SWM_D_INIT, "grab_windows: grab top level windows.\n");
                for (j = 0; j < no; j++) {
-                       c = xcb_get_window_attributes(conn, wins[j]);
-                       r = xcb_get_window_attributes_reply(conn, c, NULL);
-                       if (!r) {
+                       TAILQ_FOREACH(r, &screens[i].rl, entry) {
+                               if (r->id == wins[j]) {
+                                       DNPRINTF(SWM_D_INIT, "grab_windows: "
+                                           "skip %#x; region input window.\n",
+                                           wins[j]);
+                                       break;
+                               }
+                       }
+
+                       if (r)
+                               continue;
+
+                       gac = xcb_get_window_attributes(conn, wins[j]);
+                       gar = xcb_get_window_attributes_reply(conn, gac, NULL);
+                       if (!gar) {
                                DNPRINTF(SWM_D_INIT, "grab_windows: skip %#x; "
                                    "doesn't exist.\n", wins[j]);
                                continue;
                        }
-                       if (r->override_redirect) {
+
+                       if (gar->override_redirect) {
                                DNPRINTF(SWM_D_INIT, "grab_windows: skip %#x; "
                                    "override_redirect set.\n", wins[j]);
-                               free(r);
+                               free(gar);
                                continue;
                        }
 
@@ -8535,42 +8978,43 @@ grab_windows(void)
                            &trans, NULL)) {
                                DNPRINTF(SWM_D_INIT, "grab_windows: skip %#x; "
                                    "is transient for %#x.\n", wins[j], trans);
-                               free(r);
+                               free(gar);
                                continue;
                        }
 
                        state = getstate(wins[j]);
                        manage = state != XCB_ICCCM_WM_STATE_WITHDRAWN;
-                       mapped = r->map_state != XCB_MAP_STATE_UNMAPPED;
+                       mapped = gar->map_state != XCB_MAP_STATE_UNMAPPED;
                        if (mapped || manage)
                                manage_window(wins[j], mapped);
-                       free(r);
+                       free(gar);
                }
                /* transient windows */
                DNPRINTF(SWM_D_INIT, "grab_windows: grab transient windows.\n");
                for (j = 0; j < no; j++) {
-                       c = xcb_get_window_attributes(conn, wins[j]);
-                       r = xcb_get_window_attributes_reply(conn, c, NULL);
-                       if (!r) {
+                       gac = xcb_get_window_attributes(conn, wins[j]);
+                       gar = xcb_get_window_attributes_reply(conn, gac, NULL);
+                       if (!gar) {
                                DNPRINTF(SWM_D_INIT, "grab_windows: skip %#x; "
                                    "doesn't exist.\n", wins[j]);
                                continue;
                        }
-                       if (r->override_redirect) {
+
+                       if (gar->override_redirect) {
                                DNPRINTF(SWM_D_INIT, "grab_windows: skip %#x; "
                                    "override_redirect set.\n", wins[j]);
-                               free(r);
+                               free(gar);
                                continue;
                        }
 
                        state = getstate(wins[j]);
                        manage = state != XCB_ICCCM_WM_STATE_WITHDRAWN;
-                       mapped = r->map_state != XCB_MAP_STATE_UNMAPPED;
+                       mapped = gar->map_state != XCB_MAP_STATE_UNMAPPED;
                        pc = xcb_icccm_get_wm_transient_for(conn, wins[j]);
                        if (xcb_icccm_get_wm_transient_for_reply(conn, pc,
                            &trans, NULL) && manage)
                                manage_window(wins[j], mapped);
-                       free(r);
+                       free(gar);
                }
                free(qtr);
        }
@@ -8615,6 +9059,8 @@ setup_screens(void)
        for (i = 0; i < num_screens; i++) {
                DNPRINTF(SWM_D_WS, "setup_screens: init screen: %d\n", i);
                screens[i].idx = i;
+               screens[i].r_focus = NULL;
+
                TAILQ_INIT(&screens[i].rl);
                TAILQ_INIT(&screens[i].orl);
                if ((screen = get_screen(i)) == NULL)
@@ -8625,6 +9071,8 @@ setup_screens(void)
                setscreencolor("red", i + 1, SWM_S_COLOR_FOCUS);
                setscreencolor("rgb:88/88/88", i + 1, SWM_S_COLOR_UNFOCUS);
                setscreencolor("rgb:00/80/80", i + 1, SWM_S_COLOR_BAR_BORDER);
+               setscreencolor("rgb:00/40/40", i + 1,
+                   SWM_S_COLOR_BAR_BORDER_UNFOCUS);
                setscreencolor("black", i + 1, SWM_S_COLOR_BAR);
                setscreencolor("rgb:a0/a0/a0", i + 1, SWM_S_COLOR_BAR_FONT);
 
@@ -8732,7 +9180,6 @@ shutdown_cleanup(void)
                err(1, "can't disable alarm");
 
        bar_extra_stop();
-       bar_extra = 1;
        unmap_all();
 
        cursors_cleanup();
@@ -8810,7 +9257,7 @@ event_handle(xcb_generic_event_t *evt)
        EVENT(XCB_MAP_NOTIFY, mapnotify);
        EVENT(XCB_MAP_REQUEST, maprequest);
        EVENT(XCB_MAPPING_NOTIFY, mappingnotify);
-       /*EVENT(XCB_MOTION_NOTIFY, );*/
+       EVENT(XCB_MOTION_NOTIFY, motionnotify);
        /*EVENT(XCB_NO_EXPOSURE, );*/
        EVENT(XCB_PROPERTY_NOTIFY, propertynotify);
        /*EVENT(XCB_REPARENT_NOTIFY, );*/
@@ -8829,17 +9276,16 @@ event_handle(xcb_generic_event_t *evt)
 int
 main(int argc, char *argv[])
 {
-       struct swm_region       *r, *rr;
-       struct ws_win           *winfocus = NULL;
+       struct swm_region       *r;
        char                    conf[PATH_MAX], *cfile = NULL;
        struct stat             sb;
-       int                     xfd, i, num_screens;
+       int                     xfd, i, num_screens, startup = 1;
        struct sigaction        sact;
        xcb_generic_event_t     *evt;
        struct timeval          tv;
        fd_set                  rd;
        int                     rd_max;
-       int                     do_bar_update = 0;
+       int                     stdin_ready = 0;
        int                     num_readable;
 
        /* suppress unused warning since var is needed */
@@ -8947,6 +9393,8 @@ noconfig:
        if (cfile)
                conf_load(cfile, SWM_CONF_DEFAULT);
 
+       validate_spawns();
+
        setup_ewmh();
        /* set some values to work around bad programs */
        workaround();
@@ -8959,14 +9407,12 @@ noconfig:
        /* setup all bars */
        num_screens = xcb_setup_roots_length(xcb_get_setup(conn));
        for (i = 0; i < num_screens; i++)
-               TAILQ_FOREACH(r, &screens[i].rl, entry) {
-                       if (winfocus == NULL)
-                               winfocus = TAILQ_FIRST(&r->ws->winlist);
+               TAILQ_FOREACH(r, &screens[i].rl, entry)
                        bar_setup(r);
-               }
 
        grabkeys();
        stack();
+       bar_draw();
 
        xcb_ungrab_server(conn);
        xcb_flush(conn);
@@ -8982,30 +9428,33 @@ noconfig:
                }
 
                /* If just (re)started, set default focus if needed. */
-               if (winfocus && focus_mode != SWM_FOCUS_FOLLOW) {
-                       rr = winfocus->ws->r;
-                       if (rr == NULL) {
-                               /* not a visible window */
-                               winfocus = NULL;
+               if (startup) {
+                       startup = 0;
+
+                       if (focus_mode != SWM_FOCUS_FOLLOW) {
+                               r = TAILQ_FIRST(&screens[0].rl);
+                               if (r) {
+                                       focus_region(r);
+                                       focus_flush();
+                               }
                                continue;
                        }
-
-                       focus_win(get_region_focus(rr));
-                       focus_flush();
-                       winfocus = NULL;
-                       continue;
                }
 
                FD_ZERO(&rd);
-               FD_SET(STDIN_FILENO, &rd);
+
+               if (bar_extra)
+                       FD_SET(STDIN_FILENO, &rd);
+
                FD_SET(xfd, &rd);
                tv.tv_sec = 1;
                tv.tv_usec = 0;
                num_readable = select(rd_max + 1, &rd, NULL, NULL, &tv);
-               if (num_readable == -1 && errno != EINTR)
+               if (num_readable == -1 && errno != EINTR) {
                        DNPRINTF(SWM_D_MISC, "select failed");
-               else if (num_readable > 0 && FD_ISSET(STDIN_FILENO, &rd))
-                       do_bar_update = 1;
+               } else if (num_readable > 0 && FD_ISSET(STDIN_FILENO, &rd)) {
+                       stdin_ready = 1;
+               }
 
                if (restart_wm)
                        restart(NULL, NULL);
@@ -9016,10 +9465,13 @@ noconfig:
                if (!running)
                        goto done;
 
-               if (do_bar_update) {
-                       do_bar_update = 0;
-                       bar_update();
+               if (stdin_ready) {
+                       stdin_ready = 0;
+                       bar_extra_update();
                }
+
+               bar_draw();
+               xcb_flush(conn);
        }
 done:
        shutdown_cleanup();