JasonWoof Got questions, comments, patches, etc.? Contact Jason Woofenden
Fix status bar flicker by double-buffering the output.
[spectrwm.git] / spectrwm.c
index 09c7ede..1115cac 100644 (file)
@@ -312,7 +312,6 @@ pid_t                       bar_pid;
 XFontSet               bar_fs;
 XFontSetExtents                *bar_fs_extents;
 char                   *bar_fonts;
-char                   *spawn_term[] = { NULL, NULL }; /* XXX fully dynamic */
 struct passwd          *pwd;
 
 #define SWM_MENU_FN    (2)
@@ -332,6 +331,12 @@ struct swm_geometry {
 struct swm_screen;
 struct workspace;
 
+struct swm_bar {
+       Window                  id;
+       Pixmap                  buffer;
+       struct swm_geometry     g;
+};
+
 /* virtual "screens" */
 struct swm_region {
        TAILQ_ENTRY(swm_region) entry;
@@ -339,7 +344,7 @@ struct swm_region {
        struct workspace        *ws;    /* current workspace on this region */
        struct workspace        *ws_prior; /* prior workspace on this region */
        struct swm_screen       *s;     /* screen idx */
-       Window                  bar_window;
+       struct swm_bar          *bar;
 };
 TAILQ_HEAD(swm_region_list, swm_region);
 
@@ -453,8 +458,14 @@ struct workspace {
        } l_state;
 };
 
-enum   { SWM_S_COLOR_BAR, SWM_S_COLOR_BAR_BORDER, SWM_S_COLOR_BAR_FONT,
-         SWM_S_COLOR_FOCUS, SWM_S_COLOR_UNFOCUS, SWM_S_COLOR_MAX };
+enum {
+       SWM_S_COLOR_BAR,
+       SWM_S_COLOR_BAR_BORDER,
+       SWM_S_COLOR_BAR_FONT,
+       SWM_S_COLOR_FOCUS,
+       SWM_S_COLOR_UNFOCUS,
+       SWM_S_COLOR_MAX
+};
 
 /* physical screen mapping */
 #define SWM_WS_MAX             (22)    /* hard limit */
@@ -543,17 +554,33 @@ struct quirk_list         quirks = TAILQ_HEAD_INITIALIZER(quirks);
  * Supported EWMH hints should be added to
  * both the enum and the ewmh array
  */
-enum { _NET_ACTIVE_WINDOW, _NET_MOVERESIZE_WINDOW, _NET_CLOSE_WINDOW,
-    _NET_WM_WINDOW_TYPE, _NET_WM_WINDOW_TYPE_DOCK,
-    _NET_WM_WINDOW_TYPE_TOOLBAR, _NET_WM_WINDOW_TYPE_UTILITY,
-    _NET_WM_WINDOW_TYPE_SPLASH, _NET_WM_WINDOW_TYPE_DIALOG,
-    _NET_WM_WINDOW_TYPE_NORMAL, _NET_WM_STATE,
-    _NET_WM_STATE_MAXIMIZED_HORZ, _NET_WM_STATE_MAXIMIZED_VERT,
-    _NET_WM_STATE_SKIP_TASKBAR, _NET_WM_STATE_SKIP_PAGER,
-    _NET_WM_STATE_HIDDEN, _NET_WM_STATE_ABOVE, _SWM_WM_STATE_MANUAL,
-    _NET_WM_STATE_FULLSCREEN, _NET_WM_ALLOWED_ACTIONS, _NET_WM_ACTION_MOVE,
-    _NET_WM_ACTION_RESIZE, _NET_WM_ACTION_FULLSCREEN, _NET_WM_ACTION_CLOSE,
-    SWM_EWMH_HINT_MAX };
+enum {
+       _NET_ACTIVE_WINDOW,
+       _NET_CLOSE_WINDOW,
+       _NET_MOVERESIZE_WINDOW,
+       _NET_WM_ACTION_CLOSE,
+       _NET_WM_ACTION_FULLSCREEN,
+       _NET_WM_ACTION_MOVE,
+       _NET_WM_ACTION_RESIZE,
+       _NET_WM_ALLOWED_ACTIONS,
+       _NET_WM_STATE,
+       _NET_WM_STATE_ABOVE,
+       _NET_WM_STATE_FULLSCREEN,
+       _NET_WM_STATE_HIDDEN,
+       _NET_WM_STATE_MAXIMIZED_HORZ,
+       _NET_WM_STATE_MAXIMIZED_VERT,
+       _NET_WM_STATE_SKIP_PAGER,
+       _NET_WM_STATE_SKIP_TASKBAR,
+       _NET_WM_WINDOW_TYPE,
+       _NET_WM_WINDOW_TYPE_DIALOG,
+       _NET_WM_WINDOW_TYPE_DOCK,
+       _NET_WM_WINDOW_TYPE_NORMAL,
+       _NET_WM_WINDOW_TYPE_SPLASH,
+       _NET_WM_WINDOW_TYPE_TOOLBAR,
+       _NET_WM_WINDOW_TYPE_UTILITY,
+       _SWM_WM_STATE_MANUAL,
+       SWM_EWMH_HINT_MAX
+};
 
 struct ewmh_hint {
        char    *name;
@@ -561,29 +588,29 @@ struct ewmh_hint {
 } ewmh[SWM_EWMH_HINT_MAX] =    {
     /* must be in same order as in the enum */
     {"_NET_ACTIVE_WINDOW", None},
-    {"_NET_MOVERESIZE_WINDOW", None},
     {"_NET_CLOSE_WINDOW", None},
-    {"_NET_WM_WINDOW_TYPE", None},
-    {"_NET_WM_WINDOW_TYPE_DOCK", None},
-    {"_NET_WM_WINDOW_TYPE_TOOLBAR", None},
-    {"_NET_WM_WINDOW_TYPE_UTILITY", None},
-    {"_NET_WM_WINDOW_TYPE_SPLASH", None},
-    {"_NET_WM_WINDOW_TYPE_DIALOG", None},
-    {"_NET_WM_WINDOW_TYPE_NORMAL", None},
+    {"_NET_MOVERESIZE_WINDOW", None},
+    {"_NET_WM_ACTION_CLOSE", None},
+    {"_NET_WM_ACTION_FULLSCREEN", None},
+    {"_NET_WM_ACTION_MOVE", None},
+    {"_NET_WM_ACTION_RESIZE", None},
+    {"_NET_WM_ALLOWED_ACTIONS", None},
     {"_NET_WM_STATE", None},
+    {"_NET_WM_STATE_ABOVE", None},
+    {"_NET_WM_STATE_FULLSCREEN", None},
+    {"_NET_WM_STATE_HIDDEN", None},
     {"_NET_WM_STATE_MAXIMIZED_HORZ", None},
     {"_NET_WM_STATE_MAXIMIZED_VERT", None},
-    {"_NET_WM_STATE_SKIP_TASKBAR", None},
     {"_NET_WM_STATE_SKIP_PAGER", None},
-    {"_NET_WM_STATE_HIDDEN", None},
-    {"_NET_WM_STATE_ABOVE", None},
+    {"_NET_WM_STATE_SKIP_TASKBAR", None},
+    {"_NET_WM_WINDOW_TYPE", None},
+    {"_NET_WM_WINDOW_TYPE_DIALOG", None},
+    {"_NET_WM_WINDOW_TYPE_DOCK", None},
+    {"_NET_WM_WINDOW_TYPE_NORMAL", None},
+    {"_NET_WM_WINDOW_TYPE_SPLASH", None},
+    {"_NET_WM_WINDOW_TYPE_TOOLBAR", None},
+    {"_NET_WM_WINDOW_TYPE_UTILITY", None},
     {"_SWM_WM_STATE_MANUAL", None},
-    {"_NET_WM_STATE_FULLSCREEN", None},
-    {"_NET_WM_ALLOWED_ACTIONS", None},
-    {"_NET_WM_ACTION_MOVE", None},
-    {"_NET_WM_ACTION_RESIZE", None},
-    {"_NET_WM_ACTION_FULLSCREEN", None},
-    {"_NET_WM_ACTION_CLOSE", None},
 };
 
 void            store_float_geom(struct ws_win *, struct swm_region *);
@@ -1304,14 +1331,12 @@ socket_setnonblock(int fd)
 }
 
 void
-bar_print(struct swm_region *r, char *s)
+bar_print(struct swm_region *r, const char *s)
 {
        int                     x = 0;
        size_t                  len;
        XRectangle              ibox, lbox;
 
-       XClearWindow(display, r->bar_window);
-
        len = strlen(s);
        XmbTextExtents(bar_fs, s, len, &ibox, &lbox);
 
@@ -1330,9 +1355,21 @@ bar_print(struct swm_region *r, char *s)
        if (x < SWM_BAR_OFFSET)
                x = SWM_BAR_OFFSET;
 
-       DRAWSTRING(display, r->bar_window, bar_fs, r->s->bar_gc,
+       /* clear back buffer */
+       XSetForeground(display, r->s->bar_gc, r->s->c[SWM_S_COLOR_BAR].color);
+       XFillRectangle(display, r->bar->buffer, r->s->bar_gc, 0, 0,
+           WIDTH(r->bar), HEIGHT(r->bar));
+
+       /* draw back buffer */
+       XSetForeground(display, r->s->bar_gc,
+           r->s->c[SWM_S_COLOR_BAR_FONT].color);
+       DRAWSTRING(display, r->bar->buffer, bar_fs, r->s->bar_gc,
            x, (bar_fs_extents->max_logical_extent.height - lbox.height) / 2 -
            lbox.y, s, len);
+
+       /* blt */
+       XCopyArea(display, r->bar->buffer, r->bar->id, r->s->bar_gc, 0, 0,
+           WIDTH(r->bar), HEIGHT(r->bar), 0, 0);
 }
 
 void
@@ -1369,6 +1406,26 @@ bar_title_name(char *s, size_t sz, struct swm_region *r)
 }
 
 void
+bar_class_title_name(char *s, size_t sz, struct swm_region *r)
+{
+       if (r == NULL || r->ws == NULL || r->ws->focus == NULL)
+               return;
+
+       bar_class_name(s, sz, r);
+       strlcat(s, ":", sz);
+       bar_title_name(s, sz, r);
+}
+
+void
+bar_window_float(char *s, size_t sz, struct swm_region *r)
+{
+       if (r == NULL || r ->ws == NULL || r->ws->focus == NULL)
+               return;
+       if (r->ws->focus->floating)
+               strlcat(s, "(f)", sz);
+}
+
+void
 bar_window_name(char *s, size_t sz, struct swm_region *r)
 {
        unsigned char           *title;
@@ -1378,11 +1435,7 @@ bar_window_name(char *s, size_t sz, struct swm_region *r)
        if ((title = get_win_name(r->ws->focus->id)) == NULL)
                return;
 
-       if (r->ws->focus->floating)
-               strlcat(s, "(f) ", sz);
        strlcat(s, (char *)title, sz);
-       strlcat(s, " ", sz);
-
        XFree(title);
 }
 
@@ -1430,7 +1483,7 @@ bar_workspace_name(char *s, size_t sz, struct swm_region *r)
 
 /* build the default bar format according to the defined enabled options */
 void
-bar_fmt(char *fmtexp, char *fmtnew, struct swm_region *r, size_t sz)
+bar_fmt(const char *fmtexp, char *fmtnew, struct swm_region *r, size_t sz)
 {
        /* if format provided, just copy the buffers */
        if (bar_format != NULL) {
@@ -1449,33 +1502,39 @@ bar_fmt(char *fmtexp, char *fmtnew, struct swm_region *r, size_t sz)
        /* only show the workspace name if there's actually one */
        if (r != NULL && r->ws != NULL && r->ws->name != NULL)
                strlcat(fmtnew, "<+D>", sz);
-       strlcat(fmtnew, "   ", sz);
+       strlcat(fmtnew, "+3<", sz);
 
        if (clock_enabled) {
                strlcat(fmtnew, fmtexp, sz);
-               strlcat(fmtnew, "    ", sz);
+               strlcat(fmtnew, "+4<", sz);
        }
 
        /* bar_urgent already adds the space before the last asterisk */
        if (urgent_enabled)
-               strlcat(fmtnew, "* +U*    ", sz);
+               strlcat(fmtnew, "* +U*+4<", sz);
 
-       if (title_class_enabled)
+       if (title_class_enabled) {
                strlcat(fmtnew, "+C", sz);
-       if (title_name_enabled) {
-               /* add a colon if showing the class and something is focused */
-               if (title_class_enabled && r != NULL && r->ws != NULL &&
-                   r->ws->focus != NULL)
-                       strlcat(fmtnew, ":", sz);
-               strlcat(fmtnew, "+T", sz);
+               if (title_name_enabled == 0)
+                       strlcat(fmtnew, "+4<", sz);
        }
 
-       strlcat(fmtnew, "    ", sz);
-       if (window_name_enabled)
-               strlcat(fmtnew, "+64W", sz);
+       /* checks needed by the colon and floating strlcat(3) calls below */
+       if (r != NULL && r->ws != NULL && r->ws->focus != NULL) {
+               if (title_name_enabled) {
+                       if (title_class_enabled)
+                               strlcat(fmtnew, ":", sz);
+                       strlcat(fmtnew, "+T+4<", sz);
+               }
+               if (window_name_enabled) {
+                       if (r->ws->focus->floating)
+                               strlcat(fmtnew, "+F ", sz);
+                       strlcat(fmtnew, "+64W ", sz);
+               }
+       }
 
        /* finally add the action script output and the version */
-       strlcat(fmtnew, "     +A    +V", sz);
+       strlcat(fmtnew, "+4<+A+4<+V", sz);
 }
 
 /* replaces the bar format character sequences (like in tmux(1)) */
@@ -1513,6 +1572,12 @@ bar_replace_seq(char *fmt, char *fmtrep, struct swm_region *r, size_t *offrep,
                return (fmt);
 
        switch (*fmt) {
+       case '<':
+               /* special case; no limit given, pad one space, instead */
+               if (limit == sizeof tmp - 1)
+                       limit = 1;
+               snprintf(tmp, sizeof tmp, "%*s", limit, " ");
+               break;
        case 'A':
                snprintf(tmp, sizeof tmp, "%s", bar_ext);
                break;
@@ -1522,12 +1587,18 @@ bar_replace_seq(char *fmt, char *fmtrep, struct swm_region *r, size_t *offrep,
        case 'D':
                bar_workspace_name(tmp, sizeof tmp, r);
                break;
+       case 'F':
+               bar_window_float(tmp, sizeof tmp, r);
+               break;
        case 'I':
                snprintf(tmp, sizeof tmp, "%d", r->ws->idx + 1);
                break;
        case 'N':
                snprintf(tmp, sizeof tmp, "%d", r->s->idx + 1);
                break;
+       case 'P':
+               bar_class_title_name(tmp, sizeof tmp, r);
+               break;
        case 'S':
                snprintf(tmp, sizeof tmp, "%s", r->ws->stacker);
                break;
@@ -1633,6 +1704,8 @@ bar_fmt_print(void)
 
        for (i = 0; i < ScreenCount(display); i++) {
                TAILQ_FOREACH(r, &screens[i].rl, entry) {
+                       if (r->bar == NULL)
+                               continue;
                        bar_fmt(fmtexp, fmtnew, r, sizeof fmtnew);
                        bar_replace(fmtnew, fmtrep, r, sizeof fmtrep);
                        bar_print(r, fmtrep);
@@ -1680,14 +1753,17 @@ bar_toggle(struct swm_region *r, union arg *args)
 
        DNPRINTF(SWM_D_BAR, "bar_toggle\n");
 
-       if (bar_enabled)
+       if (bar_enabled) {
                for (i = 0; i < sc; i++)
                        TAILQ_FOREACH(tmpr, &screens[i].rl, entry)
-                               XUnmapWindow(display, tmpr->bar_window);
-       else
+                               if (tmpr->bar)
+                                       XUnmapWindow(display, tmpr->bar->id);
+       } else {
                for (i = 0; i < sc; i++)
                        TAILQ_FOREACH(tmpr, &screens[i].rl, entry)
-                               XMapRaised(display, tmpr->bar_window);
+                               if (tmpr->bar)
+                                       XMapRaised(display, tmpr->bar->id);
+       }
 
        bar_enabled = !bar_enabled;
 
@@ -1735,11 +1811,13 @@ bar_refresh(void)
        bzero(&wa, sizeof wa);
        for (i = 0; i < ScreenCount(display); i++)
                TAILQ_FOREACH(r, &screens[i].rl, entry) {
+                       if (r->bar == NULL)
+                               continue;
                        wa.border_pixel =
                            screens[i].c[SWM_S_COLOR_BAR_BORDER].color;
                        wa.background_pixel =
                            screens[i].c[SWM_S_COLOR_BAR].color;
-                       XChangeWindowAttributes(display, r->bar_window,
+                       XChangeWindowAttributes(display, r->bar->id,
                            CWBackPixel | CWBorderPixel, &wa);
                }
        bar_update();
@@ -1751,13 +1829,15 @@ bar_setup(struct swm_region *r)
        char                    *default_string;
        char                    **missing_charsets;
        int                     num_missing_charsets = 0;
-       int                     i, x, y;
+       int                     i;
 
        if (bar_fs) {
                XFreeFontSet(display, bar_fs);
                bar_fs = NULL;
        }
 
+       if ((r->bar = calloc(1, sizeof(struct swm_bar))) == NULL)
+               err(1, "bar_setup: calloc: failed to allocate memory.");
 
        DNPRINTF(SWM_D_BAR, "bar_setup: loading bar_fonts: %s\n", bar_fonts);
 
@@ -1790,18 +1870,27 @@ bar_setup(struct swm_region *r)
        if (bar_height < 1)
                bar_height = 1;
 
-       x = X(r);
-       y = bar_at_bottom ? (Y(r) + HEIGHT(r) - bar_height) : Y(r);
+       X(r->bar) = X(r);
+       Y(r->bar) = bar_at_bottom ? (Y(r) + HEIGHT(r) - bar_height) : Y(r);
+       WIDTH(r->bar) = WIDTH(r) - 2 * bar_border_width;
+       HEIGHT(r->bar) = bar_height - 2 * bar_border_width;
 
-       r->bar_window = XCreateSimpleWindow(display,
-           r->s->root, x, y, WIDTH(r) - 2 * bar_border_width,
-           bar_height - 2 * bar_border_width,
+       r->bar->id = XCreateSimpleWindow(display,
+           r->s->root, X(r->bar), Y(r->bar), WIDTH(r->bar), HEIGHT(r->bar),
            bar_border_width, r->s->c[SWM_S_COLOR_BAR_BORDER].color,
            r->s->c[SWM_S_COLOR_BAR].color);
-       XSelectInput(display, r->bar_window, VisibilityChangeMask);
+
+       r->bar->buffer = XCreatePixmap(display, r->bar->id, WIDTH(r->bar),
+           HEIGHT(r->bar), DefaultDepth(display, r->s->idx));
+
+       XSelectInput(display, r->bar->id, VisibilityChangeMask);
+
        if (bar_enabled)
-               XMapRaised(display, r->bar_window);
-       DNPRINTF(SWM_D_BAR, "bar_setup: bar_window: 0x%lx\n", r->bar_window);
+               XMapRaised(display, r->bar->id);
+
+       DNPRINTF(SWM_D_BAR, "bar_setup: window: 0x%lx, (x,y) w x h: (%d,%d) "
+           "%d x %d\n", WINID(r->bar), X(r->bar), Y(r->bar), WIDTH(r->bar),
+           HEIGHT(r->bar));
 
        if (signal(SIGALRM, bar_signal) == SIG_ERR)
                err(1, "could not install bar_signal");
@@ -1809,6 +1898,17 @@ bar_setup(struct swm_region *r)
 }
 
 void
+bar_cleanup(struct swm_region *r)
+{
+       if (r->bar == NULL)
+               return;
+       XDestroyWindow(display, r->bar->id);
+       XFreePixmap(display, r->bar->buffer);
+       free(r->bar);
+       r->bar = NULL;
+}
+
+void
 drain_enter_notify(void)
 {
        int                     i = 0;
@@ -2198,18 +2298,6 @@ spawn(int ws_idx, union arg *args, int close_fd)
 }
 
 void
-spawnterm(struct swm_region *r, union arg *args)
-{
-       DNPRINTF(SWM_D_MISC, "spawnterm\n");
-
-       if (fork() == 0) {
-               if (term_width)
-                       setenv("_SWM_XTERM_FONTADJ", "", 1);
-               spawn(r->ws->idx, args, 1);
-       }
-}
-
-void
 kill_refs(struct ws_win *win)
 {
        int                     i, x;
@@ -4365,8 +4453,6 @@ enum keyfuncid {
        kf_focus_prev,
        kf_swap_next,
        kf_swap_prev,
-       kf_spawn_term,
-       kf_spawn_menu,
        kf_quit,
        kf_restart,
        kf_focus_main,
@@ -4424,12 +4510,8 @@ enum keyfuncid {
        kf_bar_toggle,
        kf_wind_kill,
        kf_wind_del,
-       kf_screenshot_all,
-       kf_screenshot_wind,
        kf_float_toggle,
        kf_version,
-       kf_spawn_lock,
-       kf_spawn_initscr,
        kf_spawn_custom,
        kf_iconify,
        kf_uniconify,
@@ -4456,11 +4538,6 @@ dummykeyfunc(struct swm_region *r, union arg *args)
 {
 };
 
-void
-legacyfunc(struct swm_region *r, union arg *args)
-{
-};
-
 struct keyfunc {
        char                    name[SWM_FUNCNAME_LEN];
        void                    (*func)(struct swm_region *r, union arg *);
@@ -4481,8 +4558,6 @@ struct keyfunc {
        { "focus_prev",         focus,          {.id = SWM_ARG_ID_FOCUSPREV} },
        { "swap_next",          swapwin,        {.id = SWM_ARG_ID_SWAPNEXT} },
        { "swap_prev",          swapwin,        {.id = SWM_ARG_ID_SWAPPREV} },
-       { "spawn_term",         spawnterm,      {.argv = spawn_term} },
-       { "spawn_menu",         legacyfunc,     {0} },
        { "quit",               quit,           {0} },
        { "restart",            restart,        {0} },
        { "focus_main",         focus,          {.id = SWM_ARG_ID_FOCUSMAIN} },
@@ -4540,12 +4615,8 @@ struct keyfunc {
        { "bar_toggle",         bar_toggle,     {0} },
        { "wind_kill",          wkill,          {.id = SWM_ARG_ID_KILLWINDOW} },
        { "wind_del",           wkill,          {.id = SWM_ARG_ID_DELETEWINDOW} },
-       { "screenshot_all",     legacyfunc,     {0} },
-       { "screenshot_wind",    legacyfunc,     {0} },
        { "float_toggle",       floating_toggle,{0} },
        { "version",            version,        {0} },
-       { "spawn_lock",         legacyfunc,     {0} },
-       { "spawn_initscr",      legacyfunc,     {0} },
        { "spawn_custom",       dummykeyfunc,   {0} },
        { "iconify",            iconify,        {0} },
        { "uniconify",          uniconify,      {0} },
@@ -4572,7 +4643,7 @@ struct key {
        enum keyfuncid          funcid;
        char                    *spawn_name;
 };
-RB_HEAD(key_list, key);
+RB_HEAD(key_tree, key);
 
 int
 key_cmp(struct key *kp1, struct key *kp2)
@@ -4590,8 +4661,8 @@ key_cmp(struct key *kp1, struct key *kp2)
        return (0);
 }
 
-RB_GENERATE_STATIC(key_list, key, entry, key_cmp);
-struct key_list                        keys;
+RB_GENERATE(key_tree, key, entry, key_cmp);
+struct key_tree                        keys;
 
 /* mouse */
 enum { client_click, root_click };
@@ -4615,7 +4686,7 @@ update_modkey(unsigned int mod)
        struct key              *kp;
 
        mod_key = mod;
-       RB_FOREACH(kp, key_list, &keys)
+       RB_FOREACH(kp, key_tree, &keys)
                if (kp->mod & ShiftMask)
                        kp->mod = mod | ShiftMask;
                else
@@ -4983,7 +5054,7 @@ key_insert(unsigned int mod, KeySym ks, enum keyfuncid kfid, char *spawn_name)
        kp->keysym = ks;
        kp->funcid = kfid;
        kp->spawn_name = strdupsafe(spawn_name);
-       RB_INSERT(key_list, &keys, kp);
+       RB_INSERT(key_tree, &keys, kp);
 
        DNPRINTF(SWM_D_KEY, "key_insert: leave\n");
 }
@@ -4996,7 +5067,7 @@ key_lookup(unsigned int mod, KeySym ks)
        kp.keysym = ks;
        kp.mod = mod;
 
-       return (RB_FIND(key_list, &keys, &kp));
+       return (RB_FIND(key_tree, &keys, &kp));
 }
 
 void
@@ -5004,7 +5075,7 @@ key_remove(struct key *kp)
 {
        DNPRINTF(SWM_D_KEY, "key_remove: %s\n", keyfuncs[kp->funcid].name);
 
-       RB_REMOVE(key_list, &keys, kp);
+       RB_REMOVE(key_tree, &keys, kp);
        free(kp->spawn_name);
        free(kp);
 
@@ -5115,7 +5186,7 @@ setup_keys(void)
        setkeybinding(MODKEY,           XK_k,           kf_focus_prev,  NULL);
        setkeybinding(MODKEY|ShiftMask, XK_j,           kf_swap_next,   NULL);
        setkeybinding(MODKEY|ShiftMask, XK_k,           kf_swap_prev,   NULL);
-       setkeybinding(MODKEY|ShiftMask, XK_Return,      kf_spawn_term,  NULL);
+       setkeybinding(MODKEY|ShiftMask, XK_Return,      kf_spawn_custom,"term");
        setkeybinding(MODKEY,           XK_p,           kf_spawn_custom,"menu");
        setkeybinding(MODKEY|ShiftMask, XK_q,           kf_quit,        NULL);
        setkeybinding(MODKEY,           XK_q,           kf_restart,     NULL);
@@ -5267,7 +5338,7 @@ grabkeys(void)
                if (TAILQ_EMPTY(&screens[k].rl))
                        continue;
                XUngrabKey(display, AnyKey, AnyModifier, screens[k].root);
-               RB_FOREACH(kp, key_list, &keys) {
+               RB_FOREACH(kp, key_tree, &keys) {
                        if ((code = XKeysymToKeycode(display, kp->keysym)))
                                for (j = 0; j < LENGTH(modifiers); j++)
                                        XGrabKey(display, code,
@@ -5463,18 +5534,39 @@ setup_quirks(void)
 #define SWM_CONF_FILE          "spectrwm.conf"
 #define SWM_CONF_FILE_OLD      "scrotwm.conf"
 
-enum   { SWM_S_BAR_DELAY, SWM_S_BAR_ENABLED, SWM_S_BAR_BORDER_WIDTH,
-         SWM_S_STACK_ENABLED, SWM_S_CLOCK_ENABLED, SWM_S_CLOCK_FORMAT,
-         SWM_S_CYCLE_EMPTY, SWM_S_CYCLE_VISIBLE, SWM_S_WORKSPACE_LIMIT,
-         SWM_S_SS_ENABLED, SWM_S_TERM_WIDTH, SWM_S_TITLE_CLASS_ENABLED,
-         SWM_S_TITLE_NAME_ENABLED, SWM_S_WINDOW_NAME_ENABLED,
-         SWM_S_URGENT_ENABLED, SWM_S_FOCUS_MODE, SWM_S_FOCUS_CLOSE,
-         SWM_S_FOCUS_CLOSE_WRAP, SWM_S_FOCUS_DEFAULT, SWM_S_SPAWN_ORDER,
-         SWM_S_DISABLE_BORDER, SWM_S_BORDER_WIDTH, SWM_S_BAR_FONT,
-         SWM_S_BAR_ACTION, SWM_S_SPAWN_TERM, SWM_S_SS_APP, SWM_S_DIALOG_RATIO,
-         SWM_S_BAR_AT_BOTTOM, SWM_S_VERBOSE_LAYOUT, SWM_S_BAR_JUSTIFY,
-         SWM_S_BAR_FORMAT
-       };
+enum {
+       SWM_S_BAR_ACTION,
+       SWM_S_BAR_AT_BOTTOM,
+       SWM_S_BAR_BORDER_WIDTH,
+       SWM_S_BAR_DELAY,
+       SWM_S_BAR_ENABLED,
+       SWM_S_BAR_FONT,
+       SWM_S_BAR_FORMAT,
+       SWM_S_BAR_JUSTIFY,
+       SWM_S_BORDER_WIDTH,
+       SWM_S_CLOCK_ENABLED,
+       SWM_S_CLOCK_FORMAT,
+       SWM_S_CYCLE_EMPTY,
+       SWM_S_CYCLE_VISIBLE,
+       SWM_S_DIALOG_RATIO,
+       SWM_S_DISABLE_BORDER,
+       SWM_S_FOCUS_CLOSE,
+       SWM_S_FOCUS_CLOSE_WRAP,
+       SWM_S_FOCUS_DEFAULT,
+       SWM_S_FOCUS_MODE,
+       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_TITLE_CLASS_ENABLED,
+       SWM_S_TITLE_NAME_ENABLED,
+       SWM_S_URGENT_ENABLED,
+       SWM_S_VERBOSE_LAYOUT,
+       SWM_S_WINDOW_NAME_ENABLED,
+       SWM_S_WORKSPACE_LIMIT
+};
 
 int
 setconfvalue(char *selector, char *value, int flags)
@@ -5483,17 +5575,37 @@ setconfvalue(char *selector, char *value, int flags)
        char    *b;
 
        switch (flags) {
+       case SWM_S_BAR_ACTION:
+               free(bar_argv[0]);
+               if ((bar_argv[0] = strdup(value)) == NULL)
+                       err(1, "setconfvalue: bar_action");
+               break;
+       case SWM_S_BAR_AT_BOTTOM:
+               bar_at_bottom = atoi(value);
+               break;
+       case SWM_S_BAR_BORDER_WIDTH:
+               bar_border_width = atoi(value);
+               if (bar_border_width < 0)
+                       bar_border_width = 0;
+               break;
        case SWM_S_BAR_DELAY:
                bar_delay = atoi(value);
                break;
        case SWM_S_BAR_ENABLED:
                bar_enabled = atoi(value);
                break;
-       case SWM_S_BAR_BORDER_WIDTH:
-               bar_border_width = atoi(value);
+       case SWM_S_BAR_FONT:
+               b = bar_fonts;
+               if (asprintf(&bar_fonts, "%s,%s", value, bar_fonts) == -1)
+                       err(1, "setconfvalue: asprintf: failed to allocate "
+                               "memory for bar_fonts.");
+
+               free(b);
                break;
-       case SWM_S_BAR_AT_BOTTOM:
-               bar_at_bottom = atoi(value);
+       case SWM_S_BAR_FORMAT:
+               free(bar_format);
+               if ((bar_format = strdup(value)) == NULL)
+                       err(1, "setconfvalue: bar_format");
                break;
        case SWM_S_BAR_JUSTIFY:
                if (!strcmp(value, "left"))
@@ -5505,13 +5617,10 @@ setconfvalue(char *selector, char *value, int flags)
                else
                        errx(1, "invalid bar_justify");
                break;
-       case SWM_S_BAR_FORMAT:
-               free(bar_format);
-               if ((bar_format = strdup(value)) == NULL)
-                       err(1, "setconfvalue: bar_format");
-               break;
-       case SWM_S_STACK_ENABLED:
-               stack_enabled = atoi(value);
+       case SWM_S_BORDER_WIDTH:
+               border_width = atoi(value);
+               if (border_width < 0)
+                       border_width = 0;
                break;
        case SWM_S_CLOCK_ENABLED:
                clock_enabled = atoi(value);
@@ -5529,40 +5638,13 @@ setconfvalue(char *selector, char *value, int flags)
        case SWM_S_CYCLE_VISIBLE:
                cycle_visible = atoi(value);
                break;
-       case SWM_S_WORKSPACE_LIMIT:
-               workspace_limit = atoi(value);
-               if (workspace_limit > SWM_WS_MAX)
-                       workspace_limit = SWM_WS_MAX;
-               else if (workspace_limit < 1)
-                       workspace_limit = 1;
-               break;
-       case SWM_S_SS_ENABLED:
-               ss_enabled = atoi(value);
-               break;
-       case SWM_S_TERM_WIDTH:
-               term_width = atoi(value);
-               break;
-       case SWM_S_TITLE_CLASS_ENABLED:
-               title_class_enabled = atoi(value);
-               break;
-       case SWM_S_WINDOW_NAME_ENABLED:
-               window_name_enabled = atoi(value);
-               break;
-       case SWM_S_TITLE_NAME_ENABLED:
-               title_name_enabled = atoi(value);
-               break;
-       case SWM_S_URGENT_ENABLED:
-               urgent_enabled = atoi(value);
+       case SWM_S_DIALOG_RATIO:
+               dialog_ratio = atof(value);
+               if (dialog_ratio > 1.0 || dialog_ratio <= .3)
+                       dialog_ratio = .6;
                break;
-       case SWM_S_FOCUS_MODE:
-               if (!strcmp(value, "default"))
-                       focus_mode = SWM_FOCUS_DEFAULT;
-               else if (!strcmp(value, "follow_cursor"))
-                       focus_mode = SWM_FOCUS_FOLLOW;
-               else if (!strcmp(value, "synergy"))
-                       focus_mode = SWM_FOCUS_SYNERGY;
-               else
-                       errx(1, "focus_mode");
+       case SWM_S_DISABLE_BORDER:
+               disable_border = atoi(value);
                break;
        case SWM_S_FOCUS_CLOSE:
                if (!strcmp(value, "first"))
@@ -5587,6 +5669,16 @@ setconfvalue(char *selector, char *value, int flags)
                else
                        errx(1, "focus_default");
                break;
+       case SWM_S_FOCUS_MODE:
+               if (!strcmp(value, "default"))
+                       focus_mode = SWM_FOCUS_DEFAULT;
+               else if (!strcmp(value, "follow_cursor"))
+                       focus_mode = SWM_FOCUS_FOLLOW;
+               else if (!strcmp(value, "synergy"))
+                       focus_mode = SWM_FOCUS_SYNERGY;
+               else
+                       errx(1, "focus_mode");
+               break;
        case SWM_S_SPAWN_ORDER:
                if (!strcmp(value, "first"))
                        spawn_position = SWM_STACK_BOTTOM;
@@ -5599,36 +5691,30 @@ setconfvalue(char *selector, char *value, int flags)
                else
                        errx(1, "spawn_position");
                break;
-       case SWM_S_DISABLE_BORDER:
-               disable_border = atoi(value);
+       case SWM_S_SPAWN_TERM:
+               setconfspawn("term", value, 0);
                break;
-       case SWM_S_BORDER_WIDTH:
-               border_width = atoi(value);
+       case SWM_S_SS_APP:
                break;
-       case SWM_S_BAR_FONT:
-               b = bar_fonts;
-               if (asprintf(&bar_fonts, "%s,%s", value, bar_fonts) == -1)
-                       err(1, "setconfvalue: asprintf: failed to allocate "
-                               "memory for bar_fonts.");
-
-               free(b);
+       case SWM_S_SS_ENABLED:
+               ss_enabled = atoi(value);
                break;
-       case SWM_S_BAR_ACTION:
-               free(bar_argv[0]);
-               if ((bar_argv[0] = strdup(value)) == NULL)
-                       err(1, "setconfvalue: bar_action");
+       case SWM_S_STACK_ENABLED:
+               stack_enabled = atoi(value);
                break;
-       case SWM_S_SPAWN_TERM:
-               free(spawn_term[0]);
-               if ((spawn_term[0] = strdup(value)) == NULL)
-                       err(1, "setconfvalue: spawn_term");
+       case SWM_S_TERM_WIDTH:
+               term_width = atoi(value);
+               if (term_width < 0)
+                       term_width = 0;
                break;
-       case SWM_S_SS_APP:
+       case SWM_S_TITLE_CLASS_ENABLED:
+               title_class_enabled = atoi(value);
                break;
-       case SWM_S_DIALOG_RATIO:
-               dialog_ratio = atof(value);
-               if (dialog_ratio > 1.0 || dialog_ratio <= .3)
-                       dialog_ratio = .6;
+       case SWM_S_TITLE_NAME_ENABLED:
+               title_name_enabled = atoi(value);
+               break;
+       case SWM_S_URGENT_ENABLED:
+               urgent_enabled = atoi(value);
                break;
        case SWM_S_VERBOSE_LAYOUT:
                verbose_layout = atoi(value);
@@ -5639,6 +5725,16 @@ setconfvalue(char *selector, char *value, int flags)
                                layouts[i].l_string = plain_stacker;
                }
                break;
+       case SWM_S_WINDOW_NAME_ENABLED:
+               window_name_enabled = atoi(value);
+               break;
+       case SWM_S_WORKSPACE_LIMIT:
+               workspace_limit = atoi(value);
+               if (workspace_limit > SWM_WS_MAX)
+                       workspace_limit = SWM_WS_MAX;
+               else if (workspace_limit < 1)
+                       workspace_limit = 1;
+               break;
        default:
                return (1);
        }
@@ -5743,7 +5839,7 @@ setautorun(char *selector, char *value, int flags)
 int
 setlayout(char *selector, char *value, int flags)
 {
-       int                     ws_id, i, x, mg, ma, si, raise;
+       int                     ws_id, i, x, mg, ma, si, raise, f = 0;
        int                     st = SWM_V_STACK;
        char                    s[1024];
        struct workspace        *ws;
@@ -5763,9 +5859,15 @@ setlayout(char *selector, char *value, int flags)
 
        if (!strcasecmp(s, "vertical"))
                st = SWM_V_STACK;
-       else if (!strcasecmp(s, "horizontal"))
+       else if (!strcasecmp(s, "vertical_flip")) {
+               st = SWM_V_STACK;
+               f = 1;
+       } else if (!strcasecmp(s, "horizontal"))
                st = SWM_H_STACK;
-       else if (!strcasecmp(s, "fullscreen"))
+       else if (!strcasecmp(s, "horizontal_flip")) {
+               st = SWM_H_STACK;
+               f = 1;
+       } else if (!strcasecmp(s, "fullscreen"))
                st = SWM_MAX_STACK;
        else
                errx(1, "invalid layout entry, should be 'ws[<idx>]:"
@@ -5801,6 +5903,12 @@ setlayout(char *selector, char *value, int flags)
                            SWM_ARG_ID_STACKDEC);
                        stack();
                }
+               /* Apply flip */
+               if (f) {
+                       ws[ws_id].cur_layout->l_config(&ws[ws_id],
+                           SWM_ARG_ID_FLIPLAYOUT);
+                       stack();
+               }
        }
 
        return (0);
@@ -6828,7 +6936,7 @@ visibilitynotify(XEvent *e)
        if (e->xvisibility.state == VisibilityUnobscured)
                for (i = 0; i < ScreenCount(display); i++)
                        TAILQ_FOREACH(r, &screens[i].rl, entry)
-                               if (e->xvisibility.window == r->bar_window)
+                               if (e->xvisibility.window == WINID(r->bar))
                                        bar_update();
 }
 
@@ -6944,7 +7052,7 @@ new_region(struct swm_screen *s, int x, int y, int w, int h)
                        if (r->ws->r != NULL)
                                r->ws->old_r = r->ws->r;
                        r->ws->r = NULL;
-                       XDestroyWindow(display, r->bar_window);
+                       bar_cleanup(r);
                        TAILQ_REMOVE(&s->rl, r, entry);
                        TAILQ_INSERT_TAIL(&s->orl, r, entry);
                }
@@ -7015,7 +7123,7 @@ scan_xrandr(int i)
        /* remove any old regions */
        while ((r = TAILQ_FIRST(&screens[i].rl)) != NULL) {
                r->ws->old_r = r->ws->r = NULL;
-               XDestroyWindow(display, r->bar_window);
+               bar_cleanup(r);
                TAILQ_REMOVE(&screens[i].rl, r, entry);
                TAILQ_INSERT_TAIL(&screens[i].orl, r, entry);
        }
@@ -7166,12 +7274,10 @@ setup_screens(void)
                setscreencolor("black", i + 1, SWM_S_COLOR_BAR);
                setscreencolor("rgb:a0/a0/a0", i + 1, SWM_S_COLOR_BAR_FONT);
 
-               /* create graphics context on screen with default font color */
-               screens[i].bar_gc = XCreateGC(display, screens[i].root, 0,
-                   &gcv);
-
-               XSetForeground(display, screens[i].bar_gc,
-                   screens[i].c[SWM_S_COLOR_BAR_FONT].color);
+               /* create graphics context on screen */
+               gcv.graphics_exposures = 0;
+               screens[i].bar_gc = XCreateGC(display, screens[i].root,
+                   GCGraphicsExposures, &gcv);
 
                /* set default cursor */
                XDefineCursor(display, screens[i].root,
@@ -7211,9 +7317,6 @@ setup_globals(void)
        if ((bar_fonts = strdup(SWM_BAR_FONTS)) == NULL)
                err(1, "setup_globals: strdup: failed to allocate memory.");
 
-       if ((spawn_term[0] = strdup("xterm")) == NULL)
-               err(1, "setup_globals: strdup: failed to allocate memory.");
-
        if ((clock_format = strdup("%a %b %d %R %Z %Y")) == NULL)
                err(1, "setup_globals: strdup: failed to allocate memory.");
 }
@@ -7344,11 +7447,9 @@ main(int argc, char *argv[])
        }
 noconfig:
 
-       /* load conf (if any) and refresh font color in bar graphics contexts */
-       if (cfile && conf_load(cfile, SWM_CONF_DEFAULT) == 0)
-               for (i = 0; i < ScreenCount(display); ++i)
-                       XSetForeground(display, screens[i].bar_gc,
-                           screens[i].c[SWM_S_COLOR_BAR_FONT].color);
+       /* load conf (if any) */
+       if (cfile)
+               conf_load(cfile, SWM_CONF_DEFAULT);
 
        setup_ewmh();
        /* set some values to work around bad programs */