]> code.delx.au - spectrwm/blobdiff - spectrwm.c
Additional shutdown cleanup.
[spectrwm] / spectrwm.c
index de42566364b54ce27251447b5895e107b15cc809..b894d0b920fc805eedf164e41374d521e0b6a33d 100644 (file)
@@ -333,9 +333,9 @@ bool                        cycle_visible = false;
 int                    term_width = 0;
 int                    font_adjusted = 0;
 unsigned int           mod_key = MODKEY;
+bool                   warp_focus = false;
 bool                   warp_pointer = false;
-unsigned int           mouse_button_move = XCB_BUTTON_INDEX_1;
-unsigned int           mouse_button_resize = XCB_BUTTON_INDEX_3;
+bool                   workspace_clamp = false;
 
 /* dmenu search */
 struct swm_region      *search_r;
@@ -401,7 +401,7 @@ bool                 bar_at_bottom = false;
 bool            bar_extra = false;
 int             bar_height = 0;
 int             bar_justify = SWM_BAR_JUSTIFY_LEFT;
-char            *bar_format = NULL;
+char           *bar_format = NULL;
 bool            stack_enabled = true;
 bool            clock_enabled = true;
 bool            iconic_enabled = false;
@@ -427,15 +427,14 @@ bool               verbose_layout = false;
 time_t          time_started;
 #endif
 pid_t           bar_pid;
-XFontSet        bar_fs;
+XFontSet        bar_fs = NULL;
 XFontSetExtents        *bar_fs_extents;
-XftFont                *bar_font;
+XftFont                *bar_font = NULL;
 bool            bar_font_legacy = true;
-char           *bar_fonts;
+char           *bar_fonts = NULL;
 XftColor        bar_font_color;
 XftColor        search_font_color;
-struct passwd  *pwd;
-char           *startup_exception;
+char           *startup_exception = NULL;
 unsigned int    nr_exceptions = 0;
 
 /* layout manager data */
@@ -674,18 +673,19 @@ struct quirk {
        regex_t                 regex_name;
        uint32_t                quirk;
        int                     ws;             /* Initial workspace. */
-#define SWM_Q_FLOAT            (1<<0)  /* float this window */
-#define SWM_Q_TRANSSZ          (1<<1)  /* transiend window size too small */
-#define SWM_Q_ANYWHERE         (1<<2)  /* don't position this window */
-#define SWM_Q_XTERM_FONTADJ    (1<<3)  /* adjust xterm fonts when resizing */
-#define SWM_Q_FULLSCREEN       (1<<4)  /* remove border */
-#define SWM_Q_FOCUSPREV                (1<<5)  /* focus on caller */
+#define SWM_Q_FLOAT            (1<<0)  /* Float this window. */
+#define SWM_Q_TRANSSZ          (1<<1)  /* Transient window size too small. */
+#define SWM_Q_ANYWHERE         (1<<2)  /* Don't position this window */
+#define SWM_Q_XTERM_FONTADJ    (1<<3)  /* Adjust xterm fonts when resizing. */
+#define SWM_Q_FULLSCREEN       (1<<4)  /* Remove border when fullscreen. */
+#define SWM_Q_FOCUSPREV                (1<<5)  /* Focus on caller. */
 #define SWM_Q_NOFOCUSONMAP     (1<<6)  /* Don't focus on window when mapped. */
 #define SWM_Q_FOCUSONMAP_SINGLE        (1<<7)  /* Only focus if single win of type. */
 #define SWM_Q_OBEYAPPFOCUSREQ  (1<<8)  /* Focus when applications ask. */
 #define SWM_Q_IGNOREPID                (1<<9)  /* Ignore PID when determining ws. */
 #define SWM_Q_IGNORESPAWNWS    (1<<10) /* Ignore _SWM_WS when managing win. */
 #define SWM_Q_NOFOCUSCYCLE     (1<<11) /* Remove from normal focus cycle. */
+#define SWM_Q_MINIMALBORDER    (1<<12) /* No border when floating/unfocused. */
 };
 TAILQ_HEAD(quirk_list, quirk);
 struct quirk_list              quirks = TAILQ_HEAD_INITIALIZER(quirks);
@@ -889,19 +889,19 @@ enum keyfuncid {
        KF_RG_7,
        KF_RG_8,
        KF_RG_9,
-       KF_RG_NEXT,
-       KF_RG_PREV,
        KF_RG_MOVE_NEXT,
        KF_RG_MOVE_PREV,
+       KF_RG_NEXT,
+       KF_RG_PREV,
        KF_SCREEN_NEXT,
        KF_SCREEN_PREV,
        KF_SEARCH_WIN,
        KF_SEARCH_WORKSPACE,
        KF_SPAWN_CUSTOM,
+       KF_STACK_BALANCE,
        KF_STACK_INC,
        KF_STACK_DEC,
        KF_STACK_RESET,
-       KF_STACK_BALANCE,
        KF_SWAP_MAIN,
        KF_SWAP_NEXT,
        KF_SWAP_PREV,
@@ -983,6 +983,8 @@ void         center_pointer(struct swm_region *);
 void    check_conn(void);
 void    clear_keys(void);
 int     clear_maximized(struct workspace *);
+void    clear_quirks(void);
+void    clear_spawns(void);
 void    clientmessage(xcb_client_message_event_t *);
 void    client_msg(struct ws_win *, xcb_atom_t, xcb_timestamp_t);
 int     conf_load(const char *, int);
@@ -1130,8 +1132,6 @@ int        setautorun(const char *, const char *, int);
 int     setconfbinding(const char *, const char *, int);
 int     setconfcolor(const char *, const char *, int);
 int     setconfmodkey(const char *, const char *, int);
-int     setconfmousebuttonmove(const char *, const char *, int);
-int     setconfmousebuttonresize(const char *, const char *, int);
 int     setconfquirk(const char *, const char *, int);
 int     setconfregion(const char *, const char *, int);
 int     setconfspawn(const char *, const char *, int);
@@ -1179,7 +1179,6 @@ void       unmap_window(struct ws_win *);
 void    updatenumlockmask(void);
 void    update_floater(struct ws_win *);
 void    update_modkey(unsigned int);
-unsigned char  update_mousebutton(unsigned char, unsigned int);
 void    update_win_stacking(struct ws_win *);
 void    update_window(struct ws_win *);
 void    update_window_color(struct ws_win *);
@@ -2710,13 +2709,13 @@ fontset_init(void)
 void
 xft_init(struct swm_region *r)
 {
-       char                    *font, *d, *search;
+       char                    *font, *str, *search;
        XRenderColor            color;
 
        if (bar_font == NULL) {
-               if ((d = strdup(bar_fonts)) == NULL)
+               if ((search = str = strdup(bar_fonts)) == NULL)
                        errx(1, "insufficient memory.");
-               search = d;
+
                while ((font = strsep(&search, ",")) != NULL) {
                        if (*font == '\0')
                                continue;
@@ -2740,7 +2739,7 @@ xft_init(struct swm_region *r)
                                break;
                        }
                }
-               free(d);
+               free(str);
        }
 
        if (bar_font == NULL)
@@ -2847,7 +2846,7 @@ set_win_state(struct ws_win *win, uint8_t state)
        uint16_t                data[2] = { state, XCB_ATOM_NONE };
 
        DNPRINTF(SWM_D_EVENT, "set_win_state: win %#x, state: %u\n",
-           win->id, state);
+           WINID(win), state);
 
        if (win == NULL)
                return;
@@ -3040,13 +3039,13 @@ lower_window(struct ws_win *win)
        struct ws_win           *target = NULL;
        struct workspace        *ws;
 
+       DNPRINTF(SWM_D_EVENT, "lower_window: win %#x\n", WINID(win));
+
        if (win == NULL)
                return;
 
        ws = win->ws;
 
-       DNPRINTF(SWM_D_EVENT, "lower_window: win %#x\n", win->id);
-
        TAILQ_FOREACH(target, &ws->stack, stack_entry) {
                if (target == win || ICONIC(target))
                        continue;
@@ -3101,11 +3100,12 @@ raise_window(struct ws_win *win)
        struct ws_win           *target = NULL;
        struct workspace        *ws;
 
+       DNPRINTF(SWM_D_EVENT, "raise_window: win %#x\n", WINID(win));
+
        if (win == NULL)
                return;
-       ws = win->ws;
 
-       DNPRINTF(SWM_D_EVENT, "raise_window: win %#x\n", win->id);
+       ws = win->ws;
 
        TAILQ_FOREACH(target, &ws->stack, stack_entry) {
                if (target == win || ICONIC(target))
@@ -3494,23 +3494,29 @@ spawn(int ws_idx, union arg *args, bool close_fd)
 void
 kill_refs(struct ws_win *win)
 {
-       int                     i, x, num_screens;
-       struct swm_region       *r;
        struct workspace        *ws;
+       struct ws_win           *w;
+       int                     i, j, num_screens;
 
        if (win == NULL)
                return;
 
        num_screens = get_screen_count();
-       for (i = 0; i < num_screens; i++)
-               TAILQ_FOREACH(r, &screens[i].rl, entry)
-                       for (x = 0; x < workspace_limit; x++) {
-                               ws = &r->s->ws[x];
-                               if (win == ws->focus)
-                                       ws->focus = NULL;
-                               if (win == ws->focus_prev)
-                                       ws->focus_prev = NULL;
-                       }
+       for (i = 0; i < num_screens; i++) {
+               for (j = 0; j < workspace_limit; j++) {
+                       ws = &screens[i].ws[j];
+
+                       if (win == ws->focus)
+                               ws->focus = NULL;
+                       if (win == ws->focus_prev)
+                               ws->focus_prev = NULL;
+
+                       if (TRANS(win))
+                               TAILQ_FOREACH(w, &ws->winlist, entry)
+                                       if (win == w->focus_child)
+                                               w->focus_child = NULL;
+               }
+       }
 }
 
 int
@@ -3607,6 +3613,15 @@ unfocus_win(struct ws_win *win)
        if (win->ws->always_raise)
                raise_window(win);
 
+       /* Update border width */
+       if (win->bordered && (win->quirks & SWM_Q_MINIMALBORDER) &&
+           FLOATING(win)) {
+               win->bordered = 0;
+               X(win) += border_width;
+               Y(win) += border_width;
+               update_window(win);
+       }
+
        xcb_change_property(conn, XCB_PROP_MODE_REPLACE, win->s->root,
            ewmh[_NET_ACTIVE_WINDOW].atom, XCB_ATOM_WINDOW, 32, 1, &none);
 
@@ -3646,6 +3661,16 @@ focus_win(struct ws_win *win)
                                    &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);
+                               }
                        } else {
                                unfocus_win(cfw);
                        }
@@ -3659,9 +3684,8 @@ focus_win(struct ws_win *win)
                ws->focus = win;
        }
 
-       /* If this window directs focus to a child window, then clear. */
-       if (win->focus_child)
-               win->focus_child = NULL;
+       /* Clear focus child redirect. */
+       win->focus_child = NULL;
 
        /* If transient, adjust parent's focus child for focus_magic. */
        if (TRANS(win)) {
@@ -3734,10 +3758,20 @@ focus_win(struct ws_win *win)
                    &win->id);
        }
 
-       if (cfw != win)
+       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);
+               }
+       }
+
 out:
        bar_draw();
 
@@ -3888,6 +3922,20 @@ switchws(struct swm_region *r, union arg *args)
        if (new_ws == old_ws)
                return;
 
+       other_r = new_ws->r;
+       if (other_r && workspace_clamp) {
+               DNPRINTF(SWM_D_WS, "switchws: ws clamped.\n");
+
+               if (warp_focus) {
+                       DNPRINTF(SWM_D_WS, "switchws: warping focus to region "
+                           "with ws %d.\n", wsid);
+                       focus_region(other_r);
+                       center_pointer(other_r);
+                       focus_flush();
+               }
+               return;
+       }
+
        if ((win = old_ws->focus) != NULL) {
                update_window_color(win);
 
@@ -3896,17 +3944,17 @@ switchws(struct swm_region *r, union arg *args)
                    &none);
        }
 
-       other_r = new_ws->r;
-       if (other_r == NULL) {
-               /* the other workspace is hidden, hide this one */
-               old_ws->r = NULL;
-               unmap_old = true;
-       } else {
+       if (other_r) {
                /* the other ws is visible in another region, exchange them */
                other_r->ws_prior = new_ws;
                other_r->ws = old_ws;
                old_ws->r = other_r;
+       } else {
+               /* the other workspace is hidden, hide this one */
+               old_ws->r = NULL;
+               unmap_old = true;
        }
+
        this_r->ws_prior = old_ws;
        this_r->ws = new_ws;
        new_ws->r = this_r;
@@ -4652,6 +4700,8 @@ update_floater(struct ws_win *win)
        struct workspace        *ws;
        struct swm_region       *r;
 
+       DNPRINTF(SWM_D_MISC, "update_floater: win %#x\n", WINID(win));
+
        if (win == NULL)
                return;
 
@@ -4660,8 +4710,6 @@ update_floater(struct ws_win *win)
        if ((r = ws->r) == NULL)
                return;
 
-       DNPRINTF(SWM_D_MISC, "update_floater: win %#x\n", win->id);
-
        win->bordered = true;
 
        if (FULLSCREEN(win)) {
@@ -4696,9 +4744,11 @@ update_floater(struct ws_win *win)
                if (r != ws->old_r)
                        load_float_geom(win);
 
-               if ((win->quirks & SWM_Q_FULLSCREEN) &&
-                   WIDTH(win) >= WIDTH(r) && HEIGHT(win) >= HEIGHT(r)) {
-                       /* Remove border for FULLSCREEN quirk. */
+               if (((win->quirks & SWM_Q_FULLSCREEN) &&
+                    WIDTH(win) >= WIDTH(r) && HEIGHT(win) >= HEIGHT(r)) ||
+                   ((!WS_FOCUSED(win->ws) || win->ws->focus != win) &&
+                    (win->quirks & SWM_Q_MINIMALBORDER))) {
+                       /* Remove border */
                        win->bordered = false;
                } else if (!MANUAL(win)) {
                        if (TRANS(win) && (win->quirks & SWM_Q_TRANSSZ)) {
@@ -4973,9 +5023,6 @@ vertical_config(struct workspace *ws, int id)
                ws->l_state.vertical_mwin = 1;
                ws->l_state.vertical_stacks = 1;
                break;
-       case SWM_ARG_ID_STACKBALANCE:
-               ws->l_state.vertical_msize = SWM_V_SLICE / (ws->l_state.vertical_stacks + 1);
-               break;
        case SWM_ARG_ID_MASTERSHRINK:
                if (ws->l_state.vertical_msize > 1)
                        ws->l_state.vertical_msize--;
@@ -4991,6 +5038,9 @@ vertical_config(struct workspace *ws, int id)
                if (ws->l_state.vertical_mwin > 0)
                        ws->l_state.vertical_mwin--;
                break;
+       case SWM_ARG_ID_STACKBALANCE:
+               ws->l_state.vertical_msize = SWM_V_SLICE / (ws->l_state.vertical_stacks + 1);
+               break;
        case SWM_ARG_ID_STACKINC:
                ws->l_state.vertical_stacks++;
                break;
@@ -5026,9 +5076,6 @@ horizontal_config(struct workspace *ws, int id)
                ws->l_state.horizontal_msize = SWM_H_SLICE / 2;
                ws->l_state.horizontal_stacks = 1;
                break;
-       case SWM_ARG_ID_STACKBALANCE:
-               ws->l_state.horizontal_msize = SWM_H_SLICE / (ws->l_state.horizontal_stacks + 1);
-               break;
        case SWM_ARG_ID_MASTERSHRINK:
                if (ws->l_state.horizontal_msize > 1)
                        ws->l_state.horizontal_msize--;
@@ -5044,6 +5091,9 @@ horizontal_config(struct workspace *ws, int id)
                if (ws->l_state.horizontal_mwin > 0)
                        ws->l_state.horizontal_mwin--;
                break;
+       case SWM_ARG_ID_STACKBALANCE:
+               ws->l_state.horizontal_msize = SWM_H_SLICE / (ws->l_state.horizontal_stacks + 1);
+               break;
        case SWM_ARG_ID_STACKINC:
                ws->l_state.horizontal_stacks++;
                break;
@@ -5847,10 +5897,13 @@ ewmh_update_current_desktop(void)
        int                     num_screens, i;
 
        num_screens = get_screen_count();
-       for (i = 0; i < num_screens; ++i)
-               xcb_change_property(conn, XCB_PROP_MODE_REPLACE,
-                   screens[i].root, ewmh[_NET_CURRENT_DESKTOP].atom,
-                   XCB_ATOM_CARDINAL, 32, 1, &screens[i].r_focus->ws->idx);
+       for (i = 0; i < num_screens; ++i) {
+               if (screens[i].r_focus)
+                       xcb_change_property(conn, XCB_PROP_MODE_REPLACE,
+                           screens[i].root, ewmh[_NET_CURRENT_DESKTOP].atom,
+                           XCB_ATOM_CARDINAL, 32, 1,
+                           &screens[i].r_focus->ws->idx);
+       }
 }
 
 void
@@ -6733,19 +6786,19 @@ struct keyfunc {
        { "rg_7",               focusrg,        {.id = 6} },
        { "rg_8",               focusrg,        {.id = 7} },
        { "rg_9",               focusrg,        {.id = 8} },
-       { "rg_next",            cyclerg,        {.id = SWM_ARG_ID_CYCLERG_UP} },
-       { "rg_prev",            cyclerg,        {.id = SWM_ARG_ID_CYCLERG_DOWN} },
        { "rg_move_next",       cyclerg,        {.id = SWM_ARG_ID_CYCLERG_MOVE_UP} },
        { "rg_move_prev",       cyclerg,        {.id = SWM_ARG_ID_CYCLERG_MOVE_DOWN} },
+       { "rg_next",            cyclerg,        {.id = SWM_ARG_ID_CYCLERG_UP} },
+       { "rg_prev",            cyclerg,        {.id = SWM_ARG_ID_CYCLERG_DOWN} },
        { "screen_next",        cyclerg,        {.id = SWM_ARG_ID_CYCLERG_UP} },
        { "screen_prev",        cyclerg,        {.id = SWM_ARG_ID_CYCLERG_DOWN} },
        { "search_win",         search_win,     {0} },
        { "search_workspace",   search_workspace,       {0} },
        { "spawn_custom",       NULL,           {0} },
+       { "stack_balance",      stack_config,   {.id = SWM_ARG_ID_STACKBALANCE} },
        { "stack_inc",          stack_config,   {.id = SWM_ARG_ID_STACKINC} },
        { "stack_dec",          stack_config,   {.id = SWM_ARG_ID_STACKDEC} },
        { "stack_reset",        stack_config,   {.id = SWM_ARG_ID_STACKRESET} },
-       { "stack_balance",      stack_config,   {.id = SWM_ARG_ID_STACKBALANCE} },
        { "swap_main",          swapwin,        {.id = SWM_ARG_ID_SWAPMAIN} },
        { "swap_next",          swapwin,        {.id = SWM_ARG_ID_SWAPNEXT} },
        { "swap_prev",          swapwin,        {.id = SWM_ARG_ID_SWAPPREV} },
@@ -6841,31 +6894,6 @@ update_modkey(unsigned int mod)
                        buttons[i].mask = mod;
 }
 
-unsigned char
-update_mousebutton(unsigned char type, unsigned int but)
-{
-       int                     i;
-
-       switch (type) {
-               case 0:
-                       mouse_button_move = but;
-                       break;
-               case 1:
-                       mouse_button_resize = but;
-                       break;
-       }
-
-       for (i = 0; i < LENGTH(buttons); i++) {
-               if (buttons[i].func == move)
-                       buttons[i].button = mouse_button_move;
-
-               if (buttons[i].func == resize)
-                       buttons[i].button = mouse_button_resize;
-       }
-
-       return(1);
-}
-
 int
 spawn_expand(struct swm_region *r, union arg *args, const char *spawn_name,
     char ***ret_args)
@@ -7132,6 +7160,16 @@ spawn_remove(struct spawn_prog *sp)
        DNPRINTF(SWM_D_SPAWN, "spawn_remove: leave\n");
 }
 
+void
+clear_spawns(void)
+{
+       struct spawn_prog       *sp;
+
+       while ((sp = TAILQ_FIRST(&spawns)) != NULL) {
+               spawn_remove(sp);
+       }
+}
+
 struct spawn_prog*
 spawn_find(const char *name)
 {
@@ -7536,8 +7574,6 @@ setup_keys(void)
        setkeybinding(MODKEY,           XK_KP_Up,       KF_RG_8,        NULL);
        setkeybinding(MODKEY,           XK_KP_Prior,    KF_RG_9,        NULL);
        setkeybinding(MODKEY_SHIFT,     XK_Right,       KF_RG_NEXT,     NULL);
-       setkeybinding(MODKEY,           XK_c,           KF_RG_MOVE_NEXT,NULL);
-       setkeybinding(MODKEY_SHIFT,     XK_c,           KF_RG_MOVE_PREV,NULL);
        setkeybinding(MODKEY_SHIFT,     XK_Left,        KF_RG_PREV,     NULL);
        setkeybinding(MODKEY,           XK_f,           KF_SEARCH_WIN,  NULL);
        setkeybinding(MODKEY,           XK_slash,       KF_SEARCH_WORKSPACE,NULL);
@@ -7550,7 +7586,6 @@ setup_keys(void)
        setkeybinding(MODKEY_SHIFT,     XK_comma,       KF_STACK_INC,   NULL);
        setkeybinding(MODKEY_SHIFT,     XK_period,      KF_STACK_DEC,   NULL);
        setkeybinding(MODKEY_SHIFT,     XK_space,       KF_STACK_RESET, NULL);
-       setkeybinding(MODKEY_SHIFT,     XK_h,           KF_STACK_BALANCE, NULL);
        setkeybinding(MODKEY,           XK_Return,      KF_SWAP_MAIN,   NULL);
        setkeybinding(MODKEY_SHIFT,     XK_j,           KF_SWAP_NEXT,   NULL);
        setkeybinding(MODKEY_SHIFT,     XK_k,           KF_SWAP_PREV,   NULL);
@@ -7749,6 +7784,7 @@ const char *quirkname[] = {
        "IGNOREPID",
        "IGNORESPAWNWS",
        "NOFOCUSCYCLE",
+       "MINIMALBORDER",
 };
 
 /* SWM_Q_DELIM: retain '|' for back compat for now (2009-08-11) */
@@ -7762,10 +7798,9 @@ parsequirks(const char *qstr, uint32_t *quirk, int *ws)
        if (quirk == NULL || qstr == NULL)
                return (1);
 
-       if ((str = strdup(qstr)) == NULL)
+       if ((cp = str = strdup(qstr)) == NULL)
                err(1, "parsequirks: strdup");
 
-       cp = str;
        *quirk = 0;
        while ((name = strsep(&cp, SWM_Q_DELIM)) != NULL) {
                if (cp)
@@ -7889,6 +7924,16 @@ quirk_free(struct quirk *qp)
        free(qp);
 }
 
+void
+clear_quirks(void)
+{
+       struct quirk            *qp;
+
+       while ((qp = TAILQ_FIRST(&quirks)) != NULL) {
+               quirk_remove(qp);
+       }
+}
+
 void
 quirk_replace(struct quirk *qp, const char *class, const char *instance,
     const char *name, uint32_t quirk, int ws)
@@ -8076,10 +8121,12 @@ enum {
        SWM_S_URGENT_COLLAPSE,
        SWM_S_URGENT_ENABLED,
        SWM_S_VERBOSE_LAYOUT,
+       SWM_S_WARP_FOCUS,
        SWM_S_WARP_POINTER,
        SWM_S_WINDOW_CLASS_ENABLED,
        SWM_S_WINDOW_INSTANCE_ENABLED,
        SWM_S_WINDOW_NAME_ENABLED,
+       SWM_S_WORKSPACE_CLAMP,
        SWM_S_WORKSPACE_LIMIT,
        SWM_S_WORKSPACE_NAME,
 };
@@ -8089,7 +8136,7 @@ setconfvalue(const char *selector, const char *value, int flags)
 {
        struct workspace        *ws;
        int                     i, ws_id, num_screens;
-       char                    *b, *str, s[1024];
+       char                    *b, *str, *sp, s[1024];
 
        switch (flags) {
        case SWM_S_BAR_ACTION:
@@ -8134,11 +8181,11 @@ setconfvalue(const char *selector, const char *value, int flags)
                if (!bar_font_legacy)
                        break;
 
-               if ((str = strdup(value)) == NULL)
+               if ((sp = str = strdup(value)) == NULL)
                        err(1, "setconfvalue: strdup");
 
                /* If there are any non-XLFD entries, switch to Xft mode. */
-               while ((b = strsep(&str, ",")) != NULL) {
+               while ((b = strsep(&sp, ",")) != NULL) {
                        if (*b == '\0')
                                continue;
                        if (!isxlfd(b)) {
@@ -8294,6 +8341,9 @@ setconfvalue(const char *selector, const char *value, int flags)
                                layouts[i].l_string = plain_stacker;
                }
                break;
+       case SWM_S_WARP_FOCUS:
+               warp_focus = (atoi(value) != 0);
+               break;
        case SWM_S_WARP_POINTER:
                warp_pointer = (atoi(value) != 0);
                break;
@@ -8306,6 +8356,9 @@ setconfvalue(const char *selector, const char *value, int flags)
        case SWM_S_WINDOW_NAME_ENABLED:
                window_name_enabled = (atoi(value) != 0);
                break;
+       case SWM_S_WORKSPACE_CLAMP:
+               workspace_clamp = (atoi(value) != 0);
+               break;
        case SWM_S_WORKSPACE_LIMIT:
                workspace_limit = atoi(value);
                if (workspace_limit > SWM_WS_MAX)
@@ -8369,48 +8422,6 @@ setconfmodkey(const char *selector, const char *value, int flags)
        return (0);
 }
 
-int
-setconfmousebuttonmove(const char *selector, const char *value, int flags)
-{
-       /* suppress unused warnings since vars are needed */
-       (void)selector;
-       (void)flags;
-
-       if (strncasecmp(value, "But1", strlen("But1")) == 0) {
-               if (!update_mousebutton(0, XCB_BUTTON_INDEX_1))
-                       return (1);
-       } else if (strncasecmp(value, "But2", strlen("But2")) == 0) {
-               if (!update_mousebutton(0, XCB_BUTTON_INDEX_2))
-                       return (1);
-       } else if (strncasecmp(value, "But3", strlen("But3")) == 0) {
-               if (!update_mousebutton(0, XCB_BUTTON_INDEX_3))
-                       return (1);
-       } else
-               return (1);
-       return (0);
-}
-
-int
-setconfmousebuttonresize(const char *selector, const char *value, int flags)
-{
-       /* suppress unused warnings since vars are needed */
-       (void)selector;
-       (void)flags;
-
-       if (strncasecmp(value, "But1", strlen("But1")) == 0) {
-               if (!update_mousebutton(1, XCB_BUTTON_INDEX_1))
-                       return (1);
-       } else if (strncasecmp(value, "But2", strlen("But2")) == 0) {
-               if (!update_mousebutton(1, XCB_BUTTON_INDEX_2))
-                       return (1);
-       } else if (strncasecmp(value, "But3", strlen("But3")) == 0) {
-               if (!update_mousebutton(1, XCB_BUTTON_INDEX_3))
-                       return (1);
-       } else
-               return (1);
-       return (0);
-}
-
 int
 setconfcolor(const char *selector, const char *value, int flags)
 {
@@ -8469,7 +8480,7 @@ setautorun(const char *selector, const char *value, int flags)
 {
        int                     ws_id;
        char                    s[1024];
-       char                    *ap, *sp;
+       char                    *ap, *sp, *str;
        union arg               a;
        int                     argc = 0;
        pid_t                   pid;
@@ -8489,7 +8500,7 @@ setautorun(const char *selector, const char *value, int flags)
        if (ws_id < 0 || ws_id >= workspace_limit)
                errx(1, "autorun: invalid workspace %d", ws_id + 1);
 
-       sp = expand_tilde((char *)&s);
+       sp = str = expand_tilde((char *)&s);
 
        /*
         * This is a little intricate
@@ -8508,7 +8519,7 @@ setautorun(const char *selector, const char *value, int flags)
                        err(1, "setautorun: realloc");
                a.argv[argc - 1] = ap;
        }
-       free(sp);
+       free(str);
 
        if ((a.argv = realloc(a.argv, (argc + 1) * sizeof(char *))) == NULL)
                err(1, "setautorun: realloc");
@@ -8656,12 +8667,10 @@ struct config_option configopt[] = {
        { "focus_mode",                 setconfvalue,   SWM_S_FOCUS_MODE },
        { "iconic_enabled",             setconfvalue,   SWM_S_ICONIC_ENABLED },
        { "java_workaround",            setconfvalue,   SWM_S_JAVA_WORKAROUND },
-       { "maximize_hide_bar",          setconfvalue,   SWM_S_MAXIMIZE_HIDE_BAR },
        { "keyboard_mapping",           setkeymapping,  0 },
        { "layout",                     setlayout,      0 },
+       { "maximize_hide_bar",          setconfvalue,   SWM_S_MAXIMIZE_HIDE_BAR },
        { "modkey",                     setconfmodkey,  0 },
-       { "move_button",                setconfmousebuttonmove, 0 },
-       { "resize_button",              setconfmousebuttonresize, 0 },
        { "program",                    setconfspawn,   0 },
        { "quirk",                      setconfquirk,   0 },
        { "region",                     setconfregion,  0 },
@@ -8678,10 +8687,12 @@ struct config_option configopt[] = {
        { "urgent_collapse",            setconfvalue,   SWM_S_URGENT_COLLAPSE },
        { "urgent_enabled",             setconfvalue,   SWM_S_URGENT_ENABLED },
        { "verbose_layout",             setconfvalue,   SWM_S_VERBOSE_LAYOUT },
+       { "warp_focus",                 setconfvalue,   SWM_S_WARP_FOCUS },
        { "warp_pointer",               setconfvalue,   SWM_S_WARP_POINTER },
        { "window_class_enabled",       setconfvalue,   SWM_S_WINDOW_CLASS_ENABLED },
        { "window_instance_enabled",    setconfvalue,   SWM_S_WINDOW_INSTANCE_ENABLED },
        { "window_name_enabled",        setconfvalue,   SWM_S_WINDOW_NAME_ENABLED },
+       { "workspace_clamp",            setconfvalue,   SWM_S_WORKSPACE_CLAMP },
        { "workspace_limit",            setconfvalue,   SWM_S_WORKSPACE_LIMIT },
        { "name",                       setconfvalue,   SWM_S_WORKSPACE_NAME },
 };
@@ -8827,12 +8838,6 @@ conf_load(const char *filename, int keymapping)
        if (line)
                free(line);
        fclose(config);
-
-       if (mouse_button_move == mouse_button_resize) {
-               add_startup_exception("%s: move and resize mouse buttons match",
-                   filename);
-       }
-
        DNPRINTF(SWM_D_CONF, "conf_load: end\n");
 
        return (0);
@@ -9230,17 +9235,13 @@ out:
 void
 free_window(struct ws_win *win)
 {
-       DNPRINTF(SWM_D_MISC, "free_window: win %#x\n", win->id);
+       DNPRINTF(SWM_D_MISC, "free_window: win %#x\n", WINID(win));
 
        if (win == NULL)
                return;
 
-       TAILQ_REMOVE(&win->ws->unmanagedlist, win, entry);
-
        xcb_icccm_get_wm_class_reply_wipe(&win->ch);
 
-       kill_refs(win);
-
        /* paint memory */
        memset(win, 0xff, sizeof *win); /* XXX kill later */
 
@@ -9253,11 +9254,11 @@ 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;
 
-       DNPRINTF(SWM_D_MISC, "unmanage_window: win %#x\n", win->id);
-
        if (TRANS(win)) {
                parent = find_window(win->transient);
                if (parent)
@@ -9630,35 +9631,41 @@ destroynotify(xcb_destroy_notify_event_t *e)
 
        DNPRINTF(SWM_D_EVENT, "destroynotify: win %#x\n", e->window);
 
-       if ((win = find_window(e->window)) == NULL) {
-               if ((win = find_unmanaged_window(e->window)) == NULL)
-                       return;
-               free_window(win);
-               return;
-       }
-
-       if (focus_mode != SWM_FOCUS_FOLLOW) {
-               /* If we were focused, make sure we focus on something else. */
-               if (win == win->ws->focus)
-                       win->ws->focus_pending = get_focus_prev(win);
-       }
+       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);
+               }
 
-       unmanage_window(win);
-       stack();
+               unmanage_window(win);
+               stack();
 
-       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(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);
+                       }
                }
+
+               kill_refs(win);
+               focus_flush();
+       } else {
+               win = find_unmanaged_window(e->window);
        }
 
-       free_window(win);
+       if (win) {
+               /* unmanage_window() puts win into unmanaged list. */
+               TAILQ_REMOVE(&win->ws->unmanagedlist, win, entry);
+               free_window(win);
+       }
 
-       focus_flush();
+       DNPRINTF(SWM_D_EVENT, "destroynotify: done.\n");
 }
 
 #ifdef SWM_DEBUG
@@ -10692,8 +10699,7 @@ setup_screens(void)
        xcb_randr_query_version_reply_t         *r;
 
        num_screens = get_screen_count();
-       if ((screens = calloc(num_screens,
-            sizeof(struct swm_screen))) == NULL)
+       if ((screens = calloc(num_screens, sizeof(struct swm_screen))) == NULL)
                err(1, "setup_screens: calloc: failed to allocate memory for "
                    "screens");
 
@@ -10806,6 +10812,9 @@ setup_globals(void)
 void
 shutdown_cleanup(void)
 {
+       struct swm_region       *r;
+       struct ws_win           *w;
+       struct workspace        *ws;
        int                     i, num_screens;
 
        /* disable alarm because the following code may not be interrupted */
@@ -10818,10 +10827,16 @@ shutdown_cleanup(void)
 
        cursors_cleanup();
 
+       clear_quirks();
+       clear_spawns();
+       clear_keys();
+
        teardown_ewmh();
 
        num_screens = get_screen_count();
        for (i = 0; i < num_screens; ++i) {
+               int j;
+
                xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT,
                    screens[i].root, XCB_CURRENT_TIME);
 
@@ -10833,13 +10848,51 @@ shutdown_cleanup(void)
                        XftColorFree(display, DefaultVisual(display, i),
                            DefaultColormap(display, i), &search_font_color);
                }
+
+               for (j = 0; j < SWM_S_COLOR_MAX; ++j) {
+                       free(screens[i].c[j].name);
+               }
+
+               /* Free window memory. */
+               for (j = 0; j < SWM_WS_MAX; ++j) {
+                       ws = &screens[i].ws[j];
+                       free(ws->name);
+
+                       while ((w = TAILQ_FIRST(&ws->winlist)) != NULL) {
+                               TAILQ_REMOVE(&ws->winlist, w, entry);
+                               free_window(w);
+                       }
+
+                       while ((w = TAILQ_FIRST(&ws->unmanagedlist)) != NULL) {
+                               TAILQ_REMOVE(&ws->unmanagedlist, w, entry);
+                               free_window(w);
+                       }
+               }
+
+               /* Free region memory. */
+               while ((r = TAILQ_FIRST(&screens[i].rl)) != NULL) {
+                       TAILQ_REMOVE(&screens[i].rl, r, entry);
+                       free(r->bar);
+                       free(r);
+               }
+
+               while ((r = TAILQ_FIRST(&screens[i].orl)) != NULL) {
+                       TAILQ_REMOVE(&screens[i].rl, r, entry);
+                       free(r->bar);
+                       free(r);
+               }
        }
+       free(screens);
 
-       if (bar_font_legacy)
+       free(bar_format);
+       free(bar_fonts);
+       free(clock_format);
+       free(startup_exception);
+
+       if (bar_fs)
                XFreeFontSet(display, bar_fs);
-       else {
+       if (bar_font)
                XftFontClose(display, bar_font);
-       }
 
        xcb_key_symbols_free(syms);
        xcb_flush(conn);
@@ -10916,14 +10969,14 @@ event_handle(xcb_generic_event_t *evt)
 int
 main(int argc, char *argv[])
 {
-       struct swm_region       *r;
-       char                    conf[PATH_MAX], *cfile = NULL;
-       struct stat             sb;
-       int                     xfd, i, num_screens;
+       struct pollfd           pfd[2];
        struct sigaction        sact;
+       struct stat             sb;
+       struct passwd           *pwd;
+       struct swm_region       *r;
        xcb_generic_event_t     *evt;
-       int                     num_readable;
-       struct pollfd           pfd[2];
+       int                     xfd, i, num_screens, num_readable;
+       char                    conf[PATH_MAX], *cfile = NULL;
        bool                    stdin_ready = false, startup = true;
 
        /* suppress unused warning since var is needed */
@@ -11090,8 +11143,10 @@ noconfig:
 
                num_readable = poll(pfd, bar_extra ? 2 : 1, 1000);
                if (num_readable == -1) {
-                       DNPRINTF(SWM_D_MISC, "poll failed: %s", strerror(errno));
-               } else if (num_readable > 0 && bar_extra && pfd[1].revents & POLLIN) {
+                       DNPRINTF(SWM_D_MISC, "poll failed: %s",
+                           strerror(errno));
+               } else if (num_readable > 0 && bar_extra &&
+                   pfd[1].revents & POLLIN) {
                        stdin_ready = true;
                }