]> code.delx.au - spectrwm/blobdiff - spectrwm.c
TODO
[spectrwm] / spectrwm.c
index f9a80052a2e23997eccbc66056876ff50f7a8b0c..a835d00ac98290db99336ec5e9d2dd15e6b46e7d 100644 (file)
  * 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 <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>
@@ -249,7 +218,6 @@ uint32_t            swm_debug = 0
 #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)
@@ -280,7 +248,8 @@ uint32_t            swm_debug = 0
 #define MAXIMIZED(w)           (MAXIMIZED_VERT(w) || MAXIMIZED_HORZ(w))
 #define MANUAL(w)              ((w)->ewmh_flags & SWM_F_MANUAL)
 #define TRANS(w)               ((w)->transient != XCB_WINDOW_NONE)
-#define FLOATING(w)            (ABOVE(w) || TRANS(w) || FULLSCREEN(w) ||      \
+#define FLOATING(w)            (ABOVE(w) || TRANS(w))
+#define FLOATINGFULLMAX(w)     (ABOVE(w) || TRANS(w) || FULLSCREEN(w) ||      \
     MAXIMIZED(w))
 
 /* Constrain Window flags */
@@ -297,17 +266,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)
 
@@ -433,6 +397,7 @@ int          region_padding = 0;
 int             tile_gap = 0;
 bool            java_workaround = true;
 bool            verbose_layout = false;
+bool            track_pid_ws = true;
 #ifdef SWM_DEBUG
 bool            debug_enabled;
 time_t          time_started;
@@ -535,6 +500,7 @@ void        vertical_stack(struct workspace *, struct swm_geometry *);
 void   horizontal_config(struct workspace *, int);
 void   horizontal_stack(struct workspace *, struct swm_geometry *);
 void   max_stack(struct workspace *, struct swm_geometry *);
+void   max_config(struct workspace *, int);
 void   plain_stacker(struct workspace *);
 void   fancy_stacker(struct workspace *);
 
@@ -549,7 +515,7 @@ struct layout {
        /* stack,               configure */
        { vertical_stack,       vertical_config,        0,      plain_stacker },
        { horizontal_stack,     horizontal_config,      0,      plain_stacker },
-       { max_stack,            NULL,
+       { max_stack,            max_config,
          SWM_L_MAPONFOCUS | SWM_L_FOCUSPREV,                   plain_stacker },
        { NULL,                 NULL,                   0,      NULL  },
 };
@@ -572,6 +538,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 */
@@ -635,6 +602,16 @@ struct swm_screen {
 };
 struct swm_screen      *screens;
 
+struct layout_config {
+       bool configured;
+       int layout;
+       int master_grow;
+       int master_add;
+       int stack_inc;
+       int always_raise;
+       bool apply_flip;
+} initial_layouts[SWM_WS_MAX];
+
 /* args to functions */
 union arg {
        int                     id;
@@ -651,7 +628,6 @@ union arg {
 #define SWM_ARG_ID_MASTERADD   (22)
 #define SWM_ARG_ID_MASTERDEL   (23)
 #define SWM_ARG_ID_FLIPLAYOUT  (24)
-#define SWM_ARG_ID_STACKRESET  (30)
 #define SWM_ARG_ID_STACKINIT   (31)
 #define SWM_ARG_ID_STACKBALANCE        (32)
 #define SWM_ARG_ID_CYCLEWS_UP  (40)
@@ -664,8 +640,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)
@@ -678,8 +652,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)
@@ -749,6 +721,7 @@ enum {
        _NET_WM_STATE_SKIP_PAGER,
        _NET_WM_STATE_SKIP_TASKBAR,
        _NET_WM_WINDOW_TYPE,
+       _NET_WM_WINDOW_TYPE_DESKTOP,
        _NET_WM_WINDOW_TYPE_DIALOG,
        _NET_WM_WINDOW_TYPE_DOCK,
        _NET_WM_WINDOW_TYPE_NORMAL,
@@ -793,6 +766,7 @@ struct ewmh_hint {
     {"_NET_WM_STATE_SKIP_PAGER", XCB_ATOM_NONE},
     {"_NET_WM_STATE_SKIP_TASKBAR", XCB_ATOM_NONE},
     {"_NET_WM_WINDOW_TYPE", XCB_ATOM_NONE},
+    {"_NET_WM_WINDOW_TYPE_DESKTOP", XCB_ATOM_NONE},
     {"_NET_WM_WINDOW_TYPE_DIALOG", XCB_ATOM_NONE},
     {"_NET_WM_WINDOW_TYPE_DOCK", XCB_ATOM_NONE},
     {"_NET_WM_WINDOW_TYPE_NORMAL", XCB_ATOM_NONE},
@@ -865,6 +839,7 @@ enum actionid {
        FN_FOCUS_NEXT,
        FN_FOCUS_PREV,
        FN_FOCUS_URGENT,
+       FN_FULLSCREEN_TOGGLE,
        FN_MAXIMIZE_TOGGLE,
        FN_HEIGHT_GROW,
        FN_HEIGHT_SHRINK,
@@ -887,6 +862,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,
@@ -911,6 +888,7 @@ enum actionid {
        FN_MVWS_22,
        FN_NAME_WORKSPACE,
        FN_QUIT,
+       FN_RAISE,
        FN_RAISE_TOGGLE,
        FN_RESIZE,
        FN_RESIZE_CENTERED,
@@ -936,7 +914,7 @@ enum actionid {
        FN_STACK_BALANCE,
        FN_STACK_INC,
        FN_STACK_DEC,
-       FN_STACK_RESET,
+       FN_STACK_INIT,
        FN_SWAP_MAIN,
        FN_SWAP_NEXT,
        FN_SWAP_PREV,
@@ -1022,11 +1000,12 @@ void     bar_toggle(struct binding *, struct swm_region *, union arg *);
 void    bar_urgent(char *, size_t);
 void    bar_window_class(char *, size_t, struct swm_region *);
 void    bar_window_class_instance(char *, size_t, struct swm_region *);
-void    bar_window_float(char *, size_t, struct swm_region *);
+void    bar_window_index_count(char *, size_t, struct swm_region *);
 void    bar_window_instance(char *, size_t, struct swm_region *);
 void    bar_window_name(char *, size_t, struct swm_region *);
 void    bar_window_state(char *, size_t, struct swm_region *);
 void    bar_workspace_name(char *, size_t, struct swm_region *);
+void    bar_workspace_state(char *, size_t, struct swm_region *);
 int     binding_cmp(struct binding *, struct binding *);
 void    binding_insert(uint16_t, enum binding_type, uint32_t, enum actionid,
             uint32_t, const char *);
@@ -1070,6 +1049,7 @@ void       ewmh_apply_flags(struct ws_win *, uint32_t);
 void    ewmh_autoquirk(struct ws_win *);
 void    ewmh_get_desktop_names(void);
 void    ewmh_get_wm_state(struct ws_win *);
+int     ewmh_handle_special_types(xcb_window_t, struct swm_region *);
 void    ewmh_update_actions(struct ws_win *);
 void    ewmh_update_client_list(void);
 void    ewmh_update_current_desktop(void);
@@ -1099,6 +1079,7 @@ void       focusout(xcb_focus_out_event_t *);
 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);
@@ -1133,6 +1114,7 @@ void       grab_windows(void);
 void    grabbuttons(void);
 void    grabkeys(void);
 void    iconify(struct binding *, struct swm_region *, union arg *);
+void    initlayout(struct workspace *);
 bool    isxlfd(char *);
 bool    keybindreleased(struct binding *, xcb_key_release_event_t *);
 void    keypress(xcb_key_press_event_t *);
@@ -1174,6 +1156,7 @@ 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);
@@ -1196,6 +1179,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);
@@ -1542,6 +1526,51 @@ teardown_ewmh(void)
        }
 }
 
+int
+ewmh_handle_special_types(xcb_window_t id, struct swm_region *region)
+{
+       xcb_get_property_reply_t        *r;
+       xcb_get_property_cookie_t       c;
+       xcb_atom_t                      *type;
+       int                             i, n;
+       uint16_t                        configure_mask = 0;
+       uint32_t                        wa[2];
+
+       c = xcb_get_property(conn, 0, id,
+           ewmh[_NET_WM_WINDOW_TYPE].atom, XCB_ATOM_ATOM, 0, UINT32_MAX);
+       r = xcb_get_property_reply(conn, c, NULL);
+       if (r == NULL)
+               return 0;
+
+       type = xcb_get_property_value(r);
+       n = xcb_get_property_value_length(r) / sizeof(xcb_atom_t);
+
+       for (i = 0; i < n; i++) {
+               if (type[i] == ewmh[_NET_WM_WINDOW_TYPE_DESKTOP].atom) {
+                       configure_mask = XCB_CONFIG_WINDOW_STACK_MODE |
+                               XCB_CONFIG_WINDOW_SIBLING;
+                       wa[0] = region->id;
+                       wa[1] = XCB_STACK_MODE_ABOVE;
+                       break;
+               }
+
+               if (type[i] == ewmh[_NET_WM_WINDOW_TYPE_DOCK].atom) {
+                       configure_mask = XCB_CONFIG_WINDOW_STACK_MODE;
+                       wa[0] = XCB_STACK_MODE_ABOVE;
+                       break;
+               }
+       }
+       free(r);
+
+       if (configure_mask != 0) {
+               xcb_map_window(conn, id);
+               xcb_configure_window (conn, id, configure_mask, wa);
+               return 1;
+       }
+
+       return 0;
+}
+
 void
 ewmh_autoquirk(struct ws_win *win)
 {
@@ -1562,8 +1591,7 @@ ewmh_autoquirk(struct ws_win *win)
        for (i = 0; i < n; i++) {
                if (type[i] == ewmh[_NET_WM_WINDOW_TYPE_NORMAL].atom)
                        break;
-               if (type[i] == ewmh[_NET_WM_WINDOW_TYPE_DOCK].atom ||
-                   type[i] == ewmh[_NET_WM_WINDOW_TYPE_TOOLBAR].atom ||
+               if (type[i] == ewmh[_NET_WM_WINDOW_TYPE_TOOLBAR].atom ||
                    type[i] == ewmh[_NET_WM_WINDOW_TYPE_UTILITY].atom) {
                        win->quirks = SWM_Q_FLOAT | SWM_Q_ANYWHERE;
                        break;
@@ -1575,6 +1603,25 @@ ewmh_autoquirk(struct ws_win *win)
                }
        }
        free(r);
+
+
+       c = xcb_get_property(conn, 0, win->id,
+           ewmh[_NET_WM_STATE].atom, XCB_ATOM_ATOM, 0, UINT32_MAX);
+       r = xcb_get_property_reply(conn, c, NULL);
+       if (r == NULL)
+               return;
+
+       type = xcb_get_property_value(r);
+       n = xcb_get_property_value_length(r) / sizeof(xcb_atom_t);
+
+       for (i = 0; i < n; i++) {
+               if (type[i] == ewmh[_NET_WM_STATE_SKIP_PAGER].atom ||
+                   type[i] == ewmh[_NET_WM_STATE_SKIP_TASKBAR].atom) {
+                       win->quirks = SWM_Q_FLOAT | SWM_Q_ANYWHERE;
+                       break;
+               }
+       }
+       free(r);
 }
 
 void
@@ -1693,19 +1740,14 @@ ewmh_apply_flags(struct ws_win *win, uint32_t pending)
        }
 
        if (changed & EWMH_F_ABOVE) {
-               if (ws->cur_layout != &layouts[SWM_MAX_STACK]) {
-                       if (ABOVE(win))
-                               load_float_geom(win);
-                       else if (!MAXIMIZED(win))
-                               store_float_geom(win);
+               if (ABOVE(win))
+                       load_float_geom(win);
+               else if (!MAXIMIZED(win))
+                       store_float_geom(win);
 
-                       win->ewmh_flags &= ~EWMH_F_MAXIMIZED;
-                       changed &= ~EWMH_F_MAXIMIZED;
-                       raise_window(win);
-               } else {
-                       /* Revert. */
-                       win->ewmh_flags ^= EWMH_F_ABOVE & pending;
-               }
+               win->ewmh_flags &= ~EWMH_F_MAXIMIZED;
+               changed &= ~EWMH_F_MAXIMIZED;
+               raise_window(win);
        }
 
        if (changed & EWMH_F_MAXIMIZED) {
@@ -2386,6 +2428,30 @@ bar_window_class_instance(char *s, size_t sz, struct swm_region *r)
        bar_window_instance(s, sz, r);
 }
 
+void
+bar_window_index_count(char *s, size_t sz, struct swm_region *r)
+{
+       struct ws_win           *w;
+       int                     count, index;
+
+       if (r == NULL || r->ws == NULL || r->ws->focus == NULL) {
+               strlcat(s, "0/0", sz);
+               return;
+       }
+
+       count = 0;
+       index = 0;
+
+       TAILQ_FOREACH(w, &r->ws->winlist, entry) {
+               ++count;
+               if (w->id == r->ws->focus->id) {
+                       index = count;
+               }
+       }
+
+       snprintf(s, sz, "%d/%d", index, count);
+}
+
 void
 bar_window_state(char *s, size_t sz, struct swm_region *r)
 {
@@ -2452,7 +2518,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;
 }
 
@@ -2465,6 +2531,44 @@ bar_workspace_name(char *s, size_t sz, struct swm_region *r)
                strlcat(s, r->ws->name, sz);
 }
 
+void
+bar_workspace_state(char *s, size_t sz, struct swm_region *r)
+{
+       struct ws_win           *win;
+       int                     i, j, num_screens;
+       bool                    used_workspaces[SWM_WS_MAX];
+       char                    tmp[8];
+       int                     first = 1;
+       char                    *fmt;
+
+       if (r == NULL || r->ws == NULL)
+               return;
+
+       for (i = 0; i < SWM_WS_MAX; ++i)
+               used_workspaces[i] = false;
+
+       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)
+                               ++used_workspaces[win->ws->idx];
+
+       for (i = 0; i < SWM_WS_MAX; ++i) {
+               fmt = NULL;
+               if (i == r->ws->idx) {
+                       fmt = " [%d]";
+               } else if (used_workspaces[i]) {
+                       fmt = " %d";
+               }
+               if (fmt) {
+                       fmt = fmt + first;
+                       first = 0;
+                       snprintf(tmp, sizeof tmp, fmt, i + 1);
+                       strlcat(s, tmp, sz);
+               }
+       }
+}
+
 /* build the default bar format according to the defined enabled options */
 void
 bar_fmt(const char *fmtexp, char *fmtnew, struct swm_region *r, size_t sz)
@@ -2587,6 +2691,9 @@ bar_replace_seq(char *fmt, char *fmtrep, struct swm_region *r, size_t *offrep,
        case 'I':
                snprintf(tmp, sizeof tmp, "%d", r->ws->idx + 1);
                break;
+       case 'i':
+               bar_workspace_state(tmp, sizeof tmp, r);
+               break;
        case 'M':
                count = 0;
                TAILQ_FOREACH(w, &r->ws->winlist, entry)
@@ -2598,6 +2705,9 @@ bar_replace_seq(char *fmt, char *fmtrep, struct swm_region *r, size_t *offrep,
        case 'N':
                snprintf(tmp, sizeof tmp, "%d", r->s->idx + 1);
                break;
+       case 'p':
+               bar_window_index_count(tmp, sizeof tmp, r);
+               break;
        case 'P':
                bar_window_class_instance(tmp, sizeof tmp, r);
                break;
@@ -3243,13 +3353,13 @@ config_win(struct ws_win *win, xcb_configure_request_event_t *ev)
 }
 
 int
-count_win(struct workspace *ws, bool count_transient)
+count_win(struct workspace *ws, bool count_floating)
 {
        struct ws_win           *win;
        int                     count = 0;
 
        TAILQ_FOREACH(win, &ws->winlist, entry) {
-               if (!count_transient && FLOATING(win))
+               if (!count_floating && FLOATING(win))
                        continue;
                if (ICONIC(win))
                        continue;
@@ -3394,11 +3504,11 @@ raise_window(struct ws_win *win)
 void
 update_win_stacking(struct ws_win *win)
 {
-       struct ws_win           *sibling;
 #ifdef SWM_DEBUG
        struct ws_win           *w;
 #endif
        struct swm_region       *r;
+       uint16_t                configure_mask;
        uint32_t                val[2];
 
        if (win == NULL || (r = win->ws->r) == NULL)
@@ -3410,23 +3520,25 @@ update_win_stacking(struct ws_win *win)
                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->frame;
-       else if (FLOATING(win) || (win->ws->always_raise &&
-           win->ws->focus == win))
-               val[0] = r->bar->id;
-       else
-               val[0] = r->id;
+        if (FLOATINGFULLMAX(win) || (win->ws->always_raise &&
+           win->ws->focus == win)) {
+               configure_mask = XCB_CONFIG_WINDOW_STACK_MODE;
+               val[0] = XCB_STACK_MODE_ABOVE;
+               val[1] = 0;
 
-       DNPRINTF(SWM_D_EVENT, "update_win_stacking: win %#x (%#x), "
-           "sibling %#x\n", win->frame, win->id, val[0]);
+               DNPRINTF(SWM_D_EVENT, "update_win_stacking: to very top "
+                   "win %#x (%#x), ", win->frame, win->id);
+       } else {
+               configure_mask = XCB_CONFIG_WINDOW_STACK_MODE |
+                       XCB_CONFIG_WINDOW_SIBLING;
+               val[0] = r->bar->id;
+               val[1] = XCB_STACK_MODE_BELOW;
 
-       val[1] = XCB_STACK_MODE_ABOVE;
+               DNPRINTF(SWM_D_EVENT, "update_win_stacking: to tile top "
+                   "win %#x (%#x), ", win->frame, win->id);
+       }
 
-       xcb_configure_window(conn, win->frame, XCB_CONFIG_WINDOW_SIBLING |
-           XCB_CONFIG_WINDOW_STACK_MODE, val);
+       xcb_configure_window(conn, win->frame, configure_mask, val);
 
 #ifdef SWM_DEBUG
        TAILQ_FOREACH(w, &win->ws->winlist, entry)
@@ -3688,7 +3800,7 @@ find_frame_window(xcb_window_t id) {
 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  *qtr;
 
@@ -3701,7 +3813,6 @@ find_window(xcb_window_t id)
                                if (id == win->id || id == win->frame)
                                        return (win);
 
-       /* win NULL */
 
        /* If window isn't top-level, try to find managed ancestor. */
        qtr = xcb_query_tree_reply(conn, xcb_query_tree(conn, id), NULL);
@@ -3747,23 +3858,34 @@ spawn(int ws_idx, union arg *args, bool close_fd)
 
        close(xcb_get_file_descriptor(conn));
 
-       setenv("LD_PRELOAD", SWM_LIB, 1);
+       if (track_pid_ws) {
+               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");
-               _exit(1);
-       }
-       setenv("_SWM_WS", ret, 1);
-       free(ret);
-       ret = NULL;
+               if (asprintf(&ret, "%d", ws_idx) == -1) {
+                       warn("spawn: asprintf SWM_WS");
+                       _exit(1);
+               }
+               setenv("_SWM_WS", ret, 1);
+               free(ret);
+               ret = NULL;
 
-       if (asprintf(&ret, "%d", getpid()) == -1) {
-               warn("spawn: asprintf _SWM_PID");
-               _exit(1);
+               if (asprintf(&ret, "%d", getpid()) == -1) {
+                       warn("spawn: asprintf _SWM_PID");
+                       _exit(1);
+               }
+               setenv("_SWM_PID", ret, 1);
+               free(ret);
+               ret = NULL;
        }
-       setenv("_SWM_PID", ret, 1);
-       free(ret);
-       ret = NULL;
 
        if (setsid() == -1) {
                warn("spawn: setsid");
@@ -3813,6 +3935,8 @@ kill_refs(struct ws_win *win)
                                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)
@@ -3898,6 +4022,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)) {
@@ -4165,6 +4292,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();
 }
 
@@ -4209,8 +4340,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;
 
@@ -4230,7 +4359,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) {
@@ -4531,9 +4661,6 @@ swapwin(struct binding *bp, struct swm_region *r, union arg *args)
                goto out;
        }
 
-       if (r->ws->cur_layout == &layouts[SWM_MAX_STACK])
-               return;
-
        clear_maximized(r->ws);
 
        source = cur_focus;
@@ -4908,14 +5035,15 @@ stack_config(struct binding *bp, struct swm_region *r, union arg *args)
        DNPRINTF(SWM_D_STACK, "stack_config: id: %d workspace: %d\n",
            args->id, ws->idx);
 
-       if (clear_maximized(ws) > 0)
-               stack(r);
-
-       if (ws->cur_layout->l_config != NULL)
+       if (args->id == SWM_ARG_ID_STACKINIT) {
+               initlayout(ws);
+       } else {
                ws->cur_layout->l_config(ws, args->id);
+       }
+
+       clear_maximized(ws);
+       stack(r);
 
-       if (args->id != SWM_ARG_ID_STACKINIT)
-               stack(r);
        bar_draw(r->bar);
 
        center_pointer(r);
@@ -5158,6 +5286,8 @@ stack_master(struct workspace *ws, struct swm_geometry *g, int rot, bool flip)
        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. */
@@ -5225,7 +5355,7 @@ stack_master(struct workspace *ws, struct swm_geometry *g, int rot, bool flip)
                if (ICONIC(win))
                        continue;
 
-               if (FLOATING(win)) {
+               if (FLOATINGFULLMAX(win)) {
                        update_floater(win);
                        continue;
                }
@@ -5359,7 +5489,6 @@ vertical_config(struct workspace *ws, int id)
            id, ws->idx);
 
        switch (id) {
-       case SWM_ARG_ID_STACKRESET:
        case SWM_ARG_ID_STACKINIT:
                ws->l_state.vertical_msize = SWM_V_SLICE / 2;
                ws->l_state.vertical_mwin = 1;
@@ -5412,7 +5541,6 @@ horizontal_config(struct workspace *ws, int id)
        DNPRINTF(SWM_D_STACK, "horizontal_config: workspace: %d\n", ws->idx);
 
        switch (id) {
-       case SWM_ARG_ID_STACKRESET:
        case SWM_ARG_ID_STACKINIT:
                ws->l_state.horizontal_mwin = 1;
                ws->l_state.horizontal_msize = SWM_H_SLICE / 2;
@@ -5465,15 +5593,13 @@ max_stack(struct workspace *ws, struct swm_geometry *g)
 {
        struct swm_geometry     gg = *g;
        struct ws_win           *w, *win = NULL, *parent = NULL, *tmpw;
-       int                     winno;
 
        DNPRINTF(SWM_D_STACK, "max_stack: workspace: %d\n", ws->idx);
 
        if (ws == NULL)
                return;
 
-       winno = count_win(ws, false);
-       if (winno == 0 && count_win(ws, true) == 0)
+       if (count_win(ws, true) == 0)
                return;
 
        /* Figure out which top level window should be visible. */
@@ -5496,17 +5622,13 @@ max_stack(struct workspace *ws, struct swm_geometry *g)
                if (ICONIC(w))
                        continue;
 
-               if (TRANS(w)) {
+               if (TRANS(w) || ABOVE(w)) {
                        update_floater(w);
                        continue;
                }
 
                /* Set maximized flag for all maxed windows. */
                if (!MAXIMIZED(w)) {
-                       /* Preserve floating geometry. */
-                       if (ABOVE(w))
-                               store_float_geom(w);
-
                        ewmh_apply_flags(w, w->ewmh_flags | EWMH_F_MAXIMIZED);
                        ewmh_update_wm_state(w);
                }
@@ -5553,6 +5675,16 @@ max_stack(struct workspace *ws, struct swm_geometry *g)
                        map_window(w);
 }
 
+void
+max_config(struct workspace *ws, int id)
+{
+       /* suppress unused warning since vars are needed */
+       (void)ws;
+       (void)id;
+
+       DNPRINTF(SWM_D_STACK, "max_config: workspace: %d (noop)\n", ws->idx);
+}
+
 void
 send_to_rg(struct binding *bp, struct swm_region *r, union arg *args)
 {
@@ -5667,6 +5799,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)
 {
@@ -5774,6 +5930,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)
 {
@@ -6156,7 +6337,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.");
 
@@ -6534,6 +6715,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)
 {
@@ -6738,7 +6945,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;
@@ -6748,7 +6955,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);
@@ -6820,8 +7027,8 @@ resize_win(struct ws_win *win, struct binding *bp, int opt)
        if (FULLSCREEN(win))
                return;
 
-       /* In max_stack mode, should only resize transients. */
-       if (win->ws->cur_layout == &layouts[SWM_MAX_STACK] && !TRANS(win))
+       /* In max_stack mode, should only resize transients/floating. */
+       if (win->ws->cur_layout == &layouts[SWM_MAX_STACK] && !TRANS(win) && !ABOVE(win))
                return;
 
        DNPRINTF(SWM_D_EVENT, "resize: win %#x, floating: %s, "
@@ -7109,10 +7316,6 @@ move_win(struct ws_win *win, struct binding *bp, int opt)
        DNPRINTF(SWM_D_EVENT, "move: win %#x, floating: %s, transient: "
            "%#x\n", win->id, YESNO(ABOVE(win)), win->transient);
 
-       /* in max_stack mode should only move transients */
-       if (win->ws->cur_layout == &layouts[SWM_MAX_STACK] && !TRANS(win))
-               return;
-
        if (!(ABOVE(win) || TRANS(win)) || MAXIMIZED(win)) {
                store_float_geom(win);
                restack = true;
@@ -7320,6 +7523,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} },
@@ -7342,6 +7546,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} },
@@ -7366,6 +7572,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} },
@@ -7391,7 +7598,7 @@ struct action {
        { "stack_balance",      stack_config,   0, {.id = SWM_ARG_ID_STACKBALANCE} },
        { "stack_inc",          stack_config,   0, {.id = SWM_ARG_ID_STACKINC} },
        { "stack_dec",          stack_config,   0, {.id = SWM_ARG_ID_STACKDEC} },
-       { "stack_reset",        stack_config,   0, {.id = SWM_ARG_ID_STACKRESET} },
+       { "stack_reset",        stack_config,   0, {.id = SWM_ARG_ID_STACKINIT} },
        { "swap_main",          swapwin,        0, {.id = SWM_ARG_ID_SWAPMAIN} },
        { "swap_next",          swapwin,        0, {.id = SWM_ARG_ID_SWAPNEXT} },
        { "swap_prev",          swapwin,        0, {.id = SWM_ARG_ID_SWAPPREV} },
@@ -8103,6 +8310,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);
@@ -8148,6 +8356,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);
@@ -8171,7 +8380,7 @@ setup_keybindings(void)
        BINDKEYSPAWN(MODSHIFT,  XK_Return,              "term");
        BINDKEY(MODSHIFT,       XK_comma,               FN_STACK_INC);
        BINDKEY(MODSHIFT,       XK_period,              FN_STACK_DEC);
-       BINDKEY(MODSHIFT,       XK_space,               FN_STACK_RESET);
+       BINDKEY(MODSHIFT,       XK_space,               FN_STACK_INIT);
        BINDKEY(MODKEY,         XK_Return,              FN_SWAP_MAIN);
        BINDKEY(MODSHIFT,       XK_j,                   FN_SWAP_NEXT);
        BINDKEY(MODSHIFT,       XK_k,                   FN_SWAP_PREV);
@@ -8796,6 +9005,7 @@ enum {
        SWM_S_STACK_ENABLED,
        SWM_S_TERM_WIDTH,
        SWM_S_TILE_GAP,
+       SWM_S_TRACK_PID_WS,
        SWM_S_URGENT_COLLAPSE,
        SWM_S_URGENT_ENABLED,
        SWM_S_VERBOSE_LAYOUT,
@@ -9004,6 +9214,9 @@ setconfvalue(const char *selector, const char *value, int flags)
        case SWM_S_TILE_GAP:
                tile_gap = atoi(value);
                break;
+       case SWM_S_TRACK_PID_WS:
+               track_pid_ws = atoi(value);
+               break;
        case SWM_S_URGENT_COLLAPSE:
                urgent_collapse = (atoi(value) != 0);
                break;
@@ -9229,7 +9442,7 @@ int
 setlayout(const char *selector, const char *value, int flags)
 {
        struct workspace        *ws;
-       int                     ws_id, i, x, mg, ma, si, ar;
+       int                     ws_id, i, mg, ma, si, ar;
        int                     st = SWM_V_STACK, num_screens;
        char                    s[1024];
        bool                    f = false;
@@ -9238,9 +9451,6 @@ setlayout(const char *selector, const char *value, int flags)
        (void)selector;
        (void)flags;
 
-       if (getenv("SWM_STARTED"))
-               return (0);
-
        bzero(s, sizeof s);
        if (sscanf(value, "ws[%d]:%d:%d:%d:%d:%1023c",
            &ws_id, &mg, &ma, &si, &ar, s) != 6)
@@ -9263,46 +9473,69 @@ setlayout(const char *selector, const char *value, int flags)
                f = true;
        } else if (strcasecmp(s, "fullscreen") == 0)
                st = SWM_MAX_STACK;
-       else
+       else {
                errx(1, "invalid layout entry, should be 'ws[<idx>]:"
                    "<master_grow>:<master_add>:<stack_inc>:<always_raise>:"
                    "<type>'");
+               return 0;
+       }
+
+       initial_layouts[ws_id].configured = true;
+       initial_layouts[ws_id].layout = st;
+       initial_layouts[ws_id].master_grow = mg;
+       initial_layouts[ws_id].master_add = ma;
+       initial_layouts[ws_id].stack_inc = si;
+       initial_layouts[ws_id].always_raise = ar;
+       initial_layouts[ws_id].apply_flip = f;
 
        num_screens = get_screen_count();
-       for (i = 0; i < num_screens; i++) {
-               ws = (struct workspace *)&screens[i].ws;
-               ws[ws_id].cur_layout = &layouts[st];
+       for (i = 0; i < num_screens; ++i) {
+               ws = &screens[i].ws[ws_id];
+               initlayout(ws);
+       }
 
-               ws[ws_id].always_raise = (ar != 0);
-               if (st == SWM_MAX_STACK)
-                       continue;
+       return 0;
+}
 
-               /* master grow */
-               for (x = 0; x < abs(mg); x++) {
-                       ws[ws_id].cur_layout->l_config(&ws[ws_id],
-                           mg >= 0 ?  SWM_ARG_ID_MASTERGROW :
-                           SWM_ARG_ID_MASTERSHRINK);
-               }
-               /* master add */
-               for (x = 0; x < abs(ma); x++) {
-                       ws[ws_id].cur_layout->l_config(&ws[ws_id],
-                           ma >= 0 ?  SWM_ARG_ID_MASTERADD :
-                           SWM_ARG_ID_MASTERDEL);
-               }
-               /* stack inc */
-               for (x = 0; x < abs(si); x++) {
-                       ws[ws_id].cur_layout->l_config(&ws[ws_id],
-                           si >= 0 ?  SWM_ARG_ID_STACKINC :
-                           SWM_ARG_ID_STACKDEC);
-               }
-               /* Apply flip */
-               if (f) {
-                       ws[ws_id].cur_layout->l_config(&ws[ws_id],
-                           SWM_ARG_ID_FLIPLAYOUT);
-               }
-       }
+void
+initlayout(struct workspace *ws)
+{
+       int                     i, ws_id;
 
-       return (0);
+       ws_id = ws->idx;
+
+       if (!initial_layouts[ws_id].configured)
+               return;
+
+       ws->cur_layout = &layouts[initial_layouts[ws_id].layout];
+
+       ws->cur_layout->l_config(ws, SWM_ARG_ID_STACKINIT);
+
+       ws->always_raise = (initial_layouts[ws_id].always_raise != 0);
+
+       /* master grow */
+       for (i = 0; i < abs(initial_layouts[ws_id].master_grow); i++) {
+               ws->cur_layout->l_config(ws,
+                   initial_layouts[ws_id].master_grow >= 0 ?  SWM_ARG_ID_MASTERGROW :
+                   SWM_ARG_ID_MASTERSHRINK);
+       }
+       /* master add */
+       for (i = 0; i < abs(initial_layouts[ws_id].master_add); i++) {
+               ws->cur_layout->l_config(ws,
+                   initial_layouts[ws_id].master_add >= 0 ?  SWM_ARG_ID_MASTERADD :
+                   SWM_ARG_ID_MASTERDEL);
+       }
+       /* stack inc */
+       for (i = 0; i < abs(initial_layouts[ws_id].stack_inc); i++) {
+               ws->cur_layout->l_config(ws,
+                   initial_layouts[ws_id].stack_inc >= 0 ?  SWM_ARG_ID_STACKINC :
+                   SWM_ARG_ID_STACKDEC);
+       }
+       /* Apply flip */
+       if (initial_layouts[ws_id].apply_flip) {
+               ws->cur_layout->l_config(ws,
+                   SWM_ARG_ID_FLIPLAYOUT);
+       }
 }
 
 /* config options */
@@ -9362,6 +9595,7 @@ struct config_option configopt[] = {
        { "tile_gap",                   setconfvalue,   SWM_S_TILE_GAP },
        { "title_class_enabled",        setconfvalue,   SWM_S_WINDOW_CLASS_ENABLED }, /* For backwards compat. */
        { "title_name_enabled",         setconfvalue,   SWM_S_WINDOW_INSTANCE_ENABLED }, /* For backwards compat. */
+       { "track_pid_ws",               setconfvalue,   SWM_S_TRACK_PID_WS },
        { "urgent_collapse",            setconfvalue,   SWM_S_URGENT_COLLAPSE },
        { "urgent_enabled",             setconfvalue,   SWM_S_URGENT_ENABLED },
        { "verbose_layout",             setconfvalue,   SWM_S_VERBOSE_LAYOUT },
@@ -9504,6 +9738,10 @@ conf_load(const char *filename, int keymapping)
                            configopt[optidx].optname);
                        continue;
                }
+               /* trim trailing spaces */
+               ce = optval + strlen(optval) - 1;
+               while (ce > optval && isspace(*ce)) --ce;
+               *(ce + 1) = '\0';
                /* call function to deal with it all */
                if (configopt[optidx].func(optsub, optval,
                    configopt[optidx].funcflags) != 0) {
@@ -9736,7 +9974,7 @@ unparent_window(struct ws_win *win)
 struct ws_win *
 manage_window(xcb_window_t id, int spawn_pos, bool mapping)
 {
-       struct ws_win                           *win, *ww;
+       struct ws_win                           *win = NULL, *ww;
        struct swm_region                       *r;
        struct pid_e                            *p;
        struct quirk                            *qp;
@@ -9770,11 +10008,6 @@ manage_window(xcb_window_t id, int spawn_pos, bool mapping)
                DNPRINTF(SWM_D_MISC, "manage_window: win %#x found on "
                    "unmanaged list.\n", id);
                TAILQ_REMOVE(&win->ws->unmanagedlist, win, entry);
-
-               if (TRANS(win))
-                       set_child_transient(win, &trans);
-
-               goto remanage;
        } else {
                DNPRINTF(SWM_D_MISC, "manage_window: win %#x is new.\n", id);
        }
@@ -9806,15 +10039,24 @@ manage_window(xcb_window_t id, int spawn_pos, bool mapping)
                goto out;
        }
 
-       /* Create and initialize ws_win object. */
-       if ((win = calloc(1, sizeof(struct ws_win))) == NULL)
-               err(1, "manage_window: calloc: failed to allocate memory for "
-                   "new window");
+       /* Figure out which region the window belongs to. */
+       r = root_to_region(gr->root, SWM_CK_ALL);
 
-       win->id = id;
+       /* Handle special windows with special _NET_WM_WINDOW_TYPE */
+       if (ewmh_handle_special_types(id, r)) {
+               DNPRINTF(SWM_D_EVENT, "manage_window: "
+                    "unmanaged ewmh window type\n");
+               goto out;
+       }
 
-       /* Figureout which region the window belongs to. */
-       r = root_to_region(gr->root, SWM_CK_ALL);
+       if (!win) {
+               /* Create and initialize ws_win object. */
+               if ((win = calloc(1, sizeof(struct ws_win))) == NULL)
+                       err(1, "manage_window: calloc: failed to allocate "
+                           "memory for new window");
+
+               win->id = id;
+       }
 
        /* Ignore window border if there is one. */
        WIDTH(win) = gr->width;
@@ -9945,7 +10187,6 @@ manage_window(xcb_window_t id, int spawn_pos, bool mapping)
                update_window(win);
        }
 
-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);
@@ -10356,7 +10597,7 @@ get_stack_mode_name(uint8_t mode)
 {
        char    *name;
 
-       switch(mode) {
+       switch (mode) {
        case XCB_STACK_MODE_ABOVE:
                name = "Above";
                break;
@@ -11031,7 +11272,7 @@ reparentnotify(xcb_reparent_notify_event_t *e)
                if (win->state == SWM_WIN_STATE_REPARENTING) {
                        win->state = SWM_WIN_STATE_REPARENTED;
 
-                       if (win->ws->r)
+                       if (win->ws->r && !ICONIC(win))
                                map_window(win);
                        else
                                unmap_window(win);
@@ -11457,7 +11698,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;
@@ -11532,13 +11772,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;
        }
@@ -11550,7 +11785,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);
 
@@ -11569,32 +11806,37 @@ 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 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;
                }
        }
 
        focus_flush();
 
        /* Update workspace state and bar on all regions. */
-       for (i = 0; i < num_screens; i++)
-               TAILQ_FOREACH(r, &screens[i].rl, entry) {
-                       r->ws->state = SWM_WS_STATE_MAPPED;
-                       bar_draw(r->bar);
-               }
+       TAILQ_FOREACH(r, &screens[i].rl, entry) {
+               r->ws->state = SWM_WS_STATE_MAPPED;
+               bar_draw(r->bar);
+       }
 }
 
 void
@@ -11761,6 +12003,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;
@@ -11769,9 +12012,7 @@ setup_screens(void)
                        TAILQ_INIT(&ws->unmanagedlist);
 
                        for (k = 0; layouts[k].l_stack != NULL; k++)
-                               if (layouts[k].l_config != NULL)
-                                       layouts[k].l_config(ws,
-                                           SWM_ARG_ID_STACKINIT);
+                               layouts[k].l_config(ws, SWM_ARG_ID_STACKINIT);
                        ws->cur_layout = &layouts[0];
                        ws->cur_layout->l_string(ws);
                }