]> code.delx.au - spectrwm/blobdiff - spectrwm.c
Fix man errors
[spectrwm] / spectrwm.c
index 056550bfb12bbf78986586ddf1bd7786cfbadf14..285bac959e8cdee7b8b29241543dc0e0ab14d1dc 100644 (file)
@@ -5,7 +5,7 @@
  * Copyright (c) 2009 Pierre-Yves Ritschard <pyr@spootnik.org>
  * Copyright (c) 2010 Tuukka Kataja <stuge@xor.fi>
  * Copyright (c) 2011 Jason L. Wright <jason@thought.net>
- * Copyright (c) 2011-2015 Reginald Kennedy <rk@rejii.com>
+ * Copyright (c) 2011-2016 Reginald Kennedy <rk@rejii.com>
  * Copyright (c) 2011-2012 Lawrence Teo <lteo@lteo.net>
  * Copyright (c) 2011-2012 Tiago Cunha <tcunha@gmx.com>
  * Copyright (c) 2012-2015 David Hill <dhill@mindcry.org>
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
-/*
- * Much code and ideas taken from dwm under the following license:
- * MIT/X Consortium License
- *
- * 2006-2008 Anselm R Garbe <garbeam at gmail dot com>
- * 2006-2007 Sander van Dijk <a dot h dot vandijk at gmail dot com>
- * 2006-2007 Jukka Salmi <jukka at salmi dot ch>
- * 2007 Premysl Hruby <dfenze at gmail dot com>
- * 2007 Szabolcs Nagy <nszabolcs at gmail dot com>
- * 2007 Christof Musik <christof at sendfax dot de>
- * 2007-2008 Enno Gottox Boland <gottox at s01 dot de>
- * 2007-2008 Peter Hartlich <sgkkr at hartlich dot com>
- * 2008 Martin Hurton <martin dot hurton at gmail dot com>
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- */
 
 /* kernel includes */
 #include <sys/types.h>
 #include <sys/time.h>
 #include <sys/stat.h>
 #include <sys/wait.h>
+#ifdef __OSX__
+#include "queue.h"
+#else
 #include <sys/queue.h>
+#endif
 #include <sys/param.h>
 #include <sys/select.h>
 #if defined(__linux__)
@@ -97,6 +69,7 @@
 #include <X11/Xcursor/Xcursor.h>
 #include <X11/Xft/Xft.h>
 #include <X11/Xlib-xcb.h>
+#include <xcb/xcb.h>
 #include <xcb/xcb_atom.h>
 #include <xcb/xcb_aux.h>
 #include <xcb/xcb_event.h>
@@ -239,19 +212,17 @@ uint32_t          swm_debug = 0
 #define CLEANMASK(mask)                ((mask) & (XCB_KEY_BUT_MASK_SHIFT |     \
     XCB_KEY_BUT_MASK_CONTROL | XCB_KEY_BUT_MASK_MOD_1 |                        \
     XCB_KEY_BUT_MASK_MOD_2 | XCB_KEY_BUT_MASK_MOD_3 |                  \
-    XCB_KEY_BUT_MASK_MOD_4 | XCB_KEY_BUT_MASK_MOD_5))
+    XCB_KEY_BUT_MASK_MOD_4 | XCB_KEY_BUT_MASK_MOD_5) & ~(numlockmask))
 #define BUTTONMASK             (XCB_EVENT_MASK_BUTTON_PRESS |          \
     XCB_EVENT_MASK_BUTTON_RELEASE)
 #define MOUSEMASK              (BUTTONMASK|XCB_EVENT_MASK_POINTER_MOTION)
 #define SWM_PROPLEN            (16)
 #define SWM_FUNCNAME_LEN       (32)
-#define SWM_KEYS_LEN           (255)
 #define SWM_QUIRK_LEN          (64)
 #define X(r)                   ((r)->g.x)
 #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.flags & XCB_ICCCM_SIZE_HINT_P_MIN_SIZE)
@@ -265,6 +236,8 @@ uint32_t            swm_debug = 0
 #define SH_INC_H(w)            ((w)->sh.height_inc)
 #define SWM_MAX_FONT_STEPS     (3)
 #define WINID(w)               ((w) ? (w)->id : XCB_WINDOW_NONE)
+#define ACCEPTS_FOCUS(w)       (!((w)->hints.flags & XCB_ICCCM_WM_HINT_INPUT) \
+    || ((w)->hints.input))
 #define WS_FOCUSED(ws)         ((ws)->r && (ws)->r->s->r_focus == (ws)->r)
 #define YESNO(x)               ((x) ? "yes" : "no")
 #define ICONIC(w)              ((w)->ewmh_flags & EWMH_F_HIDDEN)
@@ -292,17 +265,12 @@ uint32_t          swm_debug = 0
 #define SWM_FOCUS_FOLLOW       (1)
 #define SWM_FOCUS_MANUAL       (2)
 
-#define SWM_CK_NONE            (0)
 #define SWM_CK_ALL             (0xf)
 #define SWM_CK_FOCUS           (0x1)
 #define SWM_CK_POINTER         (0x2)
 #define SWM_CK_FALLBACK                (0x4)
 #define SWM_CK_REGION          (0x8)
 
-#define SWM_G_ALL              (0xf)
-#define SWM_G_SIZE             (0x1)
-#define SWM_G_POS              (0x2)
-
 #define SWM_CONF_DEFAULT       (0)
 #define SWM_CONF_KEYMAPPING    (1)
 
@@ -314,6 +282,7 @@ char                        **start_argv;
 xcb_atom_t             a_state;
 xcb_atom_t             a_prot;
 xcb_atom_t             a_delete;
+xcb_atom_t             a_net_frame_extents;
 xcb_atom_t             a_net_wm_check;
 xcb_atom_t             a_net_supported;
 xcb_atom_t             a_takefocus;
@@ -428,6 +397,7 @@ int          tile_gap = 0;
 bool            java_workaround = true;
 bool            verbose_layout = false;
 #ifdef SWM_DEBUG
+bool            debug_enabled;
 time_t          time_started;
 #endif
 pid_t           bar_pid;
@@ -456,6 +426,7 @@ struct swm_bar {
        xcb_window_t            id;
        xcb_pixmap_t            buffer;
        struct swm_geometry     g;
+       struct swm_region       *r;     /* Associated region. */
 };
 
 /* virtual "screens" */
@@ -470,16 +441,26 @@ struct swm_region {
 };
 TAILQ_HEAD(swm_region_list, swm_region);
 
+enum {
+       SWM_WIN_STATE_REPARENTING,
+       SWM_WIN_STATE_REPARENTED,
+       SWM_WIN_STATE_UNPARENTING,
+       SWM_WIN_STATE_UNPARENTED,
+};
+
 struct ws_win {
        TAILQ_ENTRY(ws_win)     entry;
        TAILQ_ENTRY(ws_win)     stack_entry;
        xcb_window_t            id;
+       xcb_window_t            frame;
        xcb_window_t            transient;
        struct ws_win           *focus_child;   /* focus on child transient */
        struct swm_geometry     g;              /* current geometry */
+       struct swm_geometry     g_prev;         /* prev configured geometry */
        struct swm_geometry     g_float;        /* region coordinates */
        bool                    g_floatvalid;   /* g_float geometry validity */
        bool                    mapped;
+       uint8_t                 state;
        bool                    bordered;
        uint32_t                ewmh_flags;
        int                     font_size_boundary[SWM_MAX_FONT_STEPS];
@@ -494,6 +475,9 @@ struct ws_win {
        xcb_size_hints_t        sh;
        xcb_icccm_get_wm_class_reply_t  ch;
        xcb_icccm_wm_hints_t    hints;
+#ifdef SWM_DEBUG
+       xcb_window_t            debug;
+#endif
 };
 TAILQ_HEAD(ws_win_list, ws_win);
 TAILQ_HEAD(ws_win_stack, ws_win);
@@ -508,7 +492,7 @@ TAILQ_HEAD(pid_list, pid_e);
 struct pid_list                        pidlist = TAILQ_HEAD_INITIALIZER(pidlist);
 
 /* layout handlers */
-void   stack(void);
+void   stack(struct swm_region *);
 void   vertical_config(struct workspace *, int);
 void   vertical_stack(struct workspace *, struct swm_geometry *);
 void   horizontal_config(struct workspace *, int);
@@ -551,6 +535,7 @@ struct workspace {
        struct ws_win           *focus;         /* may be NULL */
        struct ws_win           *focus_prev;    /* may be NULL */
        struct ws_win           *focus_pending; /* may be NULL */
+       struct ws_win           *focus_raise;           /* may be NULL */
        struct swm_region       *r;             /* may be NULL */
        struct swm_region       *old_r;         /* may be NULL */
        struct ws_win_list      winlist;        /* list of windows in ws */
@@ -643,8 +628,6 @@ union arg {
 #define SWM_ARG_ID_CYCLEWS_MOVE_DOWN   (47)
 #define SWM_ARG_ID_STACKINC    (50)
 #define SWM_ARG_ID_STACKDEC    (51)
-#define SWM_ARG_ID_SS_ALL      (60)
-#define SWM_ARG_ID_SS_WINDOW   (61)
 #define SWM_ARG_ID_DONTCENTER  (70)
 #define SWM_ARG_ID_CENTER      (71)
 #define SWM_ARG_ID_KILLWINDOW  (80)
@@ -657,8 +640,6 @@ union arg {
 #define SWM_ARG_ID_MOVEDOWN    (101)
 #define SWM_ARG_ID_MOVELEFT    (102)
 #define SWM_ARG_ID_MOVERIGHT   (103)
-#define SWM_ARG_ID_RAISE       (105)
-#define SWM_ARG_ID_LOWER       (106)
 #define SWM_ARG_ID_BAR_TOGGLE  (110)
 #define SWM_ARG_ID_BAR_TOGGLE_WS       (111)
 #define SWM_ARG_ID_CYCLERG_MOVE_UP     (112)
@@ -708,6 +689,7 @@ enum {
        _NET_DESKTOP_VIEWPORT,
        _NET_MOVERESIZE_WINDOW,
        _NET_NUMBER_OF_DESKTOPS,
+       _NET_REQUEST_FRAME_EXTENTS,
        _NET_RESTACK_WINDOW,
        _NET_WM_ACTION_ABOVE,
        _NET_WM_ACTION_CLOSE,
@@ -751,6 +733,7 @@ struct ewmh_hint {
     {"_NET_DESKTOP_VIEWPORT", XCB_ATOM_NONE},
     {"_NET_MOVERESIZE_WINDOW", XCB_ATOM_NONE},
     {"_NET_NUMBER_OF_DESKTOPS", XCB_ATOM_NONE},
+    {"_NET_REQUEST_FRAME_EXTENTS", XCB_ATOM_NONE},
     {"_NET_RESTACK_WINDOW", XCB_ATOM_NONE},
     {"_NET_WM_ACTION_ABOVE", XCB_ATOM_NONE},
     {"_NET_WM_ACTION_CLOSE", XCB_ATOM_NONE},
@@ -842,6 +825,7 @@ enum actionid {
        FN_FOCUS_NEXT,
        FN_FOCUS_PREV,
        FN_FOCUS_URGENT,
+       FN_FULLSCREEN_TOGGLE,
        FN_MAXIMIZE_TOGGLE,
        FN_HEIGHT_GROW,
        FN_HEIGHT_SHRINK,
@@ -864,6 +848,8 @@ enum actionid {
        FN_MVRG_7,
        FN_MVRG_8,
        FN_MVRG_9,
+       KF_MVRG_NEXT,
+       KF_MVRG_PREV,
        FN_MVWS_1,
        FN_MVWS_2,
        FN_MVWS_3,
@@ -888,6 +874,7 @@ enum actionid {
        FN_MVWS_22,
        FN_NAME_WORKSPACE,
        FN_QUIT,
+       FN_RAISE,
        FN_RAISE_TOGGLE,
        FN_RESIZE,
        FN_RESIZE_CENTERED,
@@ -952,7 +939,10 @@ enum actionid {
        FN_WS_PREV_ALL,
        FN_WS_PREV_MOVE,
        FN_WS_PRIOR,
-       FN_DUMPWINS, /* MUST BE LAST */
+       /* SWM_DEBUG actions MUST be here: */
+       FN_DEBUG_TOGGLE,
+       FN_DUMPWINS,
+       /* ALWAYS last: */
        FN_INVALID
 };
 
@@ -985,7 +975,7 @@ void         bar_extra_stop(void);
 int     bar_extra_update(void);
 void    bar_fmt(const char *, char *, struct swm_region *, size_t);
 void    bar_fmt_expand(char *, size_t);
-void    bar_draw(void);
+void    bar_draw(struct swm_bar *);
 void    bar_print(struct swm_region *, const char *);
 void    bar_print_legacy(struct swm_region *, const char *);
 void    bar_replace(char *, char *, struct swm_region *, size_t);
@@ -1029,6 +1019,10 @@ void      custom_region(const char *);
 void    cycle_layout(struct binding *, struct swm_region *, union arg *);
 void    cyclerg(struct binding *, struct swm_region *, union arg *);
 void    cyclews(struct binding *, struct swm_region *, union arg *);
+#ifdef SWM_DEBUG
+void    debug_refresh(struct ws_win *);
+#endif
+void    debug_toggle(struct binding *, struct swm_region *, union arg *);
 void    destroynotify(xcb_destroy_notify_event_t *);
 void    dumpwins(struct binding *, struct swm_region *, union arg *);
 int     enable_wm(void);
@@ -1050,7 +1044,10 @@ void      ewmh_update_wm_state(struct ws_win *);
 char   *expand_tilde(const char *);
 void    expose(xcb_expose_event_t *);
 void    fake_keypress(struct ws_win *, xcb_keysym_t, uint16_t);
+struct swm_bar *find_bar(xcb_window_t);
+struct ws_win  *find_frame_window(xcb_window_t);
 struct pid_e   *find_pid(pid_t);
+struct swm_region      *find_region(xcb_window_t);
 struct ws_win  *find_unmanaged_window(xcb_window_t);
 struct ws_win  *find_window(xcb_window_t);
 void    floating_toggle(struct binding *, struct swm_region *, union arg *);
@@ -1059,13 +1056,14 @@ void     focus_flush(void);
 void    focus_pointer(struct binding *, struct swm_region *, union arg *);
 void    focus_region(struct swm_region *);
 void    focus_win(struct ws_win *);
-#ifdef SWM_DEBUG
 void    focusin(xcb_focus_in_event_t *);
+#ifdef SWM_DEBUG
 void    focusout(xcb_focus_out_event_t *);
 #endif
 void    focusrg(struct binding *, struct swm_region *, union arg *);
 void    fontset_init(void);
 void    free_window(struct ws_win *);
+void    fullscreen_toggle(struct binding *, struct swm_region *, union arg *);
 xcb_atom_t get_atom_from_string(const char *);
 #ifdef SWM_DEBUG
 char   *get_atom_name(xcb_atom_t);
@@ -1089,6 +1087,9 @@ char      *get_state_mask_label(uint16_t);
 #endif
 int32_t         get_swm_ws(xcb_window_t);
 bool    get_urgent(struct ws_win *);
+#ifdef SWM_DEBUG
+char   *get_win_input_model(struct ws_win *);
+#endif
 char   *get_win_name(xcb_window_t);
 uint8_t         get_win_state(xcb_window_t);
 void    get_wm_protocols(struct ws_win *);
@@ -1129,7 +1130,6 @@ void       pressbutton(struct binding *, struct swm_region *, union arg *);
 void    priorws(struct binding *, struct swm_region *, union arg *);
 #ifdef SWM_DEBUG
 void    print_win_geom(xcb_window_t);
-void    print_win_input_model(struct ws_win *);
 #endif
 void    propertynotify(xcb_property_notify_event_t *);
 void    put_back_event(xcb_generic_event_t *);
@@ -1139,11 +1139,14 @@ void     quirk_remove(struct quirk *);
 void    quirk_replace(struct quirk *, const char *, const char *, const char *,
             uint32_t, int);
 void    quit(struct binding *, struct swm_region *, union arg *);
+void    raise_focus(struct binding *, struct swm_region *, union arg *);
 void    raise_toggle(struct binding *, struct swm_region *, union arg *);
 void    raise_window(struct ws_win *);
 void    region_containment(struct ws_win *, struct swm_region *, int);
 struct swm_region      *region_under(struct swm_screen *, int, int);
 void    regionize(struct ws_win *, int, int);
+void    reparent_window(struct ws_win *);
+void    reparentnotify(xcb_reparent_notify_event_t *);
 void    resize(struct binding *, struct swm_region *, union arg *);
 void    resize_win(struct ws_win *, struct binding *, int);
 void    restart(struct binding *, struct swm_region *, union arg *);
@@ -1159,6 +1162,7 @@ void       search_win(struct binding *, struct swm_region *, union arg *);
 void    search_win_cleanup(void);
 void    search_workspace(struct binding *, struct swm_region *, union arg *);
 void    send_to_rg(struct binding *, struct swm_region *, union arg *);
+void    send_to_rg_relative(struct binding *, struct swm_region *, union arg *);
 void    send_to_ws(struct binding *, struct swm_region *, union arg *);
 void    set_region(struct swm_region *);
 int     setautorun(const char *, const char *, int);
@@ -1211,11 +1215,12 @@ void     unmanage_window(struct ws_win *);
 void    unmap_all(void);
 void    unmap_window(struct ws_win *);
 void    unmapnotify(xcb_unmap_notify_event_t *);
+void    unparent_window(struct ws_win *);
 void    update_floater(struct ws_win *);
 void    update_modkey(uint16_t);
 void    update_win_stacking(struct ws_win *);
 void    update_window(struct ws_win *);
-void    update_window_color(struct ws_win *);
+void    draw_frame(struct ws_win *);
 void    update_wm_state(struct  ws_win *win);
 void    updatenumlockmask(void);
 void    validate_spawns(void);
@@ -1457,18 +1462,9 @@ setup_ewmh(void)
                xcb_change_property(conn, XCB_PROP_MODE_REPLACE, win,
                    a_net_wm_check, XCB_ATOM_WINDOW, 32, 1, &win);
 
-               /*
-                * Impersonate LG3D non-reparenting WM, written by Sun, to
-                * workaround a Java GUI rendering issue.
-                */
-               if (java_workaround)
-                       xcb_change_property(conn, XCB_PROP_MODE_REPLACE, win,
-                           ewmh[_NET_WM_NAME].atom, a_utf8_string,
-                           8, strlen("LG3D"), "LG3D");
-               else
-                       xcb_change_property(conn, XCB_PROP_MODE_REPLACE, win,
-                           ewmh[_NET_WM_NAME].atom, a_utf8_string,
-                           8, strlen("spectrwm"), "spectrwm");
+               xcb_change_property(conn, XCB_PROP_MODE_REPLACE, win,
+                   ewmh[_NET_WM_NAME].atom, a_utf8_string,
+                   8, strlen("spectrwm"), "spectrwm");
 
                /* Report supported atoms */
                xcb_delete_property(conn, root, a_net_supported);
@@ -1698,7 +1694,7 @@ ewmh_apply_flags(struct ws_win *win, uint32_t pending)
                        }
                }
 
-               update_window_color(win);
+               draw_frame(win);
                raise_window(win);
        }
 
@@ -1803,8 +1799,8 @@ dumpwins(struct binding *bp, struct swm_region *r, union arg *args)
                c = xcb_get_window_attributes(conn, w->id);
                wa = xcb_get_window_attributes_reply(conn, c, NULL);
                if (wa) {
-                       DPRINTF("win %#x, map_state: %d, state: %u, "
-                           "transient: %#x\n", w->id, wa->map_state,
+                       DPRINTF("win %#x (%#x), map_state: %d, state: %u, "
+                           "transient: %#x\n", w->frame, w->id, wa->map_state,
                            state, w->transient);
                        free(wa);
                } else
@@ -1814,8 +1810,8 @@ dumpwins(struct binding *bp, struct swm_region *r, union arg *args)
 
        DPRINTF("=== stacking order (top down) === \n");
        TAILQ_FOREACH(w, &r->ws->stack, stack_entry) {
-               DPRINTF("win %#x, fs: %s, maximized: %s, above: %s, "
-                   "iconic: %s\n", w->id, YESNO(FULLSCREEN(w)),
+               DPRINTF("win %#x (%#x), fs: %s, maximized: %s, above: %s, "
+                   "iconic: %s\n", w->frame, w->id, YESNO(FULLSCREEN(w)),
                    YESNO(MAXIMIZED(w)), YESNO(ABOVE(w)), YESNO(ICONIC(w)));
        }
 
@@ -1836,6 +1832,156 @@ dumpwins(struct binding *bp, struct swm_region *r, union arg *args)
 
        DPRINTF("=================================\n");
 }
+
+void
+debug_toggle(struct binding *b, struct swm_region *r, union arg *s)
+{
+       struct ws_win           *win;
+       int                     num_screens, i, j;
+
+       /* Suppress warnings. */
+       (void)b;
+       (void)r;
+       (void)s;
+
+       DNPRINTF(SWM_D_MISC, "debug_toggle\n");
+
+       debug_enabled = !debug_enabled;
+
+       num_screens = get_screen_count();
+       for (i = 0; i < num_screens; i++)
+               for (j = 0; j < workspace_limit; j++)
+                       TAILQ_FOREACH(win, &screens[i].ws[j].winlist, entry)
+                               debug_refresh(win);
+
+       xcb_flush(conn);
+}
+
+void
+debug_refresh(struct ws_win *win)
+{
+       struct ws_win           *w;
+       XftDraw                 *draw;
+       XGlyphInfo              info;
+       GC                      l_draw;
+       XGCValues               l_gcv;
+       XRectangle              l_ibox, l_lbox;
+       xcb_rectangle_t         rect;
+       size_t                  len;
+       uint32_t                wc[4], mask, width, height, gcv[1];
+       int                     widx, sidx;
+       char                    *s;
+       xcb_screen_t            *screen;
+
+       if (debug_enabled) {
+               /* Create debug window if it doesn't exist. */
+               if (win->debug == XCB_WINDOW_NONE) {
+                       if ((screen = get_screen(win->s->idx)) == NULL)
+                               errx(1, "ERROR: can't get screen %d.",
+                                   win->s->idx);
+
+                       win->debug = xcb_generate_id(conn);
+                       wc[0] = win->s->c[SWM_S_COLOR_BAR].pixel;
+                       wc[1] = win->s->c[SWM_S_COLOR_BAR_BORDER].pixel;
+                       wc[2] = screen->default_colormap;
+
+                       xcb_create_window(conn, screen->root_depth, win->debug,
+                           win->frame, 0, 0, 10, 10, 1,
+                           XCB_WINDOW_CLASS_INPUT_OUTPUT, screen->root_visual,
+                           XCB_CW_BACK_PIXEL | XCB_CW_BORDER_PIXEL |
+                           XCB_CW_COLORMAP, wc);
+
+                       xcb_map_window(conn, win->debug);
+               }
+
+               /* Determine workspace window list index. */
+               widx = 0;
+               TAILQ_FOREACH(w, &win->ws->winlist, entry) {
+                       ++widx;
+                       if (w == win)
+                               break;
+               }
+
+               /* Determine stacking index (top down). */
+               sidx = 0;
+               TAILQ_FOREACH(w, &win->ws->stack, stack_entry) {
+                       ++sidx;
+                       if (w == win)
+                               break;
+               }
+
+               if (asprintf(&s, "%#x f:%#x wl:%d s:%d im:%s", win->id,
+                   win->frame, widx, sidx, get_win_input_model(win)) == -1)
+                       return;
+
+               len = strlen(s);
+
+               /* Update window to an appropriate dimension. */
+               if (bar_font_legacy) {
+                       XmbTextExtents(bar_fs, s, len, &l_ibox, &l_lbox);
+                       width = l_lbox.width + 4;
+                       height = bar_fs_extents->max_logical_extent.height + 4;
+               } else {
+                       XftTextExtentsUtf8(display, bar_font, (FcChar8 *)s, len,
+                           &info);
+                       width = info.width + 4;
+                       height = bar_font->height + 4;
+               }
+
+               mask = XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y |
+                   XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT;
+               if (win->bordered)
+                       wc[0] = wc[1] = border_width;
+               else
+                       wc[0] = wc[1] = 0;
+
+               wc[2] = width;
+               wc[3] = height;
+
+               xcb_configure_window(conn, win->debug, mask, wc);
+
+               /* Draw a filled rectangle to 'clear' window. */
+               rect.x = 0;
+               rect.y = 0;
+               rect.width = width;
+               rect.height = height;
+
+               gcv[0] = win->s->c[SWM_S_COLOR_BAR].pixel;
+               xcb_change_gc(conn, win->s->bar_gc, XCB_GC_FOREGROUND, gcv);
+               xcb_poly_fill_rectangle(conn, win->debug, win->s->bar_gc, 1,
+                   &rect);
+
+               /* Draw text. */
+               if (bar_font_legacy) {
+                       l_gcv.graphics_exposures = 0;
+                       l_draw = XCreateGC(display, win->debug, 0, &l_gcv);
+
+                       XSetForeground(display, l_draw,
+                               win->s->c[SWM_S_COLOR_BAR_FONT].pixel);
+
+                       DRAWSTRING(display, win->debug, bar_fs, l_draw, 2,
+                           (bar_fs_extents->max_logical_extent.height -
+                           l_lbox.height) / 2 - l_lbox.y, s, len);
+
+                       XFreeGC(display, l_draw);
+               } else {
+                       draw = XftDrawCreate(display, win->debug,
+                           DefaultVisual(display, win->s->idx),
+                           DefaultColormap(display, win->s->idx));
+
+                       XftDrawStringUtf8(draw, &bar_font_color, bar_font, 2,
+                           (bar_height + bar_font->height) / 2 -
+                           bar_font->descent, (FcChar8 *)s, len);
+
+                       XftDrawDestroy(draw);
+               }
+
+               free(s);
+       } else if (win->debug != XCB_WINDOW_NONE) {
+                       xcb_destroy_window(conn, win->debug);
+                       win->debug = XCB_WINDOW_NONE;
+       }
+}
 #else
 void
 dumpwins(struct binding *b, struct swm_region *r, union arg *s)
@@ -1844,6 +1990,14 @@ dumpwins(struct binding *b, struct swm_region *r, union arg *s)
        (void)r;
        (void)s;
 }
+
+void
+debug_toggle(struct binding *b, struct swm_region *r, union arg *s)
+{
+       (void)b;
+       (void)r;
+       (void)s;
+}
 #endif /* SWM_DEBUG */
 
 void
@@ -2265,7 +2419,7 @@ bar_urgent(char *s, size_t sz)
                        strlcat(s, "- ", sz);
                }
        }
-       if(urgent_collapse && s[0])
+       if (urgent_collapse && s[0])
                s[strlen(s) - 1] = 0;
 }
 
@@ -2506,46 +2660,43 @@ bar_fmt_expand(char *fmtexp, size_t sz)
 #endif
 }
 
-/* Redraws the bar; need to follow with xcb_flush() or focus_flush(). */
+/* Redraws a region bar; need to follow with xcb_flush() or focus_flush(). */
 void
-bar_draw(void)
+bar_draw(struct swm_bar *bar)
 {
+       struct swm_region       *r;
        char                    fmtexp[SWM_BAR_MAX], fmtnew[SWM_BAR_MAX];
        char                    fmtrep[SWM_BAR_MAX];
-       int                     i, num_screens;
-       struct swm_region       *r;
 
        /* expand the format by first passing it through strftime(3) */
        bar_fmt_expand(fmtexp, sizeof fmtexp);
 
-       num_screens = get_screen_count();
-       for (i = 0; i < num_screens; i++) {
-               TAILQ_FOREACH(r, &screens[i].rl, entry) {
-                       if (r->bar == NULL)
-                               continue;
+       if (bar == NULL)
+               return;
 
-                       if (bar_enabled && r->ws->bar_enabled)
-                               xcb_map_window(conn, r->bar->id);
-                       else {
-                               xcb_unmap_window(conn, r->bar->id);
-                               continue;
-                       }
+       r = bar->r;
 
-                       if (startup_exception)
-                               snprintf(fmtrep, sizeof fmtrep, "total "
-                                   "exceptions: %d, first exception: %s",
-                                   nr_exceptions,
-                                   startup_exception);
-                       else {
-                               bar_fmt(fmtexp, fmtnew, r, sizeof fmtnew);
-                               bar_replace(fmtnew, fmtrep, r, sizeof fmtrep);
-                       }
-                       if (bar_font_legacy)
-                               bar_print_legacy(r, fmtrep);
-                       else
-                               bar_print(r, fmtrep);
-               }
+       if (bar_enabled && r->ws->bar_enabled)
+               xcb_map_window(conn, bar->id);
+       else {
+               xcb_unmap_window(conn, bar->id);
+               return;
+       }
+
+       if (startup_exception)
+               snprintf(fmtrep, sizeof fmtrep, "total "
+                   "exceptions: %d, first exception: %s",
+                   nr_exceptions,
+                   startup_exception);
+       else {
+               bar_fmt(fmtexp, fmtnew, r, sizeof fmtnew);
+               bar_replace(fmtnew, fmtrep, r, sizeof fmtrep);
        }
+
+       if (bar_font_legacy)
+               bar_print_legacy(r, fmtrep);
+       else
+               bar_print(r, fmtrep);
 }
 
 /*
@@ -2633,10 +2784,13 @@ bar_toggle(struct binding *bp, struct swm_region *r, union arg *args)
                                        xcb_unmap_window(conn, tmpr->bar->id);
                        }
 
-       stack();
-
-       /* must be after stack */
-       bar_draw();
+       /* Restack all regions and redraw bar. */
+       num_screens = get_screen_count();
+       for (i = 0; i < num_screens; i++)
+               TAILQ_FOREACH(tmpr, &screens[i].rl, entry) {
+                       stack(tmpr);
+                       bar_draw(tmpr->bar);
+               }
 
        focus_flush();
 }
@@ -2714,7 +2868,8 @@ fontset_init(void)
                bar_fs = NULL;
        }
 
-       DNPRINTF(SWM_D_INIT, "fontset_init: loading bar_fonts: %s\n", bar_fonts);
+       DNPRINTF(SWM_D_INIT, "fontset_init: loading bar_fonts: %s\n",
+           bar_fonts);
 
        bar_fs = XCreateFontSet(display, bar_fonts, &missing_charsets,
            &num_missing_charsets, &default_string);
@@ -2826,6 +2981,7 @@ bar_setup(struct swm_region *r)
        else
                xft_init(r);
 
+       r->bar->r = 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;
@@ -2918,6 +3074,9 @@ get_win_state(xcb_window_t w)
 void
 version(struct binding *bp, struct swm_region *r, union arg *args)
 {
+       struct swm_region       *tmpr;
+       int                     i, num_screens;
+
        /* suppress unused warnings since vars are needed */
        (void)bp;
        (void)r;
@@ -2930,8 +3089,13 @@ version(struct binding *bp, struct swm_region *r, union arg *args)
        else
                strlcpy(bar_vertext, "", sizeof bar_vertext);
 
-       bar_draw();
-       xcb_flush(conn);
+       num_screens = get_screen_count();
+       for (i = 0; i < num_screens; i++) {
+               TAILQ_FOREACH(tmpr, &screens[i].rl, entry) {
+                       bar_draw(tmpr->bar);
+                       xcb_flush(conn);
+               }
+       }
 }
 
 void
@@ -2980,13 +3144,13 @@ config_win(struct ws_win *win, xcb_configure_request_event_t *ev)
        ce.y = Y(win);
        ce.width = WIDTH(win);
        ce.height = HEIGHT(win);
+       ce.border_width = 0;
        ce.override_redirect = 0;
 
        if (ev == NULL) {
                /* EWMH */
                ce.event = win->id;
                ce.window = win->id;
-               ce.border_width = BORDER(win);
                ce.above_sibling = XCB_WINDOW_NONE;
        } else {
                /* normal */
@@ -3031,9 +3195,9 @@ config_win(struct ws_win *win, xcb_configure_request_event_t *ev)
                }
 
                /* adjust x and y for requested border_width. */
-               ce.x += BORDER(win) - ev->border_width;
-               ce.y += BORDER(win) - ev->border_width;
-               ce.border_width = ev->border_width;
+               ce.x += ev->border_width;
+               ce.y += ev->border_width;
+
                ce.above_sibling = ev->sibling;
        }
 
@@ -3198,29 +3362,43 @@ void
 update_win_stacking(struct ws_win *win)
 {
        struct ws_win           *sibling;
+#ifdef SWM_DEBUG
+       struct ws_win           *w;
+#endif
        struct swm_region       *r;
        uint32_t                val[2];
 
        if (win == NULL || (r = win->ws->r) == NULL)
                return;
 
+       if (win->frame == XCB_WINDOW_NONE) {
+               DNPRINTF(SWM_D_EVENT, "update_window_stacking: win %#x not "
+                   "reparented.\n", win->id);
+               return;
+       }
+
        sibling = TAILQ_NEXT(win, stack_entry);
        if (sibling != NULL && (FLOATING(win) == FLOATING(sibling) ||
            (win->ws->always_raise && win->ws->focus == win)))
-               val[0] = sibling->id;
+               val[0] = sibling->frame;
        else if (FLOATING(win) || (win->ws->always_raise &&
            win->ws->focus == win))
                val[0] = r->bar->id;
        else
                val[0] = r->id;
 
-       DNPRINTF(SWM_D_EVENT, "update_win_stacking: %#x, sibling %#x\n",
-           win->id, val[0]);
+       DNPRINTF(SWM_D_EVENT, "update_win_stacking: win %#x (%#x), "
+           "sibling %#x\n", win->frame, win->id, val[0]);
 
        val[1] = XCB_STACK_MODE_ABOVE;
 
-       xcb_configure_window(conn, win->id, XCB_CONFIG_WINDOW_SIBLING |
+       xcb_configure_window(conn, win->frame, XCB_CONFIG_WINDOW_SIBLING |
            XCB_CONFIG_WINDOW_STACK_MODE, val);
+
+#ifdef SWM_DEBUG
+       TAILQ_FOREACH(w, &win->ws->winlist, entry)
+               debug_refresh(w);
+#endif
 }
 
 void
@@ -3235,6 +3413,7 @@ map_window(struct ws_win *win)
        if (win->mapped)
                return;
 
+       xcb_map_window(conn, win->frame);
        xcb_map_window(conn, win->id);
        set_win_state(win, XCB_ICCCM_WM_STATE_NORMAL);
        win->mapped = true;
@@ -3253,6 +3432,7 @@ unmap_window(struct ws_win *win)
                return;
 
        xcb_unmap_window(conn, win->id);
+       xcb_unmap_window(conn, win->frame);
        set_win_state(win, XCB_ICCCM_WM_STATE_ICONIC);
        win->mapped = false;
 }
@@ -3361,7 +3541,7 @@ center_pointer(struct swm_region *r)
        DNPRINTF(SWM_D_EVENT, "center_pointer: win %#x.\n", WINID(win));
 
        if (win && win->mapped)
-               xcb_warp_pointer(conn, XCB_NONE, win->id, 0, 0, 0, 0,
+               xcb_warp_pointer(conn, XCB_NONE, win->frame, 0, 0, 0, 0,
                    WIDTH(win) / 2, HEIGHT(win) / 2);
        else
                xcb_warp_pointer(conn, XCB_NONE, r->id, 0, 0, 0, 0,
@@ -3426,56 +3606,100 @@ root_to_region(xcb_window_t root, int check)
        return (r);
 }
 
-struct ws_win *
-find_unmanaged_window(xcb_window_t id)
+struct swm_region *
+find_region(xcb_window_t id)
 {
-       struct ws_win           *win;
-       int                     i, j, num_screens;
+       struct swm_region       *r;
+       int                     i, num_screens;
 
        num_screens = get_screen_count();
        for (i = 0; i < num_screens; i++)
-               for (j = 0; j < workspace_limit; j++)
-                       TAILQ_FOREACH(win, &screens[i].ws[j].unmanagedlist,
-                           entry)
-                               if (id == win->id)
-                                       return (win);
-       return (NULL);
+               TAILQ_FOREACH(r, &screens[i].rl, entry)
+                       if (r->id == id)
+                               return r;
+
+       return NULL;
+}
+
+struct swm_bar *
+find_bar(xcb_window_t id)
+{
+       struct swm_region       *r;
+       int                     i, num_screens;
+
+       num_screens = get_screen_count();
+       for (i = 0; i < num_screens; i++)
+               TAILQ_FOREACH(r, &screens[i].rl, entry)
+                       if (r->bar && r->bar->id == id)
+                               return r->bar;
+
+       return NULL;
+}
+
+struct ws_win *
+find_frame_window(xcb_window_t id) {
+       struct swm_region       *r;
+       struct ws_win           *w;
+       int                     i, num_screens;
+
+       num_screens = get_screen_count();
+       for (i = 0; i < num_screens; i++)
+               TAILQ_FOREACH(r, &screens[i].rl, entry)
+                       TAILQ_FOREACH(w, &r->ws->winlist, entry)
+                               if (w->frame == id)
+                                       return w;
+
+       return NULL;
 }
 
 struct ws_win *
 find_window(xcb_window_t id)
 {
-       struct ws_win           *win;
+       struct ws_win           *win = NULL;
        int                     i, j, num_screens;
-       xcb_query_tree_reply_t  *r;
+       xcb_query_tree_reply_t  *qtr;
+
+       DNPRINTF(SWM_D_MISC, "find_window: id: %#x\n", id);
 
        num_screens = get_screen_count();
        for (i = 0; i < num_screens; i++)
                for (j = 0; j < workspace_limit; j++)
                        TAILQ_FOREACH(win, &screens[i].ws[j].winlist, entry)
-                               if (id == win->id)
+                               if (id == win->id || id == win->frame)
                                        return (win);
 
-       r = xcb_query_tree_reply(conn, xcb_query_tree(conn, id), NULL);
-       if (r == NULL)
-               return (NULL);
 
-       /* if we were looking for the parent return that window instead */
-       if (r->parent == 0 || r->root == r->parent) {
-               free(r);
-               return (NULL);
+       /* If window isn't top-level, try to find managed ancestor. */
+       qtr = xcb_query_tree_reply(conn, xcb_query_tree(conn, id), NULL);
+       if (qtr) {
+               if (qtr->parent != XCB_WINDOW_NONE && qtr->parent != qtr->root)
+                       win = find_window(qtr->parent);
+
+#ifdef SWM_DEBUG
+               if (win)
+                       DNPRINTF(SWM_D_MISC, "find_window: found child %#x "
+                           "of %#x.\n", win->id, qtr->parent);
+#endif
+
+               free(qtr);
        }
 
-       /* look for parent */
+       return (win);
+}
+
+struct ws_win *
+find_unmanaged_window(xcb_window_t id)
+{
+       struct ws_win           *win;
+       int                     i, j, num_screens;
+
+       num_screens = get_screen_count();
        for (i = 0; i < num_screens; i++)
                for (j = 0; j < workspace_limit; j++)
-                       TAILQ_FOREACH(win, &screens[i].ws[j].winlist, entry)
-                               if (r->parent == win->id) {
-                                       free(r);
+                       TAILQ_FOREACH(win, &screens[i].ws[j].unmanagedlist,
+                           entry)
+                               if (id == win->id)
                                        return (win);
-                               }
-
-       free(r);
        return (NULL);
 }
 
@@ -3489,7 +3713,16 @@ spawn(int ws_idx, union arg *args, bool close_fd)
 
        close(xcb_get_file_descriptor(conn));
 
-       setenv("LD_PRELOAD", SWM_LIB, 1);
+       if ((ret = getenv("LD_PRELOAD"))) {
+               if (asprintf(&ret, "%s:%s", SWM_LIB, ret) == -1) {
+                       warn("spawn: asprintf LD_PRELOAD");
+                       _exit(1);
+               }
+               setenv("LD_PRELOAD", ret, 1);
+               free(ret);
+       } else {
+               setenv("LD_PRELOAD", SWM_LIB, 1);
+       }
 
        if (asprintf(&ret, "%d", ws_idx) == -1) {
                warn("spawn: asprintf SWM_WS");
@@ -3553,6 +3786,10 @@ kill_refs(struct ws_win *win)
                                ws->focus = NULL;
                        if (win == ws->focus_prev)
                                ws->focus_prev = NULL;
+                       if (win == ws->focus_pending)
+                               ws->focus_pending = NULL;
+                       if (win == ws->focus_raise)
+                               ws->focus_raise = NULL;
 
                        if (TRANS(win))
                                TAILQ_FOREACH(w, &ws->winlist, entry)
@@ -3638,6 +3875,9 @@ unfocus_win(struct ws_win *win)
        if (win->ws->focus == win) {
                win->ws->focus = NULL;
                win->ws->focus_prev = win;
+               if (win->ws->focus_raise == win && !FLOATING(win)) {
+                       update_win_stacking(win);
+               }
        }
 
        if (validate_win(win->ws->focus)) {
@@ -3650,7 +3890,7 @@ unfocus_win(struct ws_win *win)
                win->ws->focus_prev = NULL;
        }
 
-       update_window_color(win);
+       draw_frame(win);
 
        /* Raise window to "top unfocused position." */
        if (win->ws->always_raise)
@@ -3676,7 +3916,8 @@ focus_win(struct ws_win *win)
 {
        struct ws_win                   *cfw = NULL, *parent = NULL, *w, *tmpw;
        struct workspace                *ws;
-       xcb_get_input_focus_reply_t     *gifr;
+       xcb_get_input_focus_reply_t     *gifr = NULL;
+       xcb_get_window_attributes_reply_t       *war = NULL;
 
        DNPRINTF(SWM_D_FOCUS, "focus_win: win %#x\n", WINID(win));
 
@@ -3695,30 +3936,28 @@ focus_win(struct ws_win *win)
 
        gifr = xcb_get_input_focus_reply(conn, xcb_get_input_focus(conn), NULL);
        if (gifr) {
+               DNPRINTF(SWM_D_FOCUS, "focus_win: cur focus: %#x\n",
+                   gifr->focus);
+
                cfw = find_window(gifr->focus);
-               if (cfw != NULL && cfw != win) {
-                       if (cfw->ws != ws && cfw->ws->r != NULL) {
-                               /* Change border to unfocused color. */
-                               xcb_change_window_attributes(conn, cfw->id,
-                                   XCB_CW_BORDER_PIXEL,
-                                   &cfw->s->c[(MAXIMIZED(cfw) ?
-                                   SWM_S_COLOR_UNFOCUS_MAXIMIZED :
-                                   SWM_S_COLOR_UNFOCUS)].pixel);
-
-                               /* Update border width */
-                               if (cfw->bordered &&
-                                   (cfw->quirks & SWM_Q_MINIMALBORDER) &&
-                                   FLOATING(cfw)) {
-                                       cfw->bordered = 0;
-                                       X(cfw) += border_width;
-                                       Y(cfw) += border_width;
-                                       update_window(cfw);
+               if (cfw) {
+                       if (cfw != win) {
+                               if (cfw->ws != ws && cfw->ws->r != NULL &&
+                                   cfw->frame != XCB_WINDOW_NONE) {
+                                       draw_frame(cfw);
+                               } else {
+                                       unfocus_win(cfw);
                                }
-                       } else {
-                               unfocus_win(cfw);
+                       }
+               } else {
+                       war = xcb_get_window_attributes_reply(conn,
+                           xcb_get_window_attributes(conn, gifr->focus), NULL);
+                       if (war && war->override_redirect && ws->focus == win) {
+                               DNPRINTF(SWM_D_FOCUS, "focus_win: skip refocus "
+                                   "from override_redirect.\n");
+                               goto out;
                        }
                }
-               free(gifr);
        }
 
        if (ws->focus != win) {
@@ -3737,15 +3976,28 @@ focus_win(struct ws_win *win)
                        parent->focus_child = win;
        }
 
-       if (cfw != win && ws->r != NULL) {
+       /* Update window border even if workspace is hidden. */
+       draw_frame(win);
+
+       if (cfw == win) {
+               DNPRINTF(SWM_D_FOCUS, "focus_win: already focused.\n");
+               goto out;
+       }
+
+       if (ws->r) {
                /* Set input focus if no input hint, or indicated by hint. */
-               if (!(win->hints.flags & XCB_ICCCM_WM_HINT_INPUT) ||
-                   win->hints.input)
-                       xcb_set_input_focus(conn, XCB_INPUT_FOCUS_PARENT,
-                                       win->id, last_event_time);
-               else
-                       xcb_set_input_focus(conn, XCB_INPUT_FOCUS_PARENT,
+               if (ACCEPTS_FOCUS(win)) {
+                       DNPRINTF(SWM_D_FOCUS, "focus_win: set_input_focus: %#x,"
+                           " revert-to: parent, time: %#x\n", win->id,
+                           last_event_time);
+                       xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT,
+                           win->id, last_event_time);
+               } else if (!win->take_focus) {
+                       DNPRINTF(SWM_D_FOCUS, "focus_win: set_input_focus: %#x,"
+                           " revert-to: parent, time: 0\n", ws->r->id);
+                       xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT,
                            ws->r->id, XCB_CURRENT_TIME);
+               }
 
                /* Tell app it can adjust focus to a specific window. */
                if (win->take_focus) {
@@ -3798,25 +4050,13 @@ focus_win(struct ws_win *win)
                xcb_change_property(conn, XCB_PROP_MODE_REPLACE, win->s->root,
                    ewmh[_NET_ACTIVE_WINDOW].atom, XCB_ATOM_WINDOW, 32, 1,
                    &win->id);
-       }
 
-       if (cfw != win) {
-               /* Update window border even if workspace is hidden. */
-               update_window_color(win);
-
-               /* Update border width */
-               if (!win->bordered && WS_FOCUSED(win->ws) &&
-                   (win->quirks & SWM_Q_MINIMALBORDER) && FLOATING(win)) {
-                       win->bordered = 1;
-                       X(win) -= border_width;
-                       Y(win) -= border_width;
-                       update_window(win);
-               }
-       }
+               bar_draw(ws->r->bar);
+       }
 
 out:
-       bar_draw();
-
+       free(gifr);
+       free(war);
        DNPRINTF(SWM_D_FOCUS, "focus_win: done.\n");
 }
 
@@ -3905,6 +4145,10 @@ set_region(struct swm_region *r)
 
        r->s->r_focus = r;
 
+       /* Update the focus window frame on the now unfocused region. */
+       if (rf && rf->ws->focus)
+               draw_frame(rf->ws->focus);
+
        ewmh_update_current_desktop();
 }
 
@@ -3925,14 +4169,17 @@ focus_region(struct swm_region *r)
                focus_win(nfw);
        } else {
                /* New region is empty; need to manually unfocus win. */
-               if (old_r)
+               if (old_r) {
                        unfocus_win(old_r->ws->focus);
+                       /* Clear bar since empty. */
+                       bar_draw(old_r->bar);
+               }
 
-               xcb_set_input_focus(conn, XCB_INPUT_FOCUS_PARENT, r->id,
+               DNPRINTF(SWM_D_FOCUS, "focus_region: set_input_focus: %#x, "
+                   "revert-to: parent, time: 0\n", r->id);
+               xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT, r->id,
                    XCB_CURRENT_TIME);
 
-               /* Clear bar since empty. */
-               bar_draw();
        }
 }
 
@@ -3946,8 +4193,6 @@ switchws(struct binding *bp, struct swm_region *r, union arg *args)
        int                     wsid = args->id;
        bool                    unmap_old = false;
 
-       (void)bp;
-
        if (!(r && r->s))
                return;
 
@@ -3967,7 +4212,8 @@ switchws(struct binding *bp, struct swm_region *r, union arg *args)
                return;
 
        other_r = new_ws->r;
-       if (other_r && workspace_clamp) {
+       if (other_r && workspace_clamp &&
+           bp->action != FN_RG_MOVE_NEXT && bp->action != FN_RG_MOVE_PREV) {
                DNPRINTF(SWM_D_WS, "switchws: ws clamped.\n");
 
                if (warp_focus) {
@@ -3981,7 +4227,7 @@ switchws(struct binding *bp, struct swm_region *r, union arg *args)
        }
 
        if ((win = old_ws->focus) != NULL) {
-               update_window_color(win);
+               draw_frame(win);
 
                xcb_change_property(conn, XCB_PROP_MODE_REPLACE, win->s->root,
                    ewmh[_NET_ACTIVE_WINDOW].atom, XCB_ATOM_WINDOW, 32, 1,
@@ -4012,7 +4258,9 @@ switchws(struct binding *bp, struct swm_region *r, union arg *args)
        }
 
        new_ws->state = SWM_WS_STATE_MAPPING;
-       stack();
+
+       stack(other_r);
+       stack(this_r);
 
        /* unmap old windows */
        if (unmap_old) {
@@ -4031,9 +4279,11 @@ switchws(struct binding *bp, struct swm_region *r, union arg *args)
 
        /* Clear bar and set focus on region input win if new ws is empty. */
        if (new_ws->focus_pending == NULL && new_ws->focus == NULL) {
-               xcb_set_input_focus(conn, XCB_INPUT_FOCUS_PARENT, r->id,
+               DNPRINTF(SWM_D_FOCUS, "switchws: set_input_focus: %#x, "
+                   "revert-to: parent, time: 0\n", r->id);
+               xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT, r->id,
                    XCB_CURRENT_TIME);
-               bar_draw();
+               bar_draw(r->bar);
        }
 
        ewmh_update_current_desktop();
@@ -4325,7 +4575,7 @@ swapwin(struct binding *bp, struct swm_region *r, union arg *args)
        sort_windows(wl);
        ewmh_update_client_list();
 
-       stack();
+       stack(r);
        center_pointer(r);
        focus_flush();
 out:
@@ -4583,7 +4833,7 @@ focus(struct binding *bp, struct swm_region *r, union arg *args)
        }
 
        if (clear_maximized(ws) > 0)
-               stack();
+               stack(r);
 
        focus_win(get_focus_magic(winfocus));
        center_pointer(r);
@@ -4622,8 +4872,8 @@ cycle_layout(struct binding *bp, struct swm_region *r, union arg *args)
 
        clear_maximized(ws);
 
-       stack();
-       bar_draw();
+       stack(r);
+       bar_draw(r->bar);
 
        focus_win(get_region_focus(r));
 
@@ -4642,79 +4892,71 @@ stack_config(struct binding *bp, struct swm_region *r, union arg *args)
            args->id, ws->idx);
 
        if (clear_maximized(ws) > 0)
-               stack();
+               stack(r);
 
        if (ws->cur_layout->l_config != NULL)
                ws->cur_layout->l_config(ws, args->id);
 
        if (args->id != SWM_ARG_ID_STACKINIT)
-               stack();
-       bar_draw();
+               stack(r);
+       bar_draw(r->bar);
 
        center_pointer(r);
        focus_flush();
 }
 
 void
-stack(void) {
+stack(struct swm_region *r) {
        struct swm_geometry     g;
-       struct swm_region       *r, *r_prev = NULL;
-       int                     i, num_screens;
+       struct swm_region       *r_prev;
        uint32_t                val[2];
-#ifdef SWM_DEBUG
-       int j;
-#endif
 
-       DNPRINTF(SWM_D_STACK, "stack: begin\n");
+       if (r == NULL)
+               return;
 
-       num_screens = get_screen_count();
-       for (i = 0; i < num_screens; i++) {
-#ifdef SWM_DEBUG
-               j = 0;
-#endif
-               TAILQ_FOREACH(r, &screens[i].rl, entry) {
-                       /* Adjust stack area for region bar and padding. */
-                       g = r->g;
-                       g.x += region_padding;
-                       g.y += region_padding;
-                       g.w -= 2 * border_width + 2 * region_padding;
-                       g.h -= 2 * border_width + 2 * region_padding;
-                       if (bar_enabled && r->ws->bar_enabled) {
-                               if (!bar_at_bottom)
-                                       g.y += bar_height;
-                               g.h -= bar_height;
-                       }
+       DNPRINTF(SWM_D_STACK, "stack: begin\n");
 
-                       DNPRINTF(SWM_D_STACK, "stack: workspace: %d (screen: "
-                           "%d, region: %d), (x,y) WxH: (%d,%d) %d x %d\n",
-                           r->ws->idx, i, j++, g.x, g.y, g.w, g.h);
-
-                       if (r_prev) {
-                               /* Stack bar/input relative to prev. region. */
-                               val[1] = XCB_STACK_MODE_ABOVE;
-
-                               val[0] = r_prev->id;
-                               DNPRINTF(SWM_D_STACK, "stack: region input %#x "
-                                   "relative to %#x.\n", r->id, val[0]);
-                               xcb_configure_window(conn, r->id,
-                                   XCB_CONFIG_WINDOW_SIBLING |
-                                   XCB_CONFIG_WINDOW_STACK_MODE, val);
-
-                               val[0] = r_prev->bar->id;
-                               DNPRINTF(SWM_D_STACK, "stack: region bar %#x "
-                                   "relative to %#x.\n", r->bar->id, val[0]);
-                               xcb_configure_window(conn, r->bar->id,
-                                   XCB_CONFIG_WINDOW_SIBLING |
-                                   XCB_CONFIG_WINDOW_STACK_MODE, val);
-                       }
+       /* Adjust stack area for region bar and padding. */
+       g = r->g;
+       g.x += region_padding;
+       g.y += region_padding;
+       g.w -= 2 * border_width + 2 * region_padding;
+       g.h -= 2 * border_width + 2 * region_padding;
+       if (bar_enabled && r->ws->bar_enabled) {
+               if (!bar_at_bottom)
+                       g.y += bar_height;
+               g.h -= bar_height;
+       }
+
+       DNPRINTF(SWM_D_STACK, "stack: workspace: %d (screen: "
+           "%d, region: %d), (x,y) WxH: (%d,%d) %d x %d\n",
+           r->ws->idx, r->s->idx, get_region_index(r), g.x, g.y, g.w, g.h);
+
+       r_prev = TAILQ_PREV(r, swm_region_list, entry);
+       if (r_prev) {
+               /* Stack bar/input relative to prev. region. */
+               val[1] = XCB_STACK_MODE_ABOVE;
+
+               val[0] = r_prev->id;
+               DNPRINTF(SWM_D_STACK, "stack: region input %#x "
+                   "relative to %#x.\n", r->id, val[0]);
+               xcb_configure_window(conn, r->id,
+                   XCB_CONFIG_WINDOW_SIBLING |
+                   XCB_CONFIG_WINDOW_STACK_MODE, val);
 
-                       r->ws->cur_layout->l_stack(r->ws, &g);
-                       r->ws->cur_layout->l_string(r->ws);
-                       /* save r so we can track region changes */
-                       r->ws->old_r = r;
-                       r_prev = r;
-               }
+               val[0] = r_prev->bar->id;
+               DNPRINTF(SWM_D_STACK, "stack: region bar %#x "
+                   "relative to %#x.\n", r->bar->id, val[0]);
+               xcb_configure_window(conn, r->bar->id,
+                   XCB_CONFIG_WINDOW_SIBLING |
+                   XCB_CONFIG_WINDOW_STACK_MODE, val);
        }
+
+       r->ws->cur_layout->l_stack(r->ws, &g);
+       r->ws->cur_layout->l_string(r->ws);
+       /* save r so we can track region changes */
+       r->ws->old_r = r;
+
        if (font_adjusted)
                font_adjusted--;
 
@@ -4798,6 +5040,9 @@ update_floater(struct ws_win *win)
                }
 
                if (win->bordered) {
+                       /* Window geometry excludes frame. */
+                       X(win) += border_width;
+                       Y(win) += border_width;
                        HEIGHT(win) -= 2 * border_width;
                        WIDTH(win) -= 2 * border_width;
                }
@@ -4826,10 +5071,8 @@ update_floater(struct ws_win *win)
                                 * unless manually moved, resized or ANYWHERE
                                 * quirk is set.
                                 */
-                               X(win) = X(r) + (WIDTH(r) - WIDTH(win)) / 2 -
-                                   BORDER(win);
-                               Y(win) = Y(r) + (HEIGHT(r) - HEIGHT(win)) / 2 -
-                                   BORDER(win);
+                               X(win) = X(r) + (WIDTH(r) - WIDTH(win)) / 2;
+                               Y(win) = Y(r) + (HEIGHT(r) - HEIGHT(win)) / 2;
                                store_float_geom(win);
                        }
                }
@@ -4878,7 +5121,7 @@ adjust_font(struct ws_win *win)
 void
 stack_master(struct workspace *ws, struct swm_geometry *g, int rot, bool flip)
 {
-       struct swm_geometry     win_g, r_g = *g;
+       struct swm_geometry     cell, r_g = *g;
        struct ws_win           *win;
        int                     i = 0, j = 0, s = 0, stacks = 0;
        int                     w_inc = 1, h_inc, w_base = 1, h_base;
@@ -4888,9 +5131,18 @@ stack_master(struct workspace *ws, struct swm_geometry *g, int rot, bool flip)
        int                     remain, missing, v_slice, mscale;
        bool                    bordered = true, reconfigure = false;
 
+       /*
+        * cell: geometry for window, including frame.
+        * mwin: # of windows in master area.
+        * mscale: size increment of master area.
+        * stacks: # of stack columns
+        */
+
        DNPRINTF(SWM_D_STACK, "stack_master: workspace: %d, rot: %s, "
            "flip: %s\n", ws->idx, YESNO(rot), YESNO(flip));
 
+       memset(&cell, 0, sizeof(cell));
+
        /* Prepare tiling variables, if needed. */
        if ((winno = count_win(ws, false)) > 0) {
                /* Find first tiled window. */
@@ -4913,7 +5165,10 @@ stack_master(struct workspace *ws, struct swm_geometry *g, int rot, bool flip)
                        mscale = ws->l_state.vertical_msize;
                        stacks = ws->l_state.vertical_stacks;
                }
-               win_g = r_g;
+
+               cell = r_g;
+               cell.x += border_width;
+               cell.y += border_width;
 
                if (stacks > winno - mwin)
                        stacks = winno - mwin;
@@ -4926,26 +5181,27 @@ stack_master(struct workspace *ws, struct swm_geometry *g, int rot, bool flip)
 
                        split = mwin;
                        colno = split;
-                       win_g.w = v_slice * mscale;
+                       cell.w = v_slice * mscale;
 
                        if (w_inc > 1 && w_inc < v_slice) {
                                /* Adjust for requested size increment. */
-                               remain = (win_g.w - w_base) % w_inc;
-                               win_g.w -= remain;
+                               remain = (cell.w - w_base) % w_inc;
+                               cell.w -= remain;
                        }
 
-                       msize = win_g.w;
+                       msize = cell.w;
                        if (flip)
-                               win_g.x += r_g.w - msize;
+                               cell.x += r_g.w - msize;
                } else {
                        msize = -2;
                        colno = split = winno / stacks;
-                       win_g.w = ((r_g.w - (stacks * 2 * border_width) +
+                       cell.w = ((r_g.w - (stacks * 2 * border_width) +
                            2 * border_width) / stacks);
                }
+
                hrh = r_g.h / colno;
                extra = r_g.h - (colno * hrh);
-               win_g.h = hrh - 2 * border_width;
+               cell.h = hrh - 2 * border_width;
                i = j = 0, s = stacks;
        }
 
@@ -4969,24 +5225,24 @@ stack_master(struct workspace *ws, struct swm_geometry *g, int rot, bool flip)
                        extra = r_g.h - (colno * hrh);
 
                        if (!flip)
-                               win_g.x += win_g.w + 2 * border_width +
+                               cell.x += cell.w + 2 * border_width +
                                    tile_gap;
 
-                       win_g.w = (r_g.w - msize -
+                       cell.w = (r_g.w - msize -
                            (stacks * (2 * border_width + tile_gap))) / stacks;
                        if (s == 1)
-                               win_g.w += (r_g.w - msize -
+                               cell.w += (r_g.w - msize -
                                    (stacks * (2 * border_width + tile_gap))) %
                                    stacks;
 
                        if (flip)
-                               win_g.x -= win_g.w + 2 * border_width +
+                               cell.x -= cell.w + 2 * border_width +
                                    tile_gap;
                        s--;
                        j = 0;
                }
 
-               win_g.h = hrh - 2 * border_width - tile_gap;
+               cell.h = hrh - 2 * border_width - tile_gap;
 
                if (rot) {
                        h_inc = win->sh.width_inc;
@@ -4997,55 +5253,62 @@ stack_master(struct workspace *ws, struct swm_geometry *g, int rot, bool flip)
                }
 
                if (j == colno - 1) {
-                       win_g.h = hrh + extra;
+                       cell.h = hrh + extra;
                } else if (h_inc > 1 && h_inc < h_slice) {
                        /* adjust for window's requested size increment */
-                       remain = (win_g.h - h_base) % h_inc;
+                       remain = (cell.h - h_base) % h_inc;
                        missing = h_inc - remain;
 
                        if (missing <= extra || j == 0) {
                                extra -= missing;
-                               win_g.h += missing;
+                               cell.h += missing;
                        } else {
-                               win_g.h -= remain;
+                               cell.h -= remain;
                                extra += remain;
                        }
                }
 
                if (j == 0)
-                       win_g.y = r_g.y;
+                       cell.y = r_g.y + border_width;
                else
-                       win_g.y += last_h + 2 * border_width + tile_gap;
+                       cell.y += last_h + 2 * border_width + tile_gap;
 
-               if (disable_border && !(bar_enabled && ws->bar_enabled) &&
-                   winno == 1) {
-                       bordered = false;
-                       win_g.w += 2 * border_width;
-                       win_g.h += 2 * border_width;
-               } else {
+               /* Window coordinates exclude frame. */
+
+               if (winno > 1 || !disable_border ||
+                   (bar_enabled && ws->bar_enabled)) {
                        bordered = true;
+               } else {
+                       bordered = false;
                }
 
                if (rot) {
-                       if (X(win) != win_g.y || Y(win) != win_g.x ||
-                           WIDTH(win) != win_g.h || HEIGHT(win) != win_g.w) {
+                       if (X(win) != cell.y || Y(win) != cell.x ||
+                           WIDTH(win) != cell.h || HEIGHT(win) != cell.w) {
                                reconfigure = true;
-                               X(win) = win_g.y;
-                               Y(win) = win_g.x;
-                               WIDTH(win) = win_g.h;
-                               HEIGHT(win) = win_g.w;
+                               X(win) = cell.y;
+                               Y(win) = cell.x;
+                               WIDTH(win) = cell.h;
+                               HEIGHT(win) = cell.w;
                        }
                } else {
-                       if (X(win) != win_g.x || Y(win) != win_g.y ||
-                           WIDTH(win) != win_g.w || HEIGHT(win) != win_g.h) {
+                       if (X(win) != cell.x || Y(win) != cell.y ||
+                           WIDTH(win) != cell.w || HEIGHT(win) != cell.h) {
                                reconfigure = true;
-                               X(win) = win_g.x;
-                               Y(win) = win_g.y;
-                               WIDTH(win) = win_g.w;
-                               HEIGHT(win) = win_g.h;
+                               X(win) = cell.x;
+                               Y(win) = cell.y;
+                               WIDTH(win) = cell.w;
+                               HEIGHT(win) = cell.h;
                        }
                }
 
+               if (!bordered) {
+                       X(win) -= border_width;
+                       Y(win) -= border_width;
+                       WIDTH(win) += 2 * border_width;
+                       HEIGHT(win) += 2 * border_width;
+               }
+
                if (bordered != win->bordered) {
                        reconfigure = true;
                        win->bordered = bordered;
@@ -5056,7 +5319,7 @@ stack_master(struct workspace *ws, struct swm_geometry *g, int rot, bool flip)
                        update_window(win);
                }
 
-               last_h = win_g.h;
+               last_h = cell.h;
                i++;
                j++;
        }
@@ -5244,6 +5507,8 @@ max_stack(struct workspace *ws, struct swm_geometry *g)
                                HEIGHT(w) += 2 * border_width;
                        } else {
                                w->bordered = true;
+                               X(w) += border_width;
+                               Y(w) += border_width;
                        }
 
                        update_window(w);
@@ -5347,7 +5612,7 @@ send_to_ws(struct binding *bp, struct swm_region *r, union arg *args)
                win->ws->focus_pending = NULL;
 
                if (win->ws->focus_prev)
-                       update_window_color(win->ws->focus_prev);
+                       draw_frame(win->ws->focus_prev);
        }
 
        DNPRINTF(SWM_D_STACK, "send_to_ws: focus_pending: %#x, focus: %#x, "
@@ -5359,19 +5624,27 @@ send_to_ws(struct binding *bp, struct swm_region *r, union arg *args)
        ewmh_apply_flags(win, win->ewmh_flags & ~EWMH_F_MAXIMIZED);
        ewmh_update_wm_state(win);
 
-       /* Restack and set new focus on current ws. */
-       if (FLOATING(win))
-               load_float_geom(win);
+       /* Restack focused region. */
+       stack(r);
+
+       /* If destination ws has a region, restack. */
+       if (win->ws->r) {
+               if (FLOATING(win))
+                       load_float_geom(win);
 
-       stack();
+               stack(win->ws->r);
+       }
 
+       /* Set new focus on current ws. */
        if (focus_mode != SWM_FOCUS_FOLLOW) {
                if (r->ws->focus != NULL) {
                        focus_win(r->ws->focus);
                } else {
-                       xcb_set_input_focus(conn, XCB_INPUT_FOCUS_PARENT, r->id,
-                           XCB_CURRENT_TIME);
-                       bar_draw();
+                       DNPRINTF(SWM_D_FOCUS, "send_to_ws: set_input_focus: "
+                           "%#x, revert-to: parent, time: 0\n", r->id);
+                       xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT,
+                           r->id, XCB_CURRENT_TIME);
+                       bar_draw(r->bar);
                }
        }
 
@@ -5379,6 +5652,30 @@ send_to_ws(struct binding *bp, struct swm_region *r, union arg *args)
        focus_flush();
 }
 
+/* Transfer focused window to region-relative workspace and focus. */
+void
+send_to_rg_relative(struct binding *bp, struct swm_region *r, union arg *args)
+{
+       union arg               args_abs;
+       struct swm_region       *r_other;
+
+       if (args->id == 1) {
+               r_other = TAILQ_NEXT(r, entry);
+               if (r_other == NULL)
+                       r_other = TAILQ_FIRST(&r->s->rl);
+       } else {
+               r_other = TAILQ_PREV(r, swm_region_list, entry);
+               if (r_other == NULL)
+                       r_other = TAILQ_LAST(&r->s->rl, swm_region_list);
+       }
+
+       /* Map relative to absolute */
+       args_abs = *args;
+       args_abs.id = r_other->ws->idx;
+
+       send_to_ws(bp, r, &args_abs);
+}
+
 void
 win_to_ws(struct ws_win *win, int wsid, bool unfocus)
 {
@@ -5486,6 +5783,31 @@ pressbutton(struct binding *bp, struct swm_region *r, union arg *args)
            XCB_CURRENT_TIME, XCB_WINDOW_NONE, 0, 0, 0);
 }
 
+void
+raise_focus(struct binding *bp, struct swm_region *r, union arg *args)
+{
+       struct ws_win   *win;
+       uint32_t        val;
+
+       /* Suppress warning. */
+       (void)bp;
+       (void)args;
+
+       if (r == NULL || r->ws == NULL || r->ws->focus == NULL)
+               return;
+
+       win = r->ws->focus;
+       r->ws->focus_raise = win;
+       raise_window(win);
+
+       /* Temporarily override stacking order also in the stack */
+       if (!FLOATING(win)) {
+               val = XCB_STACK_MODE_ABOVE;
+               xcb_configure_window(conn, win->frame,
+                   XCB_CONFIG_WINDOW_STACK_MODE, &val);
+       }
+}
+
 void
 raise_toggle(struct binding *bp, struct swm_region *r, union arg *args)
 {
@@ -5522,7 +5844,7 @@ iconify(struct binding *bp, struct swm_region *r, union arg *args)
        ewmh_apply_flags(w, w->ewmh_flags | EWMH_F_HIDDEN);
        ewmh_update_wm_state(w);
 
-       stack();
+       stack(r);
 
        focus_flush();
 }
@@ -5749,7 +6071,7 @@ search_win(struct binding *bp, struct swm_region *r, union arg *args)
                        height = bar_font->height + 4;
                }
 
-               xcb_create_window(conn, screen->root_depth, w, win->id, 0, 0,
+               xcb_create_window(conn, screen->root_depth, w, win->frame, 0, 0,
                    width, height, 1, XCB_WINDOW_CLASS_INPUT_OUTPUT,
                    screen->root_visual, XCB_CW_BACK_PIXEL |
                    XCB_CW_BORDER_PIXEL | XCB_CW_COLORMAP, wa);
@@ -5817,7 +6139,7 @@ search_resp_uniconify(const char *resp, size_t len)
                        /* XXX this should be a callback to generalize */
                        ewmh_apply_flags(win, win->ewmh_flags & ~EWMH_F_HIDDEN);
                        ewmh_update_wm_state(win);
-                       stack();
+                       stack(search_r);
                        free(s);
                        break;
                }
@@ -5868,7 +6190,7 @@ ewmh_update_desktop_names(void)
                        ++len;
                }
 
-               if((name_list = calloc(len, sizeof(char))) == NULL)
+               if ((name_list = calloc(len, sizeof(char))) == NULL)
                        err(1, "update_desktop_names: calloc: failed to "
                            "allocate memory.");
 
@@ -6203,7 +6525,7 @@ maximize_toggle(struct binding *bp, struct swm_region *r, union arg *args)
        ewmh_apply_flags(w, w->ewmh_flags ^ EWMH_F_MAXIMIZED);
        ewmh_update_wm_state(w);
 
-       stack();
+       stack(r);
 
        if (w == w->ws->focus)
                focus_win(w);
@@ -6236,7 +6558,7 @@ floating_toggle(struct binding *bp, struct swm_region *r, union arg *args)
        ewmh_apply_flags(w, w->ewmh_flags ^ EWMH_F_ABOVE);
        ewmh_update_wm_state(w);
 
-       stack();
+       stack(r);
 
        if (w == w->ws->focus)
                focus_win(w);
@@ -6246,6 +6568,32 @@ floating_toggle(struct binding *bp, struct swm_region *r, union arg *args)
        DNPRINTF(SWM_D_MISC, "floating_toggle: done\n");
 }
 
+void
+fullscreen_toggle(struct binding *bp, struct swm_region *r, union arg *args)
+{
+       struct ws_win           *w = r->ws->focus;
+
+       /* suppress unused warning since var is needed */
+       (void)bp;
+       (void)args;
+
+       if (w == NULL)
+               return;
+
+       DNPRINTF(SWM_D_MISC, "fullscreen_toggle: win %#x\n", w->id);
+
+       ewmh_apply_flags(w, w->ewmh_flags ^ EWMH_F_FULLSCREEN);
+       ewmh_update_wm_state(w);
+
+       stack(r);
+
+       if (w == w->ws->focus)
+               focus_win(w);
+
+       center_pointer(r);
+       focus_flush();
+       DNPRINTF(SWM_D_MISC, "fullscreen_toggle: done\n");
+}
 void
 region_containment(struct ws_win *win, struct swm_region *r, int opts)
 {
@@ -6259,10 +6607,10 @@ region_containment(struct ws_win *win, struct swm_region *r, int opts)
         * side of the region boundary.  Positive values indicate the side of
         * the window has passed beyond the region boundary.
         */
-       rt = opts & SWM_CW_RIGHT ? MAX_X(win) + BORDER(win) - MAX_X(r) : bw;
-       lt = opts & SWM_CW_LEFT ? X(r) - X(win) + BORDER(win) : bw;
-       bm = opts & SWM_CW_BOTTOM ? MAX_Y(win) + BORDER(win) - MAX_Y(r) : bw;
-       tp = opts & SWM_CW_TOP ? Y(r) - Y(win) + BORDER(win) : bw;
+       rt = opts & SWM_CW_RIGHT ? MAX_X(win) - MAX_X(r) : bw;
+       lt = opts & SWM_CW_LEFT ? X(r) - X(win) : bw;
+       bm = opts & SWM_CW_BOTTOM ? MAX_Y(win) - MAX_Y(r) : bw;
+       tp = opts & SWM_CW_TOP ? Y(r) - Y(win) : bw;
 
        DNPRINTF(SWM_D_MISC, "region_containment: win %#x, rt: %d, lt: %d, "
            "bm: %d, tp: %d, SOFTBOUNDARY: %s, HARDBOUNDARY: %s\n", win->id, rt,
@@ -6297,32 +6645,32 @@ constrain_window(struct ws_win *win, struct swm_geometry *b, int *opts)
            YESNO(*opts & SWM_CW_BOTTOM), YESNO(*opts & SWM_CW_TOP),
            YESNO(*opts & SWM_CW_RESIZABLE));
 
-       if ((*opts & SWM_CW_RIGHT) && MAX_X(win) + BORDER(win) > b->x + b->w) {
+       if ((*opts & SWM_CW_RIGHT) && MAX_X(win) > b->x + b->w) {
                if (*opts & SWM_CW_RESIZABLE)
-                       WIDTH(win) = b->x + b->w - X(win) - BORDER(win);
+                       WIDTH(win) = b->x + b->w - X(win);
                else
-                       X(win) = b->x + b->w - WIDTH(win) - BORDER(win);
+                       X(win) = b->x + b->w - WIDTH(win);
        }
 
-       if ((*opts & SWM_CW_LEFT) && X(win) + BORDER(win) < b->x) {
+       if ((*opts & SWM_CW_LEFT) && X(win) < b->x) {
                if (*opts & SWM_CW_RESIZABLE)
-                       WIDTH(win) -= b->x - X(win) - BORDER(win);
+                       WIDTH(win) -= b->x - X(win);
 
-               X(win) = b->x - BORDER(win);
+               X(win) = b->x;
        }
 
-       if ((*opts & SWM_CW_BOTTOM) && MAX_Y(win) + BORDER(win) > b->y + b->h) {
+       if ((*opts & SWM_CW_BOTTOM) && MAX_Y(win) > b->y + b->h) {
                if (*opts & SWM_CW_RESIZABLE)
-                       HEIGHT(win) = b->y + b->h - Y(win) - BORDER(win);
+                       HEIGHT(win) = b->y + b->h - Y(win);
                else
-                       Y(win) = b->y + b->h - HEIGHT(win) - BORDER(win);
+                       Y(win) = b->y + b->h - HEIGHT(win);
        }
 
-       if ((*opts & SWM_CW_TOP) && Y(win) + BORDER(win) < b->y) {
+       if ((*opts & SWM_CW_TOP) && Y(win) < b->y) {
                if (*opts & SWM_CW_RESIZABLE)
-                       HEIGHT(win) -= b->y - Y(win) - BORDER(win);
+                       HEIGHT(win) -= b->y - Y(win);
 
-               Y(win) = b->y - BORDER(win);
+               Y(win) = b->y;
        }
 
        if (*opts & SWM_CW_RESIZABLE) {
@@ -6334,9 +6682,22 @@ constrain_window(struct ws_win *win, struct swm_geometry *b, int *opts)
 }
 
 void
-update_window_color(struct ws_win *win)
+draw_frame(struct ws_win *win)
 {
-       uint32_t        *pixel;
+       xcb_rectangle_t         rect[4];
+       uint32_t                *pixel;
+       int                     n = 0;
+
+       if (win->frame == XCB_WINDOW_NONE) {
+               DNPRINTF(SWM_D_EVENT, "draw_frame: win %#x not "
+                   "reparented.\n", win->id);
+               return;
+       }
+
+       if (!win->bordered) {
+               DNPRINTF(SWM_D_EVENT, "draw_frame: win %#x frame "
+                   "disabled\n", win->id);
+       }
 
        if (WS_FOCUSED(win->ws) && win->ws->focus == win)
                pixel = MAXIMIZED(win) ?
@@ -6347,8 +6708,29 @@ update_window_color(struct ws_win *win)
                    &win->s->c[SWM_S_COLOR_UNFOCUS_MAXIMIZED].pixel :
                    &win->s->c[SWM_S_COLOR_UNFOCUS].pixel;
 
-       xcb_change_window_attributes(conn, win->id,
-           XCB_CW_BORDER_PIXEL, pixel);
+       /* Top (with corners) */
+       rect[n].x = 0;
+       rect[n].y = 0;
+       rect[n].width = WIDTH(win) + 2 * border_width;
+       rect[n].height = border_width;
+       /* Left (without corners) */
+       rect[++n].x = 0;
+       rect[n].y = border_width;
+       rect[n].width = border_width;
+       rect[n].height = HEIGHT(win);
+       /* Right (without corners) */
+       rect[++n].x = border_width + WIDTH(win);
+       rect[n].y = border_width;
+       rect[n].width = border_width;
+       rect[n].height = HEIGHT(win);
+       /* Bottom (with corners)*/
+       rect[++n].x = 0;
+       rect[n].y = border_width + HEIGHT(win);
+       rect[n].width = WIDTH(win) + 2 * border_width;
+       rect[n].height = border_width;
+
+       xcb_change_gc(conn, win->s->bar_gc, XCB_GC_FOREGROUND, pixel);
+       xcb_poly_fill_rectangle(conn, win->frame, win->s->bar_gc, 4, rect);
 }
 
 void
@@ -6357,20 +6739,56 @@ update_window(struct ws_win *win)
        uint16_t        mask;
        uint32_t        wc[5];
 
+       if (win->frame == XCB_WINDOW_NONE) {
+               DNPRINTF(SWM_D_EVENT, "update_window: skip win %#x; "
+                   "not reparented.\n", win->id);
+               return;
+       }
+
        mask = XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y |
            XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT |
            XCB_CONFIG_WINDOW_BORDER_WIDTH;
-       wc[0] = X(win);
-       wc[1] = Y(win);
+
+       /* Reconfigure frame. */
+       if (win->bordered) {
+               wc[0] = X(win) - border_width;
+               wc[1] = Y(win) - border_width;
+               wc[2] = WIDTH(win) + 2 * border_width;
+               wc[3] = HEIGHT(win) + 2 * border_width;
+       } else {
+               wc[0] = X(win);
+               wc[1] = Y(win);
+               wc[2] = WIDTH(win);
+               wc[3] = HEIGHT(win);
+       }
+
+       wc[4] = 0;
+
+       DNPRINTF(SWM_D_EVENT, "update_window: win %#x frame %#x, (x,y) w x h: "
+           "(%d,%d) %d x %d, bordered: %s\n", win->id, win->frame, wc[0],
+           wc[1], wc[2], wc[3], YESNO(win->bordered));
+
+       xcb_configure_window(conn, win->frame, mask, wc);
+
+       /* Reconfigure client window. */
+       wc[0] = wc[1] = win->bordered ? border_width : 0;
        wc[2] = WIDTH(win);
        wc[3] = HEIGHT(win);
-       wc[4] = BORDER(win);
 
        DNPRINTF(SWM_D_EVENT, "update_window: win %#x, (x,y) w x h: "
            "(%d,%d) %d x %d, bordered: %s\n", win->id, wc[0], wc[1], wc[2],
            wc[3], YESNO(win->bordered));
-
        xcb_configure_window(conn, win->id, mask, wc);
+
+       /* ICCCM 4.2.3 Synthetic ConfigureNotify when moved and not resized. */
+       if ((X(win) != win->g_prev.x || Y(win) != win->g_prev.y) &&
+           (win->java || (WIDTH(win) == win->g_prev.w &&
+           HEIGHT(win) == win->g_prev.h))) {
+               /* Java has special needs when moved together with a resize. */
+               config_win(win, NULL);
+       }
+
+       win->g_prev = win->g;
 }
 
 struct event {
@@ -6380,7 +6798,7 @@ struct event {
 SIMPLEQ_HEAD(event_queue, event) events = SIMPLEQ_HEAD_INITIALIZER(events);
 
 xcb_generic_event_t *
-get_next_event(bool wait)
+get_next_event(bool dowait)
 {
        struct event            *ep;
        xcb_generic_event_t     *evt;
@@ -6390,7 +6808,7 @@ get_next_event(bool wait)
                evt = ep->ev;
                SIMPLEQ_REMOVE_HEAD(&events, entry);
                free(ep);
-       } else if (wait)
+       } else if (dowait)
                evt = xcb_wait_for_event(conn);
        else
                evt = xcb_poll_for_event(conn);
@@ -6479,7 +6897,7 @@ resize_win(struct ws_win *win, struct binding *bp, int opt)
            ~EWMH_F_MAXIMIZED);
        ewmh_update_wm_state(win);
 
-       stack();
+       stack(r);
 
        focus_flush();
 
@@ -6709,12 +7127,15 @@ regionize(struct ws_win *win, int x, int y)
                r = region_under(win->s, X(win) + WIDTH(win) / 2,
                    Y(win) + HEIGHT(win) / 2);
 
-       if (r != NULL && r != win->ws->r) {
-               if (clear_maximized(r->ws) > 0)
-                       stack();
+       if (r && r != win->ws->r) {
+               clear_maximized(r->ws);
 
                win_to_ws(win, r->ws->idx, false);
 
+               /* Stack old and new region. */
+               stack(r);
+               stack(win->ws->r);
+
                /* Set focus on new ws. */
                unfocus_win(r->ws->focus);
                r->ws->focus = win;
@@ -6762,7 +7183,7 @@ move_win(struct ws_win *win, struct binding *bp, int opt)
        ewmh_update_wm_state(win);
 
        if (restack)
-               stack();
+               stack(r);
 
        focus_flush();
 
@@ -6892,8 +7313,8 @@ move_win(struct ws_win *win, struct binding *bp, int opt)
        store_float_geom(win);
 
        /* New region set to fullscreen layout. */
-       if (win->ws->cur_layout == &layouts[SWM_MAX_STACK]) {
-               stack();
+       if (win->ws->r && win->ws->cur_layout == &layouts[SWM_MAX_STACK]) {
+               stack(win->ws->r);
                focus_flush();
        }
 
@@ -6916,6 +7337,10 @@ move(struct binding *bp, struct swm_region *r, union arg *args)
                /* move_* uses focus window. */
                if (r->ws)
                        win = r->ws->focus;
+
+               /* Disallow move_ on tiled. */
+               if (win && !(TRANS(win) || ABOVE(win)))
+                       return;
        } else {
                /* move uses window under pointer. */
                qpr = xcb_query_pointer_reply(conn,
@@ -6942,7 +7367,7 @@ struct action {
                                    union arg *);
        uint32_t                flags;
        union arg               args;
-} actions[FN_INVALID + 1] = {
+} actions[FN_INVALID + 2] = {
        /* name                 function        argument */
        { "bar_toggle",         bar_toggle,     0, {.id = SWM_ARG_ID_BAR_TOGGLE} },
        { "bar_toggle_ws",      bar_toggle,     0, {.id = SWM_ARG_ID_BAR_TOGGLE_WS} },
@@ -6955,6 +7380,7 @@ struct action {
        { "focus_next",         focus,          0, {.id = SWM_ARG_ID_FOCUSNEXT} },
        { "focus_prev",         focus,          0, {.id = SWM_ARG_ID_FOCUSPREV} },
        { "focus_urgent",       focus,          0, {.id = SWM_ARG_ID_FOCUSURGENT} },
+       { "fullscreen_toggle",  fullscreen_toggle, 0, {0} },
        { "maximize_toggle",    maximize_toggle,0, {0} },
        { "height_grow",        resize,         0, {.id = SWM_ARG_ID_HEIGHTGROW} },
        { "height_shrink",      resize,         0, {.id = SWM_ARG_ID_HEIGHTSHRINK} },
@@ -6977,6 +7403,8 @@ struct action {
        { "mvrg_7",             send_to_rg,     0, {.id = 6} },
        { "mvrg_8",             send_to_rg,     0, {.id = 7} },
        { "mvrg_9",             send_to_rg,     0, {.id = 8} },
+       { "mvrg_next",          send_to_rg_relative,    0, {.id = 1} },
+       { "mvrg_prev",          send_to_rg_relative,    0, {.id = -1} },
        { "mvws_1",             send_to_ws,     0, {.id = 0} },
        { "mvws_2",             send_to_ws,     0, {.id = 1} },
        { "mvws_3",             send_to_ws,     0, {.id = 2} },
@@ -7001,6 +7429,7 @@ struct action {
        { "mvws_22",            send_to_ws,     0, {.id = 21} },
        { "name_workspace",     name_workspace, 0, {0} },
        { "quit",               quit,           0, {0} },
+       { "raise",              raise_focus,    0, {0} },
        { "raise_toggle",       raise_toggle,   0, {0} },
        { "resize",             resize, FN_F_NOREPLAY, {.id = SWM_ARG_ID_DONTCENTER} },
        { "resize_centered",    resize, FN_F_NOREPLAY, {.id = SWM_ARG_ID_CENTER} },
@@ -7065,7 +7494,10 @@ struct action {
        { "ws_prev_all",        cyclews,        0, {.id = SWM_ARG_ID_CYCLEWS_DOWN_ALL} },
        { "ws_prev_move",       cyclews,        0, {.id = SWM_ARG_ID_CYCLEWS_MOVE_DOWN} },
        { "ws_prior",           priorws,        0, {0} },
-       { "dumpwins",           dumpwins,       0, {0} }, /* MUST BE LAST */
+       /* SWM_DEBUG actions MUST be here: */
+       { "debug_toggle",       debug_toggle,   0, {0} },
+       { "dumpwins",           dumpwins,       0, {0} },
+       /* ALWAYS last: */
        { "invalid action",     NULL,           0, {0} },
 };
 
@@ -7735,6 +8167,7 @@ setup_keybindings(void)
        BINDKEY(MODKEY,         XK_k,                   FN_FOCUS_PREV);
        BINDKEY(MODSHIFT,       XK_Tab,                 FN_FOCUS_PREV);
        BINDKEY(MODKEY,         XK_u,                   FN_FOCUS_URGENT);
+       BINDKEY(MODSHIFT,       XK_e,                   FN_FULLSCREEN_TOGGLE);
        BINDKEY(MODKEY,         XK_e,                   FN_MAXIMIZE_TOGGLE);
        BINDKEY(MODSHIFT,       XK_equal,               FN_HEIGHT_GROW);
        BINDKEY(MODSHIFT,       XK_minus,               FN_HEIGHT_SHRINK);
@@ -7780,6 +8213,7 @@ setup_keybindings(void)
        BINDKEY(MODSHIFT,       XK_F12,                 FN_MVWS_22);
        BINDKEY(MODSHIFT,       XK_slash,               FN_NAME_WORKSPACE);
        BINDKEY(MODSHIFT,       XK_q,                   FN_QUIT);
+       BINDKEY(MODKEY,         XK_r,                   FN_RAISE);
        BINDKEY(MODSHIFT,       XK_r,                   FN_RAISE_TOGGLE);
        BINDKEY(MODKEY,         XK_q,                   FN_RESTART);
        BINDKEY(MODKEY,         XK_KP_End,              FN_RG_1);
@@ -7843,6 +8277,7 @@ setup_keybindings(void)
        BINDKEY(MODSHIFT,       XK_Down,                FN_WS_PREV_MOVE);
        BINDKEY(MODKEY,         XK_a,                   FN_WS_PRIOR);
 #ifdef SWM_DEBUG
+       BINDKEY(MODKEY,         XK_d,                   FN_DEBUG_TOGGLE);
        BINDKEY(MODSHIFT,       XK_d,                   FN_DUMPWINS);
 #endif
 #undef BINDKEY
@@ -7998,7 +8433,7 @@ grabkeys(void)
                                for (j = 0; j < LENGTH(modifiers); j++) {
                                        DNPRINTF(SWM_D_MOUSE, "grabkeys: grab, "
                                            "key: %u, modifiers: %d\n",
-                                           bp->value, bp->mod);
+                                           bp->value, bp->mod | modifiers[j]);
                                        xcb_grab_key(conn, 1,
                                            screens[k].root,
                                            bp->mod | modifiers[j],
@@ -8828,7 +9263,6 @@ setautorun(const char *selector, const char *value, int flags)
                        err(1, "setautorun: realloc");
                a.argv[argc - 1] = ap;
        }
-       free(str);
 
        if ((a.argv = realloc(a.argv, (argc + 1) * sizeof(char *))) == NULL)
                err(1, "setautorun: realloc");
@@ -8840,6 +9274,7 @@ setautorun(const char *selector, const char *value, int flags)
                _exit(1);
        }
        free(a.argv);
+       free(str);
 
        /* parent */
        p = find_pid(pid);
@@ -9311,18 +9746,84 @@ get_ws_idx(struct ws_win *win)
        return ws_idx;
 }
 
-struct ws_win *
-manage_window(xcb_window_t id, int spawn_pos, bool mapped)
+void
+reparent_window(struct ws_win *win)
 {
-       struct ws_win           *win, *ww;
-       struct swm_region       *r;
-       struct pid_e            *p;
-       struct quirk            *qp;
-       xcb_get_geometry_reply_t        *gr;
-       xcb_window_t            trans = XCB_WINDOW_NONE;
-       uint32_t                i, wa[2], new_flags;
-       int                     ws_idx, force_ws = -1;
-       char                    *class, *instance, *name;
+       xcb_void_cookie_t       c;
+       xcb_generic_error_t     *error;
+       uint32_t                wa[2];
+
+       win->frame = xcb_generate_id(conn);
+
+       DNPRINTF(SWM_D_MISC, "reparent_window: win %#x, frame: %#x\n",
+           win->id, win->frame);
+
+       wa[0] =
+           XCB_EVENT_MASK_ENTER_WINDOW |
+           XCB_EVENT_MASK_STRUCTURE_NOTIFY |
+           XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT |
+           XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY |
+           XCB_EVENT_MASK_EXPOSURE;
+#ifdef SWM_DEBUG
+       wa[0] |= XCB_EVENT_MASK_LEAVE_WINDOW | XCB_EVENT_MASK_FOCUS_CHANGE;
+#endif
+
+       xcb_create_window(conn, XCB_COPY_FROM_PARENT, win->frame, win->s->root,
+           X(win), Y(win), WIDTH(win), HEIGHT(win), 0,
+           XCB_WINDOW_CLASS_INPUT_OUTPUT, XCB_COPY_FROM_PARENT,
+           XCB_CW_EVENT_MASK, wa);
+
+       win->state = SWM_WIN_STATE_REPARENTING;
+       c = xcb_reparent_window_checked(conn, win->id, win->frame, 0, 0);
+       if ((error = xcb_request_check(conn, c))) {
+               DNPRINTF(SWM_D_MISC, "reparent_window: error:\n");
+               event_error(error);
+               free(error);
+
+               /* Abort. */
+               xcb_destroy_window(conn, win->frame);
+               win->frame = XCB_WINDOW_NONE;
+       } else {
+               xcb_change_save_set(conn, XCB_SET_MODE_INSERT, win->id);
+       }
+       DNPRINTF(SWM_D_MISC, "reparent_window: done.\n");
+}
+
+void
+unparent_window(struct ws_win *win)
+{
+       xcb_change_save_set(conn, XCB_SET_MODE_DELETE, win->id);
+       xcb_reparent_window(conn, win->id, win->s->root, X(win), Y(win));
+       xcb_destroy_window(conn, win->frame);
+       win->frame = XCB_WINDOW_NONE;
+       win->state = SWM_WIN_STATE_UNPARENTING;
+}
+
+struct ws_win *
+manage_window(xcb_window_t id, int spawn_pos, bool mapping)
+{
+       struct ws_win                           *win = NULL, *ww;
+       struct swm_region                       *r;
+       struct pid_e                            *p;
+       struct quirk                            *qp;
+       xcb_get_geometry_reply_t                *gr;
+       xcb_get_window_attributes_reply_t       *war = NULL;
+       xcb_window_t                            trans = XCB_WINDOW_NONE;
+       uint32_t                                i, wa[1], new_flags;
+       int                                     ws_idx, force_ws = -1;
+       char                                    *class, *instance, *name;
+
+       if (find_bar(id)) {
+               DNPRINTF(SWM_D_MISC, "manage_window: win %#x is region bar; "
+                   "skipping.\n", id);
+               goto out;
+       }
+
+       if (find_region(id)) {
+               DNPRINTF(SWM_D_MISC, "manage_window: win %#x is region window; "
+                   "skipping.\n", id);
+               goto out;
+       }
 
        if ((win = find_window(id)) != NULL) {
                DNPRINTF(SWM_D_MISC, "manage_window: win %#x already "
@@ -9339,16 +9840,36 @@ manage_window(xcb_window_t id, int spawn_pos, bool mapped)
                if (TRANS(win))
                        set_child_transient(win, &trans);
 
-               goto out;
+               goto remanage;
        } else {
                DNPRINTF(SWM_D_MISC, "manage_window: win %#x is new.\n", id);
        }
 
+       war = xcb_get_window_attributes_reply(conn,
+           xcb_get_window_attributes(conn, id), NULL);
+       if (war == NULL) {
+               DNPRINTF(SWM_D_EVENT, "manage_window: window lost.\n");
+               goto out;
+       }
+
+       if (war->override_redirect) {
+               DNPRINTF(SWM_D_EVENT, "manage_window: override_redirect; "
+                   "skipping.\n");
+               goto out;
+       }
+
+       if (!mapping && war->map_state == XCB_MAP_STATE_UNMAPPED &&
+           get_win_state(id) == XCB_ICCCM_WM_STATE_WITHDRAWN) {
+               DNPRINTF(SWM_D_EVENT, "manage_window: window withdrawn; "
+                   "skipping.\n");
+               goto out;
+       }
+
        /* Try to get initial window geometry. */
        gr = xcb_get_geometry_reply(conn, xcb_get_geometry(conn, id), NULL);
        if (gr == NULL) {
                DNPRINTF(SWM_D_MISC, "manage_window: get geometry failed.\n");
-               return (NULL);
+               goto out;
        }
 
        /* Create and initialize ws_win object. */
@@ -9364,24 +9885,19 @@ manage_window(xcb_window_t id, int spawn_pos, bool mapped)
        /* Ignore window border if there is one. */
        WIDTH(win) = gr->width;
        HEIGHT(win) = gr->height;
-       X(win) = gr->x + gr->border_width - border_width;
-       Y(win) = gr->y + gr->border_width - border_width;
-       win->bordered = true;
-       win->mapped = mapped;
+       X(win) = gr->x + gr->border_width;
+       Y(win) = gr->y + gr->border_width;
+       win->bordered = false;
+       win->mapped = (war->map_state != XCB_MAP_STATE_UNMAPPED);
        win->s = r->s;  /* this never changes */
 
        free(gr);
 
        /* Select which X events to monitor and set border pixel color. */
-       wa[0] = win->s->c[SWM_S_COLOR_UNFOCUS].pixel;
-       wa[1] = XCB_EVENT_MASK_ENTER_WINDOW | XCB_EVENT_MASK_PROPERTY_CHANGE |
+       wa[0] = XCB_EVENT_MASK_ENTER_WINDOW | XCB_EVENT_MASK_PROPERTY_CHANGE |
            XCB_EVENT_MASK_STRUCTURE_NOTIFY;
-#ifdef SWM_DEBUG
-       wa[1] |= XCB_EVENT_MASK_LEAVE_WINDOW | XCB_EVENT_MASK_FOCUS_CHANGE;
-#endif
 
-       xcb_change_window_attributes(conn, win->id, XCB_CW_BORDER_PIXEL |
-           XCB_CW_EVENT_MASK, wa);
+       xcb_change_window_attributes(conn, win->id, XCB_CW_EVENT_MASK, wa);
 
        /* Get WM_SIZE_HINTS. */
        xcb_icccm_get_wm_normal_hints_reply(conn,
@@ -9407,7 +9923,8 @@ manage_window(xcb_window_t id, int spawn_pos, bool mapped)
 
 #ifdef SWM_DEBUG
        /* Must be after getting WM_HINTS and WM_PROTOCOLS. */
-       print_win_input_model(win);
+       DNPRINTF(SWM_D_FOCUS, "manage_window: input_model: %s\n",
+           get_win_input_model(win));
 #endif
 
        /* Set initial quirks based on EWMH. */
@@ -9494,7 +10011,7 @@ manage_window(xcb_window_t id, int spawn_pos, bool mapped)
                update_window(win);
        }
 
-out:
+remanage:
        /* Figure out where to insert the window in the workspace list. */
        if (trans && (ww = find_window(trans)))
                TAILQ_INSERT_AFTER(&win->ws->winlist, ww, win, entry);
@@ -9537,11 +10054,14 @@ out:
        /* Set initial _NET_WM_ALLOWED_ACTIONS */
        ewmh_update_actions(win);
 
+       reparent_window(win);
+
        DNPRINTF(SWM_D_MISC, "manage_window: done. win %#x, (x,y) w x h: "
            "(%d,%d) %d x %d, ws: %d, iconic: %s, transient: %#x\n", win->id,
            X(win), Y(win), WIDTH(win), HEIGHT(win), win->ws->idx,
            YESNO(ICONIC(win)), win->transient);
-
+out:
+       free(war);
        return (win);
 }
 
@@ -9565,18 +10085,13 @@ free_window(struct ws_win *win)
 void
 unmanage_window(struct ws_win *win)
 {
-       struct ws_win           *parent;
-
        DNPRINTF(SWM_D_MISC, "unmanage_window: win %#x\n", WINID(win));
 
        if (win == NULL)
                return;
 
-       if (TRANS(win)) {
-               parent = find_window(win->transient);
-               if (parent)
-                       parent->focus_child = NULL;
-       }
+       kill_refs(win);
+       unparent_window(win);
 
        TAILQ_REMOVE(&win->ws->stack, win, stack_entry);
        TAILQ_REMOVE(&win->ws->winlist, win, entry);
@@ -9588,29 +10103,57 @@ unmanage_window(struct ws_win *win)
 void
 expose(xcb_expose_event_t *e)
 {
-       int                     i, num_screens;
-       struct swm_region       *r;
+       struct ws_win           *w;
+       struct swm_bar          *b;
+#ifdef SWM_DEBUG
+       struct workspace        *ws;
+#endif
 
-       DNPRINTF(SWM_D_EVENT, "expose: win %#x\n", e->window);
+       DNPRINTF(SWM_D_EVENT, "expose: win %#x, count: %d\n", e->window,
+           e->count);
 
-       num_screens = get_screen_count();
-       for (i = 0; i < num_screens; i++)
-               TAILQ_FOREACH(r, &screens[i].rl, entry)
-                       if (e->window == WINID(r->bar))
-                               bar_draw();
+       if (e->count > 0)
+               return;
 
-       xcb_flush(conn);
+       if ((b = find_bar(e->window))) {
+               bar_draw(b);
+               xcb_flush(conn);
+       } else if ((w = find_window(e->window)) && w->frame == e->window) {
+               draw_frame(w);
+#ifdef SWM_DEBUG
+               ws = w->ws;
+               TAILQ_FOREACH(w, &ws->winlist, entry)
+                       debug_refresh(w);
+#endif
+               xcb_flush(conn);
+       }
+
+       DNPRINTF(SWM_D_EVENT, "expose: done\n");
 }
 
-#ifdef SWM_DEBUG
 void
 focusin(xcb_focus_in_event_t *e)
 {
+       struct ws_win           *win;
+
        DNPRINTF(SWM_D_EVENT, "focusin: win %#x, mode: %s(%u), "
            "detail: %s(%u)\n", e->event, get_notify_mode_label(e->mode),
            e->mode, get_notify_detail_label(e->detail), e->detail);
+       if ((win = find_window(e->event)) && win != win->ws->focus &&
+           win != win->ws->focus_pending &&
+           e->mode == XCB_NOTIFY_MODE_NORMAL) {
+               win->ws->focus_prev = win->ws->focus;
+               win->ws->focus = win;
+               win->ws->focus_pending = NULL;
+
+               if (win->ws->focus_prev)
+                       draw_frame(win->ws->focus_prev);
+               draw_frame(win);
+               raise_window(win);
+       }
 }
 
+#ifdef SWM_DEBUG
 void
 focusout(xcb_focus_out_event_t *e)
 {
@@ -9634,9 +10177,10 @@ keypress(xcb_key_press_event_t *e)
 
        DNPRINTF(SWM_D_EVENT, "keypress: keysym: %u, win (x,y): %#x (%d,%d), "
            "detail: %u, time: %u, root (x,y): %#x (%d,%d), child: %#x, "
-           "state: %u, same_screen: %s\n", keysym, e->event, e->event_x,
-           e->event_y, e->detail, e->time, e->root, e->root_x, e->root_y,
-           e->child, e->state, YESNO(e->same_screen));
+           "state: %u, cleaned: %u, same_screen: %s\n", keysym, e->event,
+           e->event_x, e->event_y, e->detail, e->time, e->root, e->root_x,
+           e->root_y, e->child, e->state, CLEANMASK(e->state),
+           YESNO(e->same_screen));
 
        bp = binding_lookup(CLEANMASK(e->state), KEYBIND, keysym);
        if (bp == NULL) {
@@ -9681,14 +10225,14 @@ keyrelease(xcb_key_release_event_t *e)
 
        last_event_time = e->time;
 
+       keysym = xcb_key_release_lookup_keysym(syms, e, 0);
+
        DNPRINTF(SWM_D_EVENT, "keyrelease: keysym: %u, win (x,y): %#x (%d,%d), "
            "detail: %u, time: %u, root (x,y): %#x (%d,%d), child: %#x, "
            "state: %u, same_screen: %s\n", keysym, e->event, e->event_x,
            e->event_y, e->detail, e->time, e->root, e->root_x, e->root_y,
            e->child, e->state, YESNO(e->same_screen));
 
-       keysym = xcb_key_release_lookup_keysym(syms, e, 0);
-
        bp = binding_lookup(CLEANMASK(e->state), KEYBIND, keysym);
        if (bp == NULL)
                /* Look for catch-all. */
@@ -9700,6 +10244,7 @@ keyrelease(xcb_key_release_event_t *e)
                DNPRINTF(SWM_D_EVENT, "keyrelease: replaying.\n");
        } else {
                xcb_allow_events(conn, XCB_ALLOW_SYNC_KEYBOARD, e->time);
+               DNPRINTF(SWM_D_EVENT, "keyrelease: unfreezing.\n");
        }
 
        xcb_flush(conn);
@@ -9710,7 +10255,7 @@ keyrelease(xcb_key_release_event_t *e)
 void
 buttonpress(xcb_button_press_event_t *e)
 {
-       struct ws_win           *win = NULL;
+       struct ws_win           *win = NULL, *newf;
        struct swm_region       *r, *old_r;
        struct action           *ap;
        struct binding          *bp;
@@ -9736,11 +10281,15 @@ buttonpress(xcb_button_press_event_t *e)
                                if (old_r && old_r != r)
                                        unfocus_win(old_r->ws->focus);
 
+                               DNPRINTF(SWM_D_FOCUS, "buttonpress: "
+                                   "set_input_focus: %#x, revert-to: parent, "
+                                   "time: %#x\n", e->root, e->time);
                                xcb_set_input_focus(conn,
-                                   XCB_INPUT_FOCUS_PARENT, e->root, e->time);
+                                   XCB_INPUT_FOCUS_POINTER_ROOT,
+                                   e->root, e->time);
 
                                /* Clear bar since empty. */
-                               bar_draw();
+                               bar_draw(r->bar);
 
                                /* No need to replay event. */
                                replay = false;
@@ -9750,8 +10299,15 @@ buttonpress(xcb_button_press_event_t *e)
                win = find_window(e->event);
        }
 
-       if (win)
-               focus_win(get_focus_magic(win));
+       if (win) {
+               newf = get_focus_magic(win);
+               if (win->ws->focus == newf && newf != win) {
+                       if (win->focus_child == win)
+                               win->focus_child = NULL;
+                       newf = win;
+               }
+               focus_win(newf);
+       }
 
        /* Handle any bound action. */
        bp = binding_lookup(CLEANMASK(e->state), BTNBIND, e->detail);
@@ -9785,7 +10341,7 @@ out:
                xcb_allow_events(conn, XCB_ALLOW_SYNC_POINTER, e->time);
        }
 
-       xcb_flush(conn);
+       focus_flush();
 }
 
 void
@@ -9819,8 +10375,8 @@ buttonrelease(xcb_button_release_event_t *e)
 }
 
 #ifdef SWM_DEBUG
-void
-print_win_input_model(struct ws_win *win)
+char *
+get_win_input_model(struct ws_win *win)
 {
        char            *inputmodel;
        /*
@@ -9831,13 +10387,12 @@ print_win_input_model(struct ws_win *win)
         *      Globally Active         False           Present
         */
 
-       if (!(win->hints.flags & XCB_ICCCM_WM_HINT_INPUT) || win->hints.input)
+       if (ACCEPTS_FOCUS(win))
                inputmodel = (win->take_focus) ? "Locally Active" : "Passive";
        else
                inputmodel = (win->take_focus) ? "Globally Active" : "No Input";
 
-       DNPRINTF(SWM_D_FOCUS, "print_win_input_model: win %#x, model:  %s\n",
-           win->id, inputmodel);
+       return inputmodel;
 }
 
 void
@@ -9867,7 +10422,7 @@ get_stack_mode_name(uint8_t mode)
 {
        char    *name;
 
-       switch(mode) {
+       switch (mode) {
        case XCB_STACK_MODE_ABOVE:
                name = "Above";
                break;
@@ -10033,12 +10588,9 @@ configurenotify(xcb_configure_notify_event_t *e)
 
        win = find_window(e->window);
        if (win) {
-               xcb_icccm_get_wm_normal_hints_reply(conn,
-                   xcb_icccm_get_wm_normal_hints(conn, win->id),
-                   &win->sh, NULL);
                adjust_font(win);
-               if (font_adjusted) {
-                       stack();
+               if (font_adjusted && win->ws->r) {
+                       stack(win->ws->r);
                        xcb_flush(conn);
                }
        }
@@ -10048,43 +10600,57 @@ void
 destroynotify(xcb_destroy_notify_event_t *e)
 {
        struct ws_win           *win;
+       struct workspace        *ws;
 
        DNPRINTF(SWM_D_EVENT, "destroynotify: win %#x\n", e->window);
 
-       if ((win = find_window(e->window))) {
-               /* Managed window cleanup. */
-               if (focus_mode != SWM_FOCUS_FOLLOW) {
-                       /* If focused, focus on something else. */
-                       if (win == win->ws->focus)
-                               win->ws->focus_pending = get_focus_prev(win);
+       if ((win = find_window(e->window)) == NULL) {
+               if ((win = find_unmanaged_window(e->window)) == NULL)
+                       goto out;
+               /* Window is on unmanaged list. */
+               TAILQ_REMOVE(&win->ws->unmanagedlist, win, entry);
+               free_window(win);
+               goto out;
+       }
+
+       ws = win->ws;
+
+       if (win->frame == e->window) {
+               DNPRINTF(SWM_D_EVENT, "destroynotify: frame for win %#x\n",
+                   win->id);
+               win->frame = XCB_WINDOW_NONE;
+               goto out;
+       }
+
+       if (focus_mode != SWM_FOCUS_FOLLOW) {
+               /* If we were focused, make sure we focus on something else. */
+               if (win == ws->focus) {
+                       ws->focus_pending = get_focus_prev(win);
+                       if (ws->focus_pending == win)
+                               ws->focus_pending = NULL;
                }
+       }
 
-               unmanage_window(win);
-               stack();
+       unmanage_window(win);
+       TAILQ_REMOVE(&win->ws->unmanagedlist, win, entry);
+       free_window(win);
+       stack(ws->r);
 
-               if (focus_mode != SWM_FOCUS_FOLLOW && WS_FOCUSED(win->ws)) {
-                       if (win->ws->focus_pending) {
-                               focus_win(win->ws->focus_pending);
-                               win->ws->focus_pending = NULL;
-                       } else if (win == win->ws->focus) {
-                               xcb_set_input_focus(conn,
-                                   XCB_INPUT_FOCUS_PARENT, win->ws->r->id,
-                                   XCB_CURRENT_TIME);
-                       }
+       if (focus_mode != SWM_FOCUS_FOLLOW && WS_FOCUSED(ws)) {
+               if (ws->focus_pending) {
+                       focus_win(ws->focus_pending);
+                       ws->focus_pending = NULL;
+               } else if (ws->focus == NULL) {
+                       DNPRINTF(SWM_D_FOCUS, "destroynotify: set_input_focus: "
+                           "%#x, revert-to: parent, time: 0\n", ws->r->id);
+                       xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT,
+                           ws->r->id, XCB_CURRENT_TIME);
                }
 
-               kill_refs(win);
                focus_flush();
-       } else {
-               win = find_unmanaged_window(e->window);
-       }
-
-       if (win) {
-               /* unmanage_window() puts win into unmanaged list. */
-               TAILQ_REMOVE(&win->ws->unmanagedlist, win, entry);
-               free_window(win);
        }
 
+out:
        DNPRINTF(SWM_D_EVENT, "destroynotify: done.\n");
 }
 
@@ -10225,6 +10791,13 @@ enternotify(xcb_enter_notify_event_t *e)
            YESNO(e->same_screen_focus), get_state_mask_label(e->state),
            e->state);
 
+       if (e->event == e->root && e->child == XCB_WINDOW_NONE &&
+           e->mode == XCB_NOTIFY_MODE_GRAB &&
+           e->detail == XCB_NOTIFY_DETAIL_INFERIOR) {
+               DNPRINTF(SWM_D_EVENT, "enternotify: grab inferior; ignoring.\n");
+               return;
+       }
+
        if (focus_mode == SWM_FOCUS_MANUAL &&
            e->mode == XCB_NOTIFY_MODE_NORMAL) {
                DNPRINTF(SWM_D_EVENT, "enternotify: manual focus; ignoring.\n");
@@ -10265,6 +10838,8 @@ enternotify(xcb_enter_notify_event_t *e)
                focus_win(get_focus_magic(win));
        }
 
+       DNPRINTF(SWM_D_EVENT, "enternotify: done\n");
+
        xcb_flush(conn);
 }
 
@@ -10294,10 +10869,18 @@ mapnotify(xcb_map_notify_event_t *e)
 
        DNPRINTF(SWM_D_EVENT, "mapnotify: win %#x\n", e->window);
 
-       if ((win = manage_window(e->window, spawn_position, true)) == NULL)
+       if ((win = manage_window(e->window, spawn_position, false)) == NULL)
                return;
        ws = win->ws;
 
+       if (win->state == SWM_WIN_STATE_REPARENTING)
+               return;
+
+       if (ws->r == NULL) {
+               unmap_window(win);
+               goto out;
+       }
+
        /* Need to know if win was mapped due to ws switch. */
        if (ws->state == SWM_WS_STATE_MAPPED) {
                if (ws->focus_pending && TRANS(ws->focus_pending))
@@ -10306,7 +10889,7 @@ mapnotify(xcb_map_notify_event_t *e)
                /* If window's parent is maximized, don't clear it. */
                if ((parent == NULL) || !MAXIMIZED(parent))
                        if (clear_maximized(ws) > 0)
-                               stack();
+                               stack(ws->r);
        }
 
        win->mapped = true;
@@ -10316,11 +10899,14 @@ mapnotify(xcb_map_notify_event_t *e)
                if (ws->focus_pending == win) {
                        focus_win(win);
                        ws->focus_pending = NULL;
-                       center_pointer(win->ws->r);
+                       center_pointer(ws->r);
                        focus_flush();
                }
        }
 
+out:
+       DNPRINTF(SWM_D_EVENT, "mapnotify: done\n");
+
        xcb_flush(conn);
 }
 
@@ -10339,36 +10925,17 @@ void
 maprequest(xcb_map_request_event_t *e)
 {
        struct ws_win           *win, *w = NULL;
-       xcb_get_window_attributes_reply_t *war;
 
        DNPRINTF(SWM_D_EVENT, "maprequest: win %#x\n",
            e->window);
 
-       war = xcb_get_window_attributes_reply(conn,
-           xcb_get_window_attributes(conn, e->window),
-           NULL);
-       if (war == NULL) {
-               DNPRINTF(SWM_D_EVENT, "maprequest: window lost.\n");
-               goto out;
-       }
-
-       if (war->override_redirect) {
-               DNPRINTF(SWM_D_EVENT, "maprequest: override_redirect; "
-                   "skipping.\n");
-               goto out;
-       }
-
-       win = manage_window(e->window, spawn_position,
-           (war->map_state == XCB_MAP_STATE_VIEWABLE));
+       win = manage_window(e->window, spawn_position, true);
        if (win == NULL)
                goto out;
 
        /* The new window should get focus; prepare. */
        if (focus_mode != SWM_FOCUS_FOLLOW &&
-           !(win->quirks & SWM_Q_NOFOCUSONMAP) &&
-           (!(win->hints.flags & XCB_ICCCM_WM_HINT_INPUT) ||
-            (win->hints.flags & XCB_ICCCM_WM_HINT_INPUT &&
-             win->hints.input))) {
+           !(win->quirks & SWM_Q_NOFOCUSONMAP) && ACCEPTS_FOCUS(win)) {
                if (win->quirks & SWM_Q_FOCUSONMAP_SINGLE) {
                        /* See if other wins of same type are already mapped. */
                        TAILQ_FOREACH(w, &win->ws->winlist, entry) {
@@ -10392,14 +10959,12 @@ maprequest(xcb_map_request_event_t *e)
        }
 
        /* All windows need to be mapped if they are in the current workspace.*/
-       if (win->ws->r)
-               stack();
+       stack(win->ws->r);
 
        /* Ignore EnterNotify to handle the mapnotify without interference. */
        if (focus_mode == SWM_FOCUS_DEFAULT)
                event_drain(XCB_ENTER_NOTIFY);
 out:
-       free(war);
        DNPRINTF(SWM_D_EVENT, "maprequest: done.\n");
 }
 
@@ -10496,6 +11061,7 @@ propertynotify(xcb_property_notify_event_t *e)
                if (e->state == XCB_PROPERTY_NEW_VALUE) {
                        if (focus_mode != SWM_FOCUS_FOLLOW && WS_FOCUSED(ws)) {
                                if (win->mapped &&
+                                   win->state == SWM_WIN_STATE_REPARENTED &&
                                    ws->focus_pending == win) {
                                        focus_win(ws->focus_pending);
                                        ws->focus_pending = NULL;
@@ -10504,14 +11070,46 @@ propertynotify(xcb_property_notify_event_t *e)
                }
        } else if (e->atom == XCB_ATOM_WM_CLASS ||
            e->atom == XCB_ATOM_WM_NAME) {
-               bar_draw();
+               if (ws->r)
+                       bar_draw(ws->r->bar);
        } else if (e->atom == a_prot) {
                get_wm_protocols(win);
+       } else if (e->atom == XCB_ATOM_WM_NORMAL_HINTS) {
+               xcb_icccm_get_wm_normal_hints_reply(conn,
+                   xcb_icccm_get_wm_normal_hints(conn, win->id),
+                   &win->sh, NULL);
        }
 
        xcb_flush(conn);
 }
 
+void
+reparentnotify(xcb_reparent_notify_event_t *e)
+{
+       struct ws_win   *win;
+
+       DNPRINTF(SWM_D_EVENT, "reparentnotify: event: %#x, win %#x, "
+           "parent: %#x, (x,y): (%u,%u), override_redirect: %u\n",
+           e->event, e->window, e->parent, e->x, e->y, e->override_redirect);
+
+       win = find_window(e->window);
+       if (win) {
+               if (win->state == SWM_WIN_STATE_REPARENTING) {
+                       win->state = SWM_WIN_STATE_REPARENTED;
+
+                       if (win->ws->r && !ICONIC(win))
+                               map_window(win);
+                       else
+                               unmap_window(win);
+
+                       update_window(win);
+                       update_win_stacking(win);
+               } else if (win->state == SWM_WIN_STATE_UNPARENTING) {
+                       win->state = SWM_WIN_STATE_UNPARENTED;
+               }
+       }
+}
+
 void
 unmapnotify(xcb_unmap_notify_event_t *e)
 {
@@ -10522,16 +11120,26 @@ unmapnotify(xcb_unmap_notify_event_t *e)
 
        /* If we aren't managing the window, then ignore. */
        win = find_window(e->window);
-       if (win == NULL || win->id != e->window)
+       if (win == NULL || win->id != e->window) {
+               DNPRINTF(SWM_D_EVENT, "unmapnotify: ignore unmanaged.\n");
                return;
+       }
 
        /* Do nothing if already withdrawn. */
-       if (!win->mapped && !ICONIC(win))
+       if (!win->mapped && !ICONIC(win)) {
+               DNPRINTF(SWM_D_EVENT, "unmapnotify: ignore withdrawn.\n");
                return;
+       }
 
        ws = win->ws;
        win->mapped = false;
 
+       /* Ignore if reparenting-related. */
+       if (win->state != SWM_WIN_STATE_REPARENTED) {
+               DNPRINTF(SWM_D_EVENT, "unmapnotify: ignore not reparented.\n");
+               return;
+       }
+
        /* If win was focused, make sure to focus on something else. */
        if (win == ws->focus) {
                if (focus_mode != SWM_FOCUS_FOLLOW) {
@@ -10546,15 +11154,16 @@ unmapnotify(xcb_unmap_notify_event_t *e)
 
        if (ICONIC(win)) {
                /* Iconify. */
+               DNPRINTF(SWM_D_EVENT, "unmapnotify: iconify.\n");
                set_win_state(win, XCB_ICCCM_WM_STATE_ICONIC);
        } else {
                /* Withdraw. */
+               DNPRINTF(SWM_D_EVENT, "unmapnotify: withdraw.\n");
                set_win_state(win, XCB_ICCCM_WM_STATE_WITHDRAWN);
                unmanage_window(win);
        }
 
-       if (ws->r)
-               stack();
+       stack(ws->r);
 
        /* Update focus if ws is active. */
        if (WS_FOCUSED(ws)) {
@@ -10564,13 +11173,17 @@ unmapnotify(xcb_unmap_notify_event_t *e)
                        focus_win(ws->focus_pending);
                        ws->focus_pending = NULL;
                } else if (ws->focus == NULL) {
-                       xcb_set_input_focus(conn, XCB_INPUT_FOCUS_PARENT,
+                       DNPRINTF(SWM_D_FOCUS, "unmapnotify: set_input_focus: "
+                           "%#x, revert-to: parent, time: 0\n",
+                           ws->r->id);
+                       xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT,
                            ws->r->id, XCB_CURRENT_TIME);
                }
        }
 
        center_pointer(ws->r);
        focus_flush();
+       DNPRINTF(SWM_D_EVENT, "unmapnotify: done.\n");
 }
 
 #ifdef SWM_DEBUG
@@ -10603,7 +11216,7 @@ clientmessage(xcb_client_message_event_t *e)
        struct ws_win           *win;
        struct swm_region       *r = NULL;
        union arg               a;
-       uint32_t                val[2];
+       uint32_t                vals[4];
        int                     num_screens, i;
        xcb_map_request_event_t mre;
 #ifdef SWM_DEBUG
@@ -10629,6 +11242,14 @@ clientmessage(xcb_client_message_event_t *e)
                        focus_flush();
                }
 
+               return;
+       } else if (e->type == ewmh[_NET_REQUEST_FRAME_EXTENTS].atom) {
+               DNPRINTF(SWM_D_EVENT,
+                   "clientmessage: set _NET_FRAME_EXTENTS on window.\n");
+               vals[0] = vals[1] = vals[2] = vals[3] = border_width;
+               xcb_change_property(conn, XCB_PROP_MODE_REPLACE, e->window,
+                   a_net_frame_extents, XCB_ATOM_CARDINAL, 32, 4, vals);
+               xcb_flush(conn);
                return;
        }
 
@@ -10688,11 +11309,13 @@ clientmessage(xcb_client_message_event_t *e)
                }
        } else if (e->type == ewmh[_NET_RESTACK_WINDOW].atom) {
                DNPRINTF(SWM_D_EVENT, "clientmessage: _NET_RESTACK_WINDOW\n");
-               val[0] = e->data.data32[1]; /* Sibling window. */
-               val[1] = e->data.data32[2]; /* Stack mode detail. */
+               vals[0] = e->data.data32[1]; /* Sibling window. */
+               vals[1] = e->data.data32[2]; /* Stack mode detail. */
 
-               xcb_configure_window(conn, win->id, XCB_CONFIG_WINDOW_SIBLING |
-                   XCB_CONFIG_WINDOW_STACK_MODE, val);
+               if (win->frame != XCB_WINDOW_NONE)
+                       xcb_configure_window(conn, win->frame,
+                           XCB_CONFIG_WINDOW_SIBLING |
+                           XCB_CONFIG_WINDOW_STACK_MODE, vals);
        } else  if (e->type == ewmh[_NET_WM_STATE].atom) {
                DNPRINTF(SWM_D_EVENT, "clientmessage: _NET_WM_STATE\n");
                ewmh_change_wm_state(win, e->data.data32[1], e->data.data32[0]);
@@ -10701,19 +11324,24 @@ clientmessage(xcb_client_message_event_t *e)
                            e->data.data32[0]);
 
                ewmh_update_wm_state(win);
-               stack();
+               stack(win->ws->r);
        } else if (e->type == ewmh[_NET_WM_DESKTOP].atom) {
                DNPRINTF(SWM_D_EVENT, "clientmessage: _NET_WM_DESKTOP\n");
                r = win->ws->r;
 
                win_to_ws(win, e->data.data32[0], true);
 
-               /* Restack if either the source or destination ws is mapped. */
-               if (r != NULL || win->ws->r != NULL) {
-                       if (FLOATING(win))
-                               load_float_geom(win);
+               /* Stack source and destination ws, if mapped. */
+               if (r != win->ws->r) {
+                       if (r)
+                               stack(r);
 
-                       stack();
+                       if (win->ws->r) {
+                               if (FLOATING(win))
+                                       load_float_geom(win);
+
+                               stack(win->ws->r);
+                       }
                }
        }
 
@@ -10758,7 +11386,16 @@ enable_wm(void)
 {
        int                     num_screens, i;
        const uint32_t          val = XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT |
-           XCB_EVENT_MASK_ENTER_WINDOW;
+           XCB_EVENT_MASK_ENTER_WINDOW |
+           XCB_EVENT_MASK_OWNER_GRAB_BUTTON |
+           XCB_EVENT_MASK_STRUCTURE_NOTIFY |
+           XCB_EVENT_MASK_ENTER_WINDOW |
+           XCB_EVENT_MASK_LEAVE_WINDOW |
+           XCB_EVENT_MASK_BUTTON_PRESS |
+           XCB_EVENT_MASK_BUTTON_RELEASE |
+           XCB_EVENT_MASK_KEY_PRESS |
+           XCB_EVENT_MASK_KEY_RELEASE |
+           XCB_EVENT_MASK_PROPERTY_CHANGE;
        xcb_screen_t            *sc;
        xcb_void_cookie_t       wac;
        xcb_generic_error_t     *error;
@@ -10806,6 +11443,7 @@ new_region(struct swm_screen *s, int x, int y, int w, int h)
                        r->ws->r = NULL;
                        bar_cleanup(r);
                        xcb_destroy_window(conn, r->id);
+                       r->id = XCB_WINDOW_NONE;
                        TAILQ_REMOVE(&s->rl, r, entry);
                        TAILQ_INSERT_TAIL(&s->orl, r, entry);
                }
@@ -10853,6 +11491,7 @@ new_region(struct swm_screen *s, int x, int y, int w, int h)
        Y(r) = y;
        WIDTH(r) = w;
        HEIGHT(r) = h;
+       r->bar = NULL;
        r->s = s;
        r->ws = ws;
        r->ws_prior = NULL;
@@ -10884,7 +11523,6 @@ scan_randr(int idx)
        int                                             ncrtc = 0;
 #endif /* SWM_XRR_HAS_CRTC */
        struct swm_region                               *r;
-       struct ws_win                                   *win;
        int                                             num_screens;
        xcb_randr_get_screen_resources_current_cookie_t src;
        xcb_randr_get_screen_resources_current_reply_t  *srr;
@@ -10907,6 +11545,7 @@ scan_randr(int idx)
                r->ws->old_r = r->ws->r = NULL;
                bar_cleanup(r);
                xcb_destroy_window(conn, r->id);
+               r->id = XCB_WINDOW_NONE;
                TAILQ_REMOVE(&screens[idx].rl, r, entry);
                TAILQ_INSERT_TAIL(&screens[idx].orl, r, entry);
        }
@@ -10958,13 +11597,8 @@ scan_randr(int idx)
                    screen->height_in_pixels);
 
 out:
-       /* Cleanup unused previously visible workspaces. */
+       /* The screen shouldn't focus on unused regions. */
        TAILQ_FOREACH(r, &screens[idx].orl, entry) {
-               TAILQ_FOREACH(win, &r->ws->winlist, entry)
-                       unmap_window(win);
-               r->ws->state = SWM_WS_STATE_HIDDEN;
-
-               /* The screen shouldn't focus on an unused region. */
                if (screens[idx].r_focus == r)
                        screens[idx].r_focus = NULL;
        }
@@ -10976,7 +11610,9 @@ void
 screenchange(xcb_randr_screen_change_notify_event_t *e)
 {
        struct swm_region               *r;
-       int                             i, num_screens;
+       struct workspace                *ws;
+       struct ws_win                   *win;
+       int                             i, j, num_screens;
 
        DNPRINTF(SWM_D_EVENT, "screenchange: root: %#x\n", e->root);
 
@@ -10995,46 +11631,49 @@ screenchange(xcb_randr_screen_change_notify_event_t *e)
        print_win_geom(e->root);
 #endif
        /* add bars to all regions */
-       for (i = 0; i < num_screens; i++) {
-               TAILQ_FOREACH(r, &screens[i].rl, entry)
-                       bar_setup(r);
-       }
+       TAILQ_FOREACH(r, &screens[i].rl, entry)
+               bar_setup(r);
 
-       stack();
+       /* Stack all regions. */
+       TAILQ_FOREACH(r, &screens[i].rl, entry)
+               stack(r);
 
-       /* Make sure a region has focus on each screen. */
-       for (i = 0; i < num_screens; i++) {
-               if (screens[i].r_focus == NULL) {
-                       r = TAILQ_FIRST(&screens[i].rl);
-                       if (r != NULL)
-                               focus_region(r);
+       /* Make sure a region has focus. */
+       if (screens[i].r_focus == NULL) {
+               r = TAILQ_FIRST(&screens[i].rl);
+               if (r != NULL)
+                       focus_region(r);
+       }
+
+       /* Cleanup unused previously visible workspaces. */
+       for (j = 0; j < workspace_limit; j++) {
+               ws = &screens[i].ws[j];
+               if (ws->r == NULL && ws->state != SWM_WS_STATE_HIDDEN) {
+                       TAILQ_FOREACH(win, &ws->winlist, entry)
+                               unmap_window(win);
+                       ws->state = SWM_WS_STATE_HIDDEN;
                }
        }
 
-       bar_draw();
        focus_flush();
 
-       /* Update workspace state on all regions. */
-       for (i = 0; i < num_screens; i++)
-               TAILQ_FOREACH(r, &screens[i].rl, entry)
-                       r->ws->state = SWM_WS_STATE_MAPPED;
+       /* Update workspace state and bar on all regions. */
+       TAILQ_FOREACH(r, &screens[i].rl, entry) {
+               r->ws->state = SWM_WS_STATE_MAPPED;
+               bar_draw(r->bar);
+       }
 }
 
 void
 grab_windows(void)
 {
-       struct swm_region       *r = NULL;
-       xcb_window_t            *wins = NULL, trans, *cwins = NULL;
-       int                     i, j, k, n, no, num_screens;
-       uint8_t                 state;
-       bool                    manage, mapped;
-
-       xcb_query_tree_cookie_t                 qtc;
-       xcb_query_tree_reply_t                  *qtr;
-       xcb_get_window_attributes_cookie_t      gac;
-       xcb_get_window_attributes_reply_t       *gar;
-       xcb_get_property_cookie_t               pc;
-       xcb_get_property_reply_t                *pr;
+       struct swm_region               *r = NULL;
+       xcb_query_tree_cookie_t         qtc;
+       xcb_query_tree_reply_t          *qtr;
+       xcb_get_property_cookie_t       pc;
+       xcb_get_property_reply_t        *pr;
+       xcb_window_t                    *wins = NULL, trans, *cwins = NULL;
+       int                             i, j, k, n, no, num_screens;
 
        DNPRINTF(SWM_D_INIT, "grab_windows: begin\n");
        num_screens = get_screen_count();
@@ -11068,9 +11707,9 @@ grab_windows(void)
                        free(pr);
                }
 
-               /* attach windows to a region */
-               /* normal windows */
-               DNPRINTF(SWM_D_INIT, "grab_windows: grab top level windows.\n");
+               /* Manage top-level windows first, then transients. */
+               /* TODO: allow transients to be managed before leader. */
+               DNPRINTF(SWM_D_INIT, "grab_windows: grab top-level windows.\n");
                for (j = 0; j < no; j++) {
                        TAILQ_FOREACH(r, &screens[i].rl, entry) {
                                if (r->id == wins[j]) {
@@ -11089,63 +11728,23 @@ grab_windows(void)
                        if (r)
                                continue;
 
-                       gac = xcb_get_window_attributes(conn, wins[j]);
-                       gar = xcb_get_window_attributes_reply(conn, gac, NULL);
-                       if (gar == NULL) {
-                               DNPRINTF(SWM_D_INIT, "grab_windows: skip %#x; "
-                                   "doesn't exist.\n", wins[j]);
-                               continue;
-                       }
-
-                       if (gar->override_redirect) {
-                               DNPRINTF(SWM_D_INIT, "grab_windows: skip %#x; "
-                                   "override_redirect set.\n", wins[j]);
-                               free(gar);
-                               continue;
-                       }
-
                        pc = xcb_icccm_get_wm_transient_for(conn, wins[j]);
                        if (xcb_icccm_get_wm_transient_for_reply(conn, pc,
                            &trans, NULL)) {
                                DNPRINTF(SWM_D_INIT, "grab_windows: skip %#x; "
                                    "is transient for %#x.\n", wins[j], trans);
-                               free(gar);
                                continue;
                        }
 
-                       state = get_win_state(wins[j]);
-                       manage = state != XCB_ICCCM_WM_STATE_WITHDRAWN;
-                       mapped = gar->map_state == XCB_MAP_STATE_VIEWABLE;
-                       if (mapped || manage)
-                               manage_window(wins[j], SWM_STACK_TOP, mapped);
-                       free(gar);
+                       manage_window(wins[j], SWM_STACK_TOP, false);
                }
-               /* transient windows */
+
                DNPRINTF(SWM_D_INIT, "grab_windows: grab transient windows.\n");
                for (j = 0; j < no; j++) {
-                       gac = xcb_get_window_attributes(conn, wins[j]);
-                       gar = xcb_get_window_attributes_reply(conn, gac, NULL);
-                       if (gar == NULL) {
-                               DNPRINTF(SWM_D_INIT, "grab_windows: skip %#x; "
-                                   "doesn't exist.\n", wins[j]);
-                               continue;
-                       }
-
-                       if (gar->override_redirect) {
-                               DNPRINTF(SWM_D_INIT, "grab_windows: skip %#x; "
-                                   "override_redirect set.\n", wins[j]);
-                               free(gar);
-                               continue;
-                       }
-
-                       state = get_win_state(wins[j]);
-                       manage = state != XCB_ICCCM_WM_STATE_WITHDRAWN;
-                       mapped = gar->map_state == XCB_MAP_STATE_VIEWABLE;
                        pc = xcb_icccm_get_wm_transient_for(conn, wins[j]);
                        if (xcb_icccm_get_wm_transient_for_reply(conn, pc,
-                           &trans, NULL) && manage)
-                               manage_window(wins[j], SWM_STACK_TOP, mapped);
-                       free(gar);
+                           &trans, NULL))
+                               manage_window(wins[j], SWM_STACK_TOP, false);
                }
                free(qtr);
        }
@@ -11229,6 +11828,7 @@ setup_screens(void)
                        ws->focus = NULL;
                        ws->focus_prev = NULL;
                        ws->focus_pending = NULL;
+                       ws->focus_raise = NULL;
                        ws->r = NULL;
                        ws->old_r = NULL;
                        ws->state = SWM_WS_STATE_HIDDEN;
@@ -11267,6 +11867,7 @@ setup_globals(void)
        a_state = get_atom_from_string("WM_STATE");
        a_prot = get_atom_from_string("WM_PROTOCOLS");
        a_delete = get_atom_from_string("WM_DELETE_WINDOW");
+       a_net_frame_extents = get_atom_from_string("_NET_FRAME_EXTENTS");
        a_net_supported = get_atom_from_string("_NET_SUPPORTED");
        a_net_wm_check = get_atom_from_string("_NET_SUPPORTING_WM_CHECK");
        a_takefocus = get_atom_from_string("WM_TAKE_FOCUS");
@@ -11302,6 +11903,8 @@ shutdown_cleanup(void)
        for (i = 0; i < num_screens; ++i) {
                int j;
 
+               DNPRINTF(SWM_D_FOCUS, "shutdown_cleanup: set_input_focus: "
+                   "%#x, revert-to: root, time: 0\n", screens[i].root);
                xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT,
                    screens[i].root, XCB_CURRENT_TIME);
 
@@ -11361,6 +11964,7 @@ shutdown_cleanup(void)
 
        xcb_key_symbols_free(syms);
        xcb_flush(conn);
+       xcb_aux_sync(conn);
        xcb_disconnect(conn);
 }
 
@@ -11400,8 +12004,8 @@ event_handle(xcb_generic_event_t *evt)
        EVENT(XCB_DESTROY_NOTIFY, destroynotify);
        EVENT(XCB_ENTER_NOTIFY, enternotify);
        EVENT(XCB_EXPOSE, expose);
-#ifdef SWM_DEBUG
        EVENT(XCB_FOCUS_IN, focusin);
+#ifdef SWM_DEBUG
        EVENT(XCB_FOCUS_OUT, focusout);
 #endif
        /*EVENT(XCB_GRAPHICS_EXPOSURE, );*/
@@ -11418,7 +12022,7 @@ event_handle(xcb_generic_event_t *evt)
        EVENT(XCB_MOTION_NOTIFY, motionnotify);
        /*EVENT(XCB_NO_EXPOSURE, );*/
        EVENT(XCB_PROPERTY_NOTIFY, propertynotify);
-       /*EVENT(XCB_REPARENT_NOTIFY, );*/
+       EVENT(XCB_REPARENT_NOTIFY, reparentnotify);
        /*EVENT(XCB_RESIZE_REQUEST, );*/
        /*EVENT(XCB_SELECTION_CLEAR, );*/
        /*EVENT(XCB_SELECTION_NOTIFY, );*/
@@ -11569,16 +12173,21 @@ noconfig:
 
        grabkeys();
        grabbuttons();
-       stack();
-       bar_draw();
+
+       /* Stack all regions to trigger mapping. */
+       for (i = 0; i < num_screens; i++)
+               TAILQ_FOREACH(r, &screens[i].rl, entry)
+                       stack(r);
 
        xcb_ungrab_server(conn);
        xcb_flush(conn);
 
-       /* Update state of each newly mapped workspace. */
+       /* Update state and bar of each newly mapped workspace. */
        for (i = 0; i < num_screens; i++)
-               TAILQ_FOREACH(r, &screens[i].rl, entry)
+               TAILQ_FOREACH(r, &screens[i].rl, entry) {
                        r->ws->state = SWM_WS_STATE_MAPPED;
+                       bar_draw(r->bar);
+               }
 
        memset(&pfd, 0, sizeof(pfd));
        pfd[0].fd = xfd;
@@ -11608,6 +12217,9 @@ noconfig:
                        }
                }
 
+               if (search_resp)
+                       search_do_resp();
+
                num_readable = poll(pfd, bar_extra ? 2 : 1, 1000);
                if (num_readable == -1) {
                        DNPRINTF(SWM_D_MISC, "poll failed: %s",
@@ -11620,9 +12232,6 @@ noconfig:
                if (restart_wm)
                        restart(NULL, NULL, NULL);
 
-               if (search_resp)
-                       search_do_resp();
-
                if (!running)
                        goto done;
 
@@ -11631,7 +12240,11 @@ noconfig:
                        bar_extra_update();
                }
 
-               bar_draw();
+               /* Need to ensure the bar(s) are always updated. */
+               for (i = 0; i < num_screens; i++)
+                       TAILQ_FOREACH(r, &screens[i].rl, entry)
+                               bar_draw(r->bar);
+
                xcb_flush(conn);
        }
 done: