]> code.delx.au - spectrwm/blobdiff - spectrwm.c
Fix man errors
[spectrwm] / spectrwm.c
index 63fa75ec14c9d916af2b0943cb227685221e0618..285bac959e8cdee7b8b29241543dc0e0ab14d1dc 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)
@@ -268,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)
@@ -295,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)
 
@@ -491,6 +456,7 @@ struct ws_win {
        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;
@@ -569,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 */
@@ -661,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)
@@ -675,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)
@@ -862,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,
@@ -884,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,
@@ -908,6 +874,7 @@ enum actionid {
        FN_MVWS_22,
        FN_NAME_WORKSPACE,
        FN_QUIT,
+       FN_RAISE,
        FN_RAISE_TOGGLE,
        FN_RESIZE,
        FN_RESIZE_CENTERED,
@@ -1089,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);
@@ -1119,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 *);
@@ -1159,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 *);
@@ -1169,6 +1139,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);
@@ -1191,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);
@@ -1938,7 +1910,8 @@ debug_refresh(struct ws_win *win)
                                break;
                }
 
-               if (asprintf(&s, "%#x wl:%d s:%d", win->id, widx, sidx) == -1)
+               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);
@@ -2446,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;
 }
 
@@ -3682,10 +3655,12 @@ 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;
 
+       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++)
@@ -3693,7 +3668,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);
@@ -3739,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");
@@ -3805,6 +3788,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)
@@ -3890,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)) {
@@ -3928,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));
 
@@ -3951,15 +3940,24 @@ focus_win(struct ws_win *win)
                    gifr->focus);
 
                cfw = find_window(gifr->focus);
-               if (cfw != NULL && cfw != win) {
-                       if (cfw->ws != ws && cfw->ws->r != NULL &&
-                           cfw->frame != XCB_WINDOW_NONE) {
-                               draw_frame(cfw);
-                       } else {
-                               unfocus_win(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 {
+                       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) {
@@ -3988,8 +3986,7 @@ focus_win(struct ws_win *win)
 
        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) {
+               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);
@@ -4058,6 +4055,8 @@ focus_win(struct ws_win *win)
        }
 
 out:
+       free(gifr);
+       free(war);
        DNPRINTF(SWM_D_FOCUS, "focus_win: done.\n");
 }
 
@@ -4146,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();
 }
 
@@ -4190,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;
 
@@ -4211,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) {
@@ -5139,6 +5141,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. */
@@ -5648,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)
 {
@@ -5755,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)
 {
@@ -6137,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.");
 
@@ -6515,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)
 {
@@ -6700,7 +6779,16 @@ update_window(struct ws_win *win)
            "(%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);
-       config_win(win, NULL);
+
+       /* 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 {
@@ -6710,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;
@@ -6720,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);
@@ -7292,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} },
@@ -7314,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} },
@@ -7338,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} },
@@ -8075,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);
@@ -8120,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);
@@ -9708,7 +9802,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;
@@ -9829,7 +9923,8 @@ manage_window(xcb_window_t id, int spawn_pos, bool mapping)
 
 #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. */
@@ -10036,15 +10131,29 @@ expose(xcb_expose_event_t *e)
        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)
 {
@@ -10146,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;
@@ -10190,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);
@@ -10259,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;
        /*
@@ -10271,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
@@ -10307,7 +10422,7 @@ get_stack_mode_name(uint8_t mode)
 {
        char    *name;
 
-       switch(mode) {
+       switch (mode) {
        case XCB_STACK_MODE_ABOVE:
                name = "Above";
                break;
@@ -10810,35 +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, 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) {
@@ -10868,7 +10965,6 @@ maprequest(xcb_map_request_event_t *e)
        if (focus_mode == SWM_FOCUS_DEFAULT)
                event_drain(XCB_ENTER_NOTIFY);
 out:
-       free(war);
        DNPRINTF(SWM_D_EVENT, "maprequest: done.\n");
 }
 
@@ -11001,7 +11097,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);
@@ -11427,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;
@@ -11502,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;
        }
@@ -11520,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);
 
@@ -11539,32 +11631,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
@@ -11731,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;
@@ -11906,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, );*/