JasonWoof Got questions, comments, patches, etc.? Contact Jason Woofenden
Fix issue where _NET_ACTIVE_WINDOW is ignored on unmanaged windows.
[spectrwm.git] / spectrwm.c
index 436e84d..5b97af2 100644 (file)
@@ -173,6 +173,9 @@ u_int32_t           swm_debug = 0
 #define Y(r)                   (r)->g.y
 #define WIDTH(r)               (r)->g.w
 #define HEIGHT(r)              (r)->g.h
+#define BORDER(w)              (w->bordered ? border_width : 0)
+#define MAX_X(r)               ((r)->g.x + (r)->g.w)
+#define MAX_Y(r)               ((r)->g.y + (r)->g.h)
 #define SH_MIN(w)              (w)->sh_mask & PMinSize
 #define SH_MIN_W(w)            (w)->sh.min_width
 #define SH_MIN_H(w)            (w)->sh.min_height
@@ -312,7 +315,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 +334,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 +347,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);
 
@@ -349,13 +357,13 @@ struct ws_win {
        Window                  transient;
        struct ws_win           *child_trans;   /* transient child window */
        struct swm_geometry     g;              /* current geometry */
-       struct swm_geometry     g_float;        /* geometry when floating */
-       struct swm_geometry     rg_float;       /* region geom when floating */
+       struct swm_geometry     g_float;        /* region coordinates */
        int                     g_floatvalid;   /* g_float geometry validity */
        int                     floatmaxed;     /* whether maxed by max_stack */
        int                     floating;
        int                     manual;
        int                     iconic;
+       int                     bordered;
        unsigned int            ewmh_flags;
        int                     font_size_boundary[SWM_MAX_FONT_STEPS];
        int                     font_steps;
@@ -453,8 +461,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 +557,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,33 +591,35 @@ 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 *);
 int             floating_toggle_win(struct ws_win *);
+void            constrain_window(struct ws_win *, struct swm_region *, int);
+void            update_window(struct ws_win *);
 void            spawn_select(struct swm_region *, union arg *, char *, int *);
 unsigned char  *get_win_name(Window);
 
@@ -759,8 +791,6 @@ ewmh_autoquirk(struct ws_win *win)
 int
 ewmh_set_win_fullscreen(struct ws_win *win, int fs)
 {
-       struct swm_geometry     rg;
-
        if (!win->ws->r)
                return (0);
 
@@ -770,19 +800,18 @@ ewmh_set_win_fullscreen(struct ws_win *win, int fs)
        DNPRINTF(SWM_D_MISC, "ewmh_set_win_fullscreen: window: 0x%lx, "
            "fullscreen %s\n", win->id, YESNO(fs));
 
-       rg = win->ws->r->g;
-
        if (fs) {
-               store_float_geom(win, win->ws->r);
+               if (!win->g_floatvalid)
+                       store_float_geom(win, win->ws->r);
 
-               win->g = rg;
+               win->g = win->ws->r->g;
+               win->bordered = 0;
        } else {
                if (win->g_floatvalid) {
                        /* refloat at last floating relative position */
-                       X(win) = win->g_float.x - win->rg_float.x + rg.x;
-                       Y(win) = win->g_float.y - win->rg_float.y + rg.y;
-                       WIDTH(win) = win->g_float.w;
-                       HEIGHT(win) = win->g_float.h;
+                       win->g = win->g_float;
+                       X(win) += X(win->ws->r);
+                       Y(win) += Y(win->ws->r);
                }
        }
 
@@ -1310,8 +1339,6 @@ bar_print(struct swm_region *r, const char *s)
        size_t                  len;
        XRectangle              ibox, lbox;
 
-       XClearWindow(display, r->bar_window);
-
        len = strlen(s);
        XmbTextExtents(bar_fs, s, len, &ibox, &lbox);
 
@@ -1330,9 +1357,21 @@ bar_print(struct swm_region *r, const 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 +1408,17 @@ 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)
@@ -1454,21 +1504,21 @@ bar_fmt(const 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) {
                strlcat(fmtnew, "+C", sz);
                if (title_name_enabled == 0)
-                       strlcat(fmtnew, "    ", sz);
+                       strlcat(fmtnew, "+4<", sz);
        }
 
        /* checks needed by the colon and floating strlcat(3) calls below */
@@ -1476,7 +1526,7 @@ bar_fmt(const char *fmtexp, char *fmtnew, struct swm_region *r, size_t sz)
                if (title_name_enabled) {
                        if (title_class_enabled)
                                strlcat(fmtnew, ":", sz);
-                       strlcat(fmtnew, "+T    ", sz);
+                       strlcat(fmtnew, "+T+4<", sz);
                }
                if (window_name_enabled) {
                        if (r->ws->focus->floating)
@@ -1486,7 +1536,16 @@ bar_fmt(const char *fmtexp, char *fmtnew, struct swm_region *r, size_t sz)
        }
 
        /* finally add the action script output and the version */
-       strlcat(fmtnew, "    +A    +V", sz);
+       strlcat(fmtnew, "+4<+A+4<+V", sz);
+}
+
+void
+bar_replace_pad(char *tmp, int *limit, size_t sz)
+{
+       /* special case; no limit given, pad one space, instead */
+       if (*limit == sz - 1)
+               *limit = 1;
+       snprintf(tmp, sz, "%*s", *limit, " ");
 }
 
 /* replaces the bar format character sequences (like in tmux(1)) */
@@ -1495,35 +1554,30 @@ bar_replace_seq(char *fmt, char *fmtrep, struct swm_region *r, size_t *offrep,
     size_t sz)
 {
        char                    *ptr;
-       char                    num[8], tmp[SWM_BAR_MAX];
-       int                     limit;
-       size_t                  len, numoff = 0;
+       char                    tmp[SWM_BAR_MAX];
+       int                     limit, size;
+       size_t                  len;
 
        /* reset strlcat(3) buffer */
        *tmp = '\0';
 
        /* get number, if any */
        fmt++;
-       while (*fmt != '\0' && isdigit((unsigned char) *fmt)) {
-               if (numoff >= sizeof num - 1)
-                       break;
-               num[numoff++] = *fmt++;
-       }
-       num[numoff] = '\0';
-
-       if ((limit = strtonum(num, 1, sizeof tmp - 1, NULL)) == 0)
+       size = 0;
+       if (sscanf(fmt, "%d%n", &limit, &size) != 1)
+               limit = sizeof tmp - 1;
+       if (limit <= 0 || limit >= sizeof tmp)
                limit = sizeof tmp - 1;
 
-       /* if number is too big, skip to the first non-digit */
-       if (numoff >= sizeof num - 1) {
-               while (*fmt != '\0' && isdigit((unsigned char) *fmt))
-                       fmt++;
-       }
        /* there is nothing to replace (ie EOL) */
+       fmt += size;
        if (*fmt == '\0')
                return (fmt);
 
        switch (*fmt) {
+       case '<':
+               bar_replace_pad(tmp, &limit, sizeof tmp);
+               break;
        case 'A':
                snprintf(tmp, sizeof tmp, "%s", bar_ext);
                break;
@@ -1542,6 +1596,9 @@ bar_replace_seq(char *fmt, char *fmtrep, struct swm_region *r, size_t *offrep,
        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;
@@ -1647,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);
@@ -1694,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;
 
@@ -1749,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();
@@ -1765,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);
 
@@ -1804,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");
@@ -1823,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;
@@ -1831,7 +1917,7 @@ drain_enter_notify(void)
        while (XCheckMaskEvent(display, EnterWindowMask, &cne))
                i++;
 
-       DNPRINTF(SWM_D_MISC, "drain_enter_notify: drained: %d\n", i);
+       DNPRINTF(SWM_D_EVENT, "drain_enter_notify: drained: %d\n", i);
 }
 
 void
@@ -1915,7 +2001,7 @@ config_win(struct ws_win *win, XConfigureRequestEvent  *ev)
                ce.display = display;
                ce.event = win->id;
                ce.window = win->id;
-               ce.border_width = border_width;
+               ce.border_width = BORDER(win);
                ce.above = None;
        } else {
                /* normal */
@@ -1960,8 +2046,8 @@ config_win(struct ws_win *win, XConfigureRequestEvent  *ev)
                }
 
                /* adjust x and y for requested border_width. */
-               ce.x += border_width - ev->border_width;
-               ce.y += border_width - ev->border_width;
+               ce.x += BORDER(win) - ev->border_width;
+               ce.y += BORDER(win) - ev->border_width;
                ce.border_width = ev->border_width;
                ce.above = ev->above;
        }
@@ -2087,16 +2173,20 @@ root_to_region(Window root)
        int                     i, x, y, wx, wy;
        unsigned int            mask;
 
+       DNPRINTF(SWM_D_MISC, "root_to_region: window: 0x%lx\n", root);
+
        for (i = 0; i < ScreenCount(display); i++)
                if (screens[i].root == root)
                        break;
 
        if (XQueryPointer(display, screens[i].root,
            &rr, &cr, &x, &y, &wx, &wy, &mask) != False) {
+               DNPRINTF(SWM_D_MISC, "root_to_region: pointer: (%d,%d)\n",
+                   x, y);
                /* choose a region based on pointer location */
                TAILQ_FOREACH(r, &screens[i].rl, entry)
-                       if (x >= X(r) && x <= X(r) + WIDTH(r) &&
-                           y >= Y(r) && y <= Y(r) + HEIGHT(r))
+                       if (X(r) <= x && x < MAX_X(r) &&
+                           Y(r) <= y && y < MAX_Y(r))
                                break;
        }
 
@@ -2212,18 +2302,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;
@@ -2944,46 +3022,65 @@ store_float_geom(struct ws_win *win, struct swm_region *r)
 {
        /* retain window geom and region geom */
        win->g_float = win->g;
-       win->rg_float = r->g;
+       win->g_float.x -= X(r);
+       win->g_float.y -= Y(r);
        win->g_floatvalid = 1;
+       DNPRINTF(SWM_D_MISC, "store_float_geom: window: 0x%lx, g: (%d,%d)"
+           " %d x %d, g_float: (%d,%d) %d x %d\n", win->id, X(win), Y(win),
+           WIDTH(win), HEIGHT(win), win->g_float.x, win->g_float.y,
+           win->g_float.w, win->g_float.h);
 }
 
 void
 stack_floater(struct ws_win *win, struct swm_region *r)
 {
-       unsigned int            mask;
-       XWindowChanges          wc;
-
        if (win == NULL)
                return;
 
-       bzero(&wc, sizeof wc);
-       mask = CWX | CWY | CWBorderWidth | CWWidth | CWHeight;
+       DNPRINTF(SWM_D_MISC, "stack_floater: window: 0x%lx\n", win->id);
 
        /*
         * to allow windows to change their size (e.g. mplayer fs) only retrieve
         * geom on ws switches or return from max mode
         */
-       if (win->floatmaxed || (r != r->ws->old_r && win->g_floatvalid
-           && !(win->ewmh_flags & EWMH_F_FULLSCREEN))) {
-               /*
-                * use stored g and rg to set relative position and size
-                * as in old region or before max stack mode
-                */
-               X(win) = win->g_float.x - win->rg_float.x + X(r);
-               Y(win) = win->g_float.y - win->rg_float.y + Y(r);
-               WIDTH(win) = win->g_float.w;
-               HEIGHT(win) = win->g_float.h;
-               win->g_floatvalid = 0;
+       if (win->g_floatvalid && (win->floatmaxed || (r != r->ws->old_r &&
+           !(win->ewmh_flags & EWMH_F_FULLSCREEN)))) {
+               /* refloat at last floating relative position */
+               win->g = win->g_float;
+               X(win) += X(r);
+               Y(win) += Y(r);
        }
 
        win->floatmaxed = 0;
 
-       if ((win->quirks & SWM_Q_FULLSCREEN) && (WIDTH(win) >= WIDTH(r)) &&
-           (HEIGHT(win) >= HEIGHT(r)))
-               wc.border_width = 0;
-       else
-               wc.border_width = border_width;
+       /*
+        * if set to fullscreen mode, configure window to maximum size.
+        */
+       if (win->ewmh_flags & EWMH_F_FULLSCREEN) {
+               if (!win->g_floatvalid)
+                       store_float_geom(win, win->ws->r);
+
+               win->g = win->ws->r->g;
+       }
+
+       /*
+        * remove border on fullscreen floater when in fullscreen mode or when
+        * the quirk is present.
+        */
+       if ((win->ewmh_flags & EWMH_F_FULLSCREEN) ||
+           ((win->quirks & SWM_Q_FULLSCREEN) &&
+            (WIDTH(win) >= WIDTH(r)) && (HEIGHT(win) >= HEIGHT(r)))) {
+               if (win->bordered) {
+                       win->bordered = 0;
+                       X(win) += border_width;
+                       Y(win) += border_width;
+               }
+       } else if (!win->bordered) {
+               win->bordered = 1;
+               X(win) -= border_width;
+               Y(win) -= border_width;
+       }
+
        if (win->transient && (win->quirks & SWM_Q_TRANSSZ)) {
                WIDTH(win) = (double)WIDTH(r) * dialog_ratio;
                HEIGHT(win) = (double)HEIGHT(r) * dialog_ratio;
@@ -2994,39 +3091,14 @@ stack_floater(struct ws_win *win, struct swm_region *r)
                 * floaters and transients are auto-centred unless moved
                 * or resized
                 */
-               X(win) = X(r) + (WIDTH(r) - WIDTH(win)) /  2 - wc.border_width;
-               Y(win) = Y(r) + (HEIGHT(r) - HEIGHT(win)) / 2 - wc.border_width;
-       }
-
-       /* win can be outside r if new r smaller than old r */
-       /* Ensure top left corner inside r (move probs otherwise) */
-       if (X(win) < X(r) - wc.border_width)
-               X(win) = X(r) - wc.border_width;
-       if (X(win) > X(r) + WIDTH(r) - 1)
-               X(win) = (WIDTH(win) > WIDTH(r)) ? X(r) :
-                   (X(r) + WIDTH(r) - WIDTH(win) - 2 * wc.border_width);
-       if (Y(win) < Y(r) - wc.border_width)
-               Y(win) = Y(r) - wc.border_width;
-       if (Y(win) > Y(r) + HEIGHT(r) - 1)
-               Y(win) = (HEIGHT(win) > HEIGHT(r)) ? Y(r) :
-                   (Y(r) + HEIGHT(r) - HEIGHT(win) - 2 * wc.border_width);
-
-       wc.x = X(win);
-       wc.y = Y(win);
-       wc.width = WIDTH(win);
-       wc.height = HEIGHT(win);
-
-       /*
-        * Retain floater and transient geometry for correct positioning
-        * when ws changes region
-        */
-       if (!(win->ewmh_flags & EWMH_F_FULLSCREEN))
-               store_float_geom(win, r);
+               X(win) = X(r) + (WIDTH(r) - WIDTH(win)) /  2 - BORDER(win);
+               Y(win) = Y(r) + (HEIGHT(r) - HEIGHT(win)) / 2 - BORDER(win);
+       }
 
-       DNPRINTF(SWM_D_MISC, "stack_floater: window: %lu, (x,y) w x h: (%d,%d) "
-           "%d x %d\n", win->id, wc.x, wc.y, wc.width, wc.height);
+       /* keep window within region bounds */
+       constrain_window(win, r, 0);
 
-       XConfigureWindow(display, win->id, mask, &wc);
+       update_window(win);
 }
 
 /*
@@ -3066,16 +3138,15 @@ adjust_font(struct ws_win *win)
 void
 stack_master(struct workspace *ws, struct swm_geometry *g, int rot, int flip)
 {
-       XWindowChanges          wc;
        XWindowAttributes       wa;
        struct swm_geometry     win_g, r_g = *g;
-       struct ws_win           *win, *fs_win = 0;
+       struct ws_win           *win, *fs_win = NULL;
        int                     i, j, s, stacks;
        int                     w_inc = 1, h_inc, w_base = 1, h_base;
        int                     hrh, extra = 0, h_slice, last_h = 0;
        int                     split, colno, winno, mwin, msize, mscale;
        int                     remain, missing, v_slice, reconfigure;
-       unsigned int            mask;
+       int                     bordered = 1;
 
        DNPRINTF(SWM_D_STACK, "stack_master: workspace: %d, rot: %s, "
            "flip: %s\n", ws->idx, YESNO(rot), YESNO(flip));
@@ -3201,37 +3272,41 @@ stack_master(struct workspace *ws, struct swm_geometry *g, int rot, int flip)
                else
                        win_g.y += last_h + 2 * border_width;
 
-               bzero(&wc, sizeof wc);
                if (disable_border && bar_enabled == 0 && winno == 1){
-                       wc.border_width = 0;
+                       bordered = 0;
                        win_g.w += 2 * border_width;
                        win_g.h += 2 * border_width;
-               } else
-                       wc.border_width = border_width;
-               reconfigure = 0;
+               } 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) {
                                reconfigure = 1;
-                               X(win) = wc.x = win_g.y;
-                               Y(win) = wc.y = win_g.x;
-                               WIDTH(win) = wc.width = win_g.h;
-                               HEIGHT(win) = wc.height = win_g.w;
+                               X(win) = win_g.y;
+                               Y(win) = win_g.x;
+                               WIDTH(win) = win_g.h;
+                               HEIGHT(win) = win_g.w;
                        }
                } else {
                        if (X(win) != win_g.x || Y(win) != win_g.y ||
                            WIDTH(win) != win_g.w || HEIGHT(win) != win_g.h) {
                                reconfigure = 1;
-                               X(win) = wc.x = win_g.x;
-                               Y(win) = wc.y = win_g.y;
-                               WIDTH(win) = wc.width = win_g.w;
-                               HEIGHT(win) = wc.height = win_g.h;
+                               X(win) = win_g.x;
+                               Y(win) = win_g.y;
+                               WIDTH(win) = win_g.w;
+                               HEIGHT(win) = win_g.h;
                        }
                }
+
+               if (bordered != win->bordered) {
+                       reconfigure = 1;
+                       win->bordered = bordered;
+               }
+
                if (reconfigure) {
                        adjust_font(win);
-                       mask = CWX | CWY | CWWidth | CWHeight | CWBorderWidth;
-                       XConfigureWindow(display, win->id, mask, &wc);
+                       update_window(win);
                }
 
                if (XGetWindowAttributes(display, win->id, &wa))
@@ -3370,10 +3445,8 @@ horizontal_stack(struct workspace *ws, struct swm_geometry *g)
 void
 max_stack(struct workspace *ws, struct swm_geometry *g)
 {
-       XWindowChanges          wc;
        struct swm_geometry     gg = *g;
        struct ws_win           *win, *wintrans = NULL, *parent = NULL;
-       unsigned int            mask;
        int                     winno;
 
        DNPRINTF(SWM_D_STACK, "max_stack: workspace: %d\n", ws->idx);
@@ -3404,21 +3477,16 @@ max_stack(struct workspace *ws, struct swm_geometry *g)
                /* only reconfigure if necessary */
                if (X(win) != gg.x || Y(win) != gg.y || WIDTH(win) != gg.w ||
                    HEIGHT(win) != gg.h) {
-                       bzero(&wc, sizeof wc);
-                       X(win) = wc.x = gg.x;
-                       Y(win) = wc.y = gg.y;
+                       win->g = gg;
                        if (bar_enabled){
-                               wc.border_width = border_width;
-                               WIDTH(win) = wc.width = gg.w;
-                               HEIGHT(win) = wc.height = gg.h;
+                               win->bordered = 1;
                        } else {
-                               wc.border_width = 0;
-                               WIDTH(win) = wc.width = gg.w + 2 * border_width;
-                               HEIGHT(win) = wc.height = gg.h +
-                                   2 * border_width;
+                               win->bordered = 0;
+                               WIDTH(win) += 2 * border_width;
+                               HEIGHT(win) += 2 * border_width;
                        }
-                       mask = CWX | CWY | CWWidth | CWHeight | CWBorderWidth;
-                       XConfigureWindow(display, win->id, mask, &wc);
+
+                       update_window(win);
                }
                /* unmap only if we don't have multi screen */
                if (win != ws->focus)
@@ -3959,8 +4027,8 @@ floating_toggle_win(struct ws_win *win)
        } else {
                if (win->g_floatvalid) {
                        /* refloat at last floating relative position */
-                       X(win) = win->g_float.x - win->rg_float.x + X(r);
-                       Y(win) = win->g_float.y - win->rg_float.y + Y(r);
+                       X(win) = win->g_float.x + X(r);
+                       Y(win) = win->g_float.y + Y(r);
                        WIDTH(win) = win->g_float.w;
                        HEIGHT(win) = win->g_float.h;
                }
@@ -3997,38 +4065,40 @@ floating_toggle(struct swm_region *r, union arg *args)
 void
 constrain_window(struct ws_win *win, struct swm_region *r, int resizable)
 {
-       if (X(win) + WIDTH(win) > X(r) + WIDTH(r) - border_width) {
+       if (MAX_X(win) + BORDER(win) > MAX_X(r)) {
                if (resizable)
-                       WIDTH(win) = X(r) + WIDTH(r) - X(win) - border_width;
+                       WIDTH(win) = MAX_X(r) - X(win) - BORDER(win);
                else
-                       X(win) = X(r) + WIDTH(r) - WIDTH(win) - border_width;
+                       X(win) = MAX_X(r)- WIDTH(win) - BORDER(win);
        }
 
-       if (X(win) < X(r) - border_width) {
+       if (X(win) + BORDER(win) < X(r)) {
                if (resizable)
-                       WIDTH(win) -= X(r) - X(win) - border_width;
+                       WIDTH(win) -= X(r) - X(win) - BORDER(win);
 
-               X(win) = X(r) - border_width;
+               X(win) = X(r) - BORDER(win);
        }
 
-       if (Y(win) + HEIGHT(win) > Y(r) + HEIGHT(r) - border_width) {
+       if (MAX_Y(win) + BORDER(win) > MAX_Y(r)) {
                if (resizable)
-                       HEIGHT(win) = Y(r) + HEIGHT(r) - Y(win) - border_width;
+                       HEIGHT(win) = MAX_Y(r) - Y(win) - BORDER(win);
                else
-                       Y(win) = Y(r) + HEIGHT(r) - HEIGHT(win) - border_width;
+                       Y(win) = MAX_Y(r) - HEIGHT(win) - BORDER(win);
        }
 
-       if (Y(win) < Y(r) - border_width) {
+       if (Y(win) + BORDER(win) < Y(r)) {
                if (resizable)
-                       HEIGHT(win) -= Y(r) - Y(win) - border_width;
+                       HEIGHT(win) -= Y(r) - Y(win) - BORDER(win);
 
-               Y(win) = Y(r) - border_width;
+               Y(win) = Y(r) - BORDER(win);
        }
 
-       if (WIDTH(win) < 1)
-               WIDTH(win) = 1;
-       if (HEIGHT(win) < 1)
-               HEIGHT(win) = 1;
+       if (resizable) {
+               if (WIDTH(win) < 1)
+                       WIDTH(win) = 1;
+               if (HEIGHT(win) < 1)
+                       HEIGHT(win) = 1;
+       }
 }
 
 void
@@ -4039,14 +4109,16 @@ update_window(struct ws_win *win)
 
        bzero(&wc, sizeof wc);
        mask = CWBorderWidth | CWWidth | CWHeight | CWX | CWY;
-       wc.border_width = border_width;
+
+       wc.border_width = BORDER(win);
        wc.x = X(win);
        wc.y = Y(win);
        wc.width = WIDTH(win);
        wc.height = HEIGHT(win);
 
-       DNPRINTF(SWM_D_MISC, "update_window: window: 0x%lx, (x,y) w x h: "
-           "(%d,%d) %d x %d\n", win->id, wc.x, wc.y, wc.width, wc.height);
+       DNPRINTF(SWM_D_EVENT, "update_window: window: 0x%lx, (x,y) w x h: "
+           "(%d,%d) %d x %d, bordered: %s\n", win->id, wc.x, wc.y, wc.width,
+           wc.height, YESNO(win->bordered));
 
        XConfigureWindow(display, win->id, mask, &wc);
 }
@@ -4073,6 +4145,9 @@ resize(struct ws_win *win, union arg *args)
                return;
        r = win->ws->r;
 
+       if (win->ewmh_flags & EWMH_F_FULLSCREEN)
+               return;
+
        DNPRINTF(SWM_D_MOUSE, "resize: window: 0x%lx, floating: %s, "
            "transient: 0x%lx\n", win->id, YESNO(win->floating),
            win->transient);
@@ -4256,6 +4331,9 @@ move(struct ws_win *win, union arg *args)
                return;
        r = win->ws->r;
 
+       if (win->ewmh_flags & EWMH_F_FULLSCREEN)
+               return;
+
        DNPRINTF(SWM_D_MOUSE, "move: window: 0x%lx, floating: %s, transient: "
            "0x%lx\n", win->id, YESNO(win->floating), win->transient);
 
@@ -4265,7 +4343,7 @@ move(struct ws_win *win, union arg *args)
 
        win->manual = 1;
        if (win->floating == 0 && !win->transient) {
-               store_float_geom(win,r);
+               store_float_geom(win, r);
                ewmh_update_win_state(win, ewmh[_NET_WM_STATE_ABOVE].atom,
                    _NET_WM_STATE_ADD);
        }
@@ -4339,7 +4417,7 @@ move(struct ws_win *win, union arg *args)
                XSync(display, False);
                update_window(win);
        }
-       store_float_geom(win,r);
+       store_float_geom(win, r);
        XUngrabPointer(display, CurrentTime);
 
        /* drain events */
@@ -4379,8 +4457,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,
@@ -4438,12 +4514,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,
@@ -4470,11 +4542,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 *);
@@ -4495,8 +4562,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} },
@@ -4554,12 +4619,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} },
@@ -4894,6 +4955,7 @@ void
 setup_spawn(void)
 {
        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);
@@ -5129,7 +5191,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);
@@ -5477,18 +5539,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)
@@ -5497,17 +5580,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"))
@@ -5519,13 +5622,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);
@@ -5543,40 +5643,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"))
@@ -5601,6 +5674,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;
@@ -5613,36 +5696,31 @@ 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);
+               setconfspawn("spawn_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);
@@ -5653,6 +5731,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);
        }
@@ -5757,7 +5845,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;
@@ -5777,9 +5865,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, "horizontal_flip")) {
                st = SWM_H_STACK;
-       else if (!strcasecmp(s, "fullscreen"))
+               f = 1;
+       } else if (!strcasecmp(s, "fullscreen"))
                st = SWM_MAX_STACK;
        else
                errx(1, "invalid layout entry, should be 'ws[<idx>]:"
@@ -5815,6 +5909,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);
@@ -6092,9 +6192,7 @@ manage_window(Window id)
        Atom                    *prot = NULL, *pp;
        unsigned char           ws_idx_str[SWM_PROPLEN], *prop = NULL;
        struct swm_region       *r;
-       long                    mask = 0;
        const char              *errstr;
-       XWindowChanges          wc;
        struct pid_e            *p;
        struct quirk            *qp;
 
@@ -6113,9 +6211,10 @@ manage_window(Window id)
                        TAILQ_INSERT_AFTER(&win->ws->winlist, ww, win, entry);
                else if ((ww = win->ws->focus) &&
                    spawn_position == SWM_STACK_ABOVE)
-                       TAILQ_INSERT_AFTER(&win->ws->winlist, win->ws->focus, win, entry);
+                       TAILQ_INSERT_AFTER(&win->ws->winlist, win->ws->focus,
+                           win, entry);
                else if (ww && spawn_position == SWM_STACK_BELOW)
-                       TAILQ_INSERT_AFTER(&win->ws->winlist, win->ws->focus, win, entry);
+                       TAILQ_INSERT_BEFORE(win->ws->focus, win, entry);
                else switch (spawn_position) {
                default:
                case SWM_STACK_TOP:
@@ -6136,6 +6235,7 @@ manage_window(Window id)
                    "new window");
 
        win->id = id;
+       win->bordered = 0;
 
        /* see if we need to override the workspace */
        p = find_pid(window_get_pid(id));
@@ -6213,19 +6313,38 @@ manage_window(Window id)
        win->s = r->s;  /* this never changes */
        if (trans && (ww = find_window(trans)))
                TAILQ_INSERT_AFTER(&ws->winlist, ww, win, entry);
-       else if (spawn_position == SWM_STACK_ABOVE && win->ws->focus)
-               TAILQ_INSERT_AFTER(&win->ws->winlist, win->ws->focus, win, entry);
-       else
-               TAILQ_INSERT_TAIL(&ws->winlist, win, entry);
+       else if (win->ws->focus && spawn_position == SWM_STACK_ABOVE)
+               TAILQ_INSERT_AFTER(&win->ws->winlist, win->ws->focus, win,
+                   entry);
+       else if (win->ws->focus && spawn_position == SWM_STACK_BELOW)
+               TAILQ_INSERT_BEFORE(win->ws->focus, win, entry);
+       else switch (spawn_position) {
+       default:
+       case SWM_STACK_TOP:
+       case SWM_STACK_ABOVE:
+               TAILQ_INSERT_TAIL(&win->ws->winlist, win, entry);
+               break;
+       case SWM_STACK_BOTTOM:
+       case SWM_STACK_BELOW:
+               TAILQ_INSERT_HEAD(&win->ws->winlist, win, entry);
+       }
 
+       /* ignore window border if there is one. */
        WIDTH(win) = win->wa.width;
        HEIGHT(win) = win->wa.height;
-       X(win) = win->wa.x;
-       Y(win) = win->wa.y;
+       X(win) = win->wa.x + win->wa.border_width;
+       Y(win) = win->wa.y + win->wa.border_width;
+       win->bordered = 0;
        win->g_floatvalid = 0;
        win->floatmaxed = 0;
        win->ewmh_flags = 0;
 
+       DNPRINTF(SWM_D_MISC, "manage_window: window: 0x%lx, (x,y) w x h: "
+           "(%d,%d) %d x %d, ws: %d\n", win->id, X(win), Y(win), WIDTH(win),
+           HEIGHT(win), ws->idx);
+
+       constrain_window(win, r, 0);
+
        /* Set window properties so we can remember this after reincarnation */
        if (ws_idx_atom && prop == NULL &&
            snprintf((char *)ws_idx_str, SWM_PROPLEN, "%d", ws->idx) <
@@ -6268,16 +6387,10 @@ manage_window(Window id)
        /* alter window position if quirky */
        if (win->quirks & SWM_Q_ANYWHERE) {
                win->manual = 1; /* don't center the quirky windows */
-               bzero(&wc, sizeof wc);
-               mask = 0;
-               if (bar_enabled && Y(win) < bar_height) {
-                       Y(win) = wc.y = bar_height;
-                       mask |= CWY;
-               }
-               if (WIDTH(win) + X(win) > WIDTH(r)) {
-                       X(win) = wc.x = WIDTH(r) - WIDTH(win) - 2;
-                       mask |= CWX;
-               }
+               if (bar_enabled && Y(win) < bar_height)
+                       Y(win) = bar_height;
+               if (WIDTH(win) + X(win) > WIDTH(r))
+                       X(win) = WIDTH(r) - WIDTH(win) - 2;
                border_me = 1;
        }
 
@@ -6295,10 +6408,10 @@ manage_window(Window id)
 
        /* border me */
        if (border_me) {
-               bzero(&wc, sizeof wc);
-               wc.border_width = border_width;
-               mask |= CWBorderWidth;
-               XConfigureWindow(display, win->id, mask, &wc);
+               win->bordered = 1;
+               X(win) -= border_width;
+               Y(win) -= border_width;
+               update_window(win);
        }
 
        XSelectInput(display, id, EnterWindowMask | FocusChangeMask |
@@ -6464,9 +6577,6 @@ configurerequest(XEvent *e)
                if ((win = find_unmanaged_window(ev->window)) == NULL)
                        new = 1;
 
-       DNPRINTF(SWM_D_EVENT, "configurerequest: window: 0x%lx, new: %s\n",
-           ev->window, YESNO(new));
-
        if (new) {
                bzero(&wc, sizeof wc);
                wc.x = ev->x;
@@ -6476,9 +6586,31 @@ configurerequest(XEvent *e)
                wc.border_width = ev->border_width;
                wc.sibling = ev->above;
                wc.stack_mode = ev->detail;
+
+               DNPRINTF(SWM_D_EVENT, "configurerequest: new window: 0x%lx, "
+                   "new: %s, (x,y) w x h: (%d,%d) %d x %d\n", ev->window,
+                   YESNO(new), wc.x, wc.y, wc.width, wc.height);
+
                XConfigureWindow(display, ev->window, ev->value_mask, &wc);
-       } else
+       } else if ((!win->manual || win->quirks & SWM_Q_ANYWHERE) &&
+           !(win->sh_mask & EWMH_F_FULLSCREEN)) {
+               win->g_float.x = ev->x - X(win->ws->r);
+               win->g_float.y = ev->y - Y(win->ws->r);
+               win->g_float.w = ev->width;
+               win->g_float.h = ev->height;
+               win->g_floatvalid = 1;
+
+               if (win->floating) {
+                       win->g = win->g_float;
+                       win->g.x += X(win->ws->r);
+                       win->g.y += Y(win->ws->r);
+                       update_window(win);
+               } else {
+                       config_win(win, ev);
+               }
+       } else {
                config_win(win, ev);
+       }
 }
 
 void
@@ -6842,21 +6974,28 @@ 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();
 }
 
 void
 clientmessage(XEvent *e)
 {
-       XClientMessageEvent *ev;
-       struct ws_win *win;
+       XClientMessageEvent     *ev;
+       struct ws_win           *win;
 
        ev = &e->xclient;
 
        win = find_window(ev->window);
-       if (win == NULL)
+       if (win == NULL) {
+               if (ev->message_type == ewmh[_NET_ACTIVE_WINDOW].atom) {
+                       DNPRINTF(SWM_D_EVENT, "clientmessage: request focus on "
+                           "unmanaged window.\n");
+                       e->xmaprequest.window = ev->window;
+                       maprequest(e);
+               }
                return;
+       }
 
        DNPRINTF(SWM_D_EVENT, "clientmessage: window: 0x%lx, type: %ld\n",
            ev->window, ev->message_type);
@@ -6958,7 +7097,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);
                }
@@ -7029,7 +7168,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);
        }
@@ -7038,7 +7177,7 @@ scan_xrandr(int i)
        /* map virtual screens onto physical screens */
 #ifdef SWM_XRR_HAS_CRTC
        if (xrandr_support) {
-               sr = XRRGetScreenResources(display, screens[i].root);
+               sr = XRRGetScreenResourcesCurrent(display, screens[i].root);
                if (sr == NULL)
                        new_region(&screens[i], 0, 0,
                            DisplayWidth(display, i),
@@ -7180,12 +7319,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,
@@ -7225,9 +7362,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.");
 }
@@ -7358,11 +7492,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 */