]> code.delx.au - spectrwm/blobdiff - spectrwm.c
Resolve memory leaks on exit
[spectrwm] / spectrwm.c
index b60cbc734e86078e2e122fd71aa0ca2e981284ba..2cc0d246b0fe30f4ffc4a69a3027bdcccb5b87c7 100644 (file)
@@ -1,14 +1,15 @@
 /*
- * Copyright (c) 2009-2012 Marco Peereboom <marco@peereboom.us>
+ * Copyright (c) 2009-2015 Marco Peereboom <marco@peereboom.us>
  * Copyright (c) 2009-2011 Ryan McBride <mcbride@countersiege.com>
  * Copyright (c) 2009 Darrin Chandler <dwchandler@stilyagin.com>
  * Copyright (c) 2009 Pierre-Yves Ritschard <pyr@spootnik.org>
  * Copyright (c) 2010 Tuukka Kataja <stuge@xor.fi>
  * Copyright (c) 2011 Jason L. Wright <jason@thought.net>
- * Copyright (c) 2011-2014 Reginald Kennedy <rk@rejii.com>
+ * Copyright (c) 2011-2015 Reginald Kennedy <rk@rejii.com>
  * Copyright (c) 2011-2012 Lawrence Teo <lteo@lteo.net>
  * Copyright (c) 2011-2012 Tiago Cunha <tcunha@gmx.com>
- * Copyright (c) 2012-2013 David Hill <dhill@mindcry.org>
+ * Copyright (c) 2012-2015 David Hill <dhill@mindcry.org>
+ * Copyright (c) 2014-2015 Yuri D'Elia <yuri.delia@eurac.edu>
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -77,6 +78,7 @@
 #include <ctype.h>
 #include <err.h>
 #include <errno.h>
+#include <poll.h>
 #include <fcntl.h>
 #include <locale.h>
 #include <paths.h>
@@ -331,6 +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;
+bool                   workspace_clamp = false;
 
 /* dmenu search */
 struct swm_region      *search_r;
@@ -400,7 +405,9 @@ char                 *bar_format = NULL;
 bool            stack_enabled = true;
 bool            clock_enabled = true;
 bool            iconic_enabled = false;
+bool            maximize_hide_bar = false;
 bool            urgent_enabled = false;
+bool            urgent_collapse = false;
 char           *clock_format = NULL;
 bool            window_class_enabled = false;
 bool            window_instance_enabled = false;
@@ -478,7 +485,7 @@ struct ws_win {
        bool                    can_delete;
        bool                    take_focus;
        bool                    java;
-       unsigned long           quirks;
+       uint32_t                quirks;
        struct workspace        *ws;    /* always valid */
        struct swm_screen       *s;     /* always valid, never changes */
        xcb_size_hints_t        sh;
@@ -622,6 +629,7 @@ union arg {
 #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)
 #define SWM_ARG_ID_CYCLEWS_DOWN        (41)
 #define SWM_ARG_ID_CYCLERG_UP  (42)
@@ -650,6 +658,8 @@ union arg {
 #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)
+#define SWM_ARG_ID_CYCLERG_MOVE_DOWN   (113)
        char                    **argv;
 };
 
@@ -662,18 +672,21 @@ struct quirk {
        regex_t                 regex_class;
        regex_t                 regex_instance;
        regex_t                 regex_name;
-       unsigned long           quirk;
+       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_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) /* Remove border when floating/unfocused */
 };
 TAILQ_HEAD(quirk_list, quirk);
 struct quirk_list              quirks = TAILQ_HEAD_INITIALIZER(quirks);
@@ -877,6 +890,8 @@ enum keyfuncid {
        KF_RG_7,
        KF_RG_8,
        KF_RG_9,
+       KF_RG_MOVE_NEXT,
+       KF_RG_MOVE_PREV,
        KF_RG_NEXT,
        KF_RG_PREV,
        KF_SCREEN_NEXT,
@@ -884,6 +899,7 @@ enum keyfuncid {
        KF_SEARCH_WIN,
        KF_SEARCH_WORKSPACE,
        KF_SPAWN_CUSTOM,
+       KF_STACK_BALANCE,
        KF_STACK_INC,
        KF_STACK_DEC,
        KF_STACK_RESET,
@@ -964,9 +980,12 @@ 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    buttonpress(xcb_button_press_event_t *);
+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);
@@ -1037,6 +1056,7 @@ char      *get_source_type_label(uint32_t);
 char   *get_stack_mode_name(uint8_t);
 #endif
 int32_t         get_swm_ws(xcb_window_t);
+bool    get_urgent(struct ws_win *);
 char   *get_win_name(xcb_window_t);
 uint8_t         get_win_state(xcb_window_t);
 void    get_wm_protocols(struct ws_win *);
@@ -1059,6 +1079,7 @@ void       kill_refs(struct ws_win *);
 void    leavenotify(xcb_leave_notify_event_t *);
 #endif
 void    load_float_geom(struct ws_win *);
+void    lower_window(struct ws_win *);
 struct ws_win  *manage_window(xcb_window_t, int, bool);
 void    map_window(struct ws_win *);
 void    mapnotify(xcb_map_notify_event_t *);
@@ -1072,7 +1093,7 @@ uint32_t name_to_pixel(int, const char *);
 void    name_workspace(struct swm_region *, union arg *);
 void    new_region(struct swm_screen *, int, int, int, int);
 int     parsekeys(const char *, unsigned int, unsigned int *, KeySym *);
-int     parsequirks(const char *, unsigned long *);
+int     parsequirks(const char *, uint32_t *, int *);
 int     parse_rgb(const char *, uint16_t *, uint16_t *, uint16_t *);
 void    pressbutton(struct swm_region *, union arg *);
 void    priorws(struct swm_region *, union arg *);
@@ -1081,10 +1102,10 @@ void     print_win_geom(xcb_window_t);
 #endif
 void    propertynotify(xcb_property_notify_event_t *);
 void    quirk_free(struct quirk *);
-void    quirk_insert(const char *, const char *, const char *,unsigned long);
+void    quirk_insert(const char *, const char *, const char *, uint32_t, int);
 void    quirk_remove(struct quirk *);
 void    quirk_replace(struct quirk *, const char *, const char *, const char *,
-            unsigned long);
+            uint32_t, int);
 void    quit(struct swm_region *, union arg *);
 void    raise_toggle(struct swm_region *, union arg *);
 void    raise_window(struct ws_win *);
@@ -1119,7 +1140,7 @@ int        setconfvalue(const char *, const char *, int);
 void    setkeybinding(unsigned int, KeySym, enum keyfuncid, const char *);
 int     setkeymapping(const char *, const char *, int);
 int     setlayout(const char *, const char *, int);
-void    setquirk(const char *, const char *, const char *,unsigned long);
+void    setquirk(const char *, const char *, const char *, uint32_t, int);
 void    setscreencolor(const char *, int, int);
 void    setspawn(const char *, const char *, int);
 void    setup_ewmh(void);
@@ -2164,6 +2185,22 @@ bar_window_name(char *s, size_t sz, struct swm_region *r)
        free(title);
 }
 
+bool
+get_urgent(struct ws_win *win)
+{
+       xcb_icccm_wm_hints_t            hints;
+       xcb_get_property_cookie_t       c;
+       bool                            urgent = false;
+
+       if (win) {
+               c = xcb_icccm_get_wm_hints(conn, win->id);
+               if (xcb_icccm_get_wm_hints_reply(conn, c, &hints, NULL))
+                       urgent = xcb_icccm_wm_hints_get_urgency(&hints);
+       }
+
+       return urgent;
+}
+
 void
 bar_urgent(char *s, size_t sz)
 {
@@ -2171,8 +2208,6 @@ bar_urgent(char *s, size_t sz)
        int                     i, j, num_screens;
        bool                    urgent[SWM_WS_MAX];
        char                    b[8];
-       xcb_get_property_cookie_t       c;
-       xcb_icccm_wm_hints_t    hints;
 
        for (i = 0; i < workspace_limit; i++)
                urgent[i] = false;
@@ -2180,22 +2215,20 @@ bar_urgent(char *s, size_t sz)
        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) {
-                               c = xcb_icccm_get_wm_hints(conn, win->id);
-                               if (xcb_icccm_get_wm_hints_reply(conn, c,
-                                   &hints, NULL) == 0)
-                                       continue;
-                               if (hints.flags & XCB_ICCCM_WM_HINT_X_URGENCY)
+                       TAILQ_FOREACH(win, &screens[i].ws[j].winlist, entry)
+                               if (get_urgent(win))
                                        urgent[j] = true;
-                       }
 
        for (i = 0; i < workspace_limit; i++) {
-               if (urgent[i])
+               if (urgent[i]) {
                        snprintf(b, sizeof b, "%d ", i + 1);
-               else
-                       snprintf(b, sizeof b, "- ");
-               strlcat(s, b, sz);
+                       strlcat(s, b, sz);
+               } else if (!urgent_collapse) {
+                       strlcat(s, "- ", sz);
+               }
        }
+       if(urgent_collapse && s[0])
+               s[strlen(s) - 1] = 0;
 }
 
 void
@@ -3001,14 +3034,74 @@ quit(struct swm_region *r, union arg *args)
        running = 0;
 }
 
+void
+lower_window(struct ws_win *win)
+{
+       struct ws_win           *target = NULL;
+       struct workspace        *ws;
+
+       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;
+               if (ws->cur_layout == &layouts[SWM_MAX_STACK])
+                       break;
+               if (TRANS(win)) {
+                       if (win->transient == target->transient)
+                               continue;
+                       if (win->transient == target->id)
+                               break;
+               }
+               if (FULLSCREEN(target))
+                       continue;
+               if (FULLSCREEN(win))
+                       break;
+               if (MAXIMIZED(target))
+                       continue;
+               if (MAXIMIZED(win))
+                       break;
+               if (ABOVE(target) || TRANS(target))
+                       continue;
+               if (ABOVE(win) || TRANS(win))
+                       break;
+       }
+
+       /* Change stack position. */
+       TAILQ_REMOVE(&ws->stack, win, stack_entry);
+       if (target)
+               TAILQ_INSERT_BEFORE(target, win, stack_entry);
+       else
+               TAILQ_INSERT_TAIL(&ws->stack, win, stack_entry);
+
+       update_win_stacking(win);
+
+#ifdef SWM_DEBUG
+       if (swm_debug & SWM_D_STACK) {
+               DPRINTF("=== stacking order (top down) === \n");
+               TAILQ_FOREACH(target, &ws->stack, stack_entry) {
+                       DPRINTF("win %#x, fs: %s, maximized: %s, above: %s, "
+                           "iconic: %s\n", target->id, YESNO(FULLSCREEN(target)),
+                           YESNO(MAXIMIZED(target)), YESNO(ABOVE(target)),
+                           YESNO(ICONIC(target)));
+               }
+       }
+#endif
+       DNPRINTF(SWM_D_EVENT, "lower_window: done\n");
+}
+
 void
 raise_window(struct ws_win *win)
 {
        struct ws_win           *target = NULL;
-       struct swm_region       *r;
        struct workspace        *ws;
 
-       if (win == NULL || (r = win->ws->r) == NULL)
+       if (win == NULL)
                return;
        ws = win->ws;
 
@@ -3030,23 +3123,25 @@ raise_window(struct ws_win *win)
                        break;
                if (MAXIMIZED(target))
                        continue;
-               if (ABOVE(win) || TRANS(win))
+               if (ABOVE(win) || TRANS(win) ||
+                   (win->ws->focus == win && ws->always_raise))
                        break;
                if (!ABOVE(target) && !TRANS(target))
                        break;
        }
 
-       if (target != NULL) {
-               /* Change stack position. */
-               TAILQ_REMOVE(&ws->stack, win, stack_entry);
+       TAILQ_REMOVE(&ws->stack, win, stack_entry);
+       if (target)
                TAILQ_INSERT_BEFORE(target, win, stack_entry);
-               update_win_stacking(win);
-       }
+       else
+               TAILQ_INSERT_TAIL(&ws->stack, win, stack_entry);
+
+       update_win_stacking(win);
 
 #ifdef SWM_DEBUG
        if (swm_debug & SWM_D_STACK) {
                DPRINTF("=== stacking order (top down) === \n");
-               TAILQ_FOREACH(target, &r->ws->stack, stack_entry) {
+               TAILQ_FOREACH(target, &ws->stack, stack_entry) {
                        DPRINTF("win %#x, fs: %s, maximized: %s, above: %s, "
                            "iconic: %s\n", target->id, YESNO(FULLSCREEN(target)),
                            YESNO(MAXIMIZED(target)), YESNO(ABOVE(target)),
@@ -3068,10 +3163,14 @@ update_win_stacking(struct ws_win *win)
                return;
 
        sibling = TAILQ_NEXT(win, stack_entry);
-       if (sibling != NULL && FLOATING(win) == FLOATING(sibling))
+       if (sibling != NULL && (FLOATING(win) == FLOATING(sibling) ||
+           (win->ws->always_raise && win->ws->focus == win)))
                val[0] = sibling->id;
+       else if (FLOATING(win) || (win->ws->always_raise &&
+           win->ws->focus == win))
+               val[0] = r->bar->id;
        else
-               val[0] = FLOATING(win) ? r->bar->id : r->id;
+               val[0] = r->id;
 
        DNPRINTF(SWM_D_EVENT, "update_win_stacking: %#x, sibling %#x\n",
            win->id, val[0]);
@@ -3206,6 +3305,26 @@ get_pointer_win(xcb_window_t root)
        return win;
 }
 
+void
+center_pointer(struct swm_region *r)
+{
+       struct ws_win                   *win;
+
+       if (!warp_pointer || r == NULL)
+               return;
+
+       win = r->ws->focus;
+
+       DNPRINTF(SWM_D_EVENT, "center_pointer: win %#x.\n", WINID(win));
+
+       if (win && win->mapped)
+               xcb_warp_pointer(conn, XCB_NONE, win->id, 0, 0, 0, 0,
+                   WIDTH(win) / 2, HEIGHT(win) / 2);
+       else
+               xcb_warp_pointer(conn, XCB_NONE, r->id, 0, 0, 0, 0,
+                   WIDTH(r) / 2, HEIGHT(r) / 2);
+}
+
 struct swm_region *
 root_to_region(xcb_window_t root, int check)
 {
@@ -3484,6 +3603,19 @@ unfocus_win(struct ws_win *win)
 
        update_window_color(win);
 
+       /* Raise window to "top unfocused position." */
+       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);
 
@@ -3523,6 +3655,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);
                        }
@@ -3611,10 +3753,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();
 
@@ -3765,6 +3917,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);
 
@@ -3773,17 +3939,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;
@@ -3823,6 +3989,7 @@ switchws(struct swm_region *r, union arg *args)
 
        ewmh_update_current_desktop();
 
+       center_pointer(r);
        focus_flush();
        new_ws->state = SWM_WS_STATE_MAPPED;
 
@@ -3919,6 +4086,7 @@ focusrg(struct swm_region *r, union arg *args)
                return;
 
        focus_region(rr);
+       center_pointer(rr);
        focus_flush();
        DNPRINTF(SWM_D_FOCUS, "focusrg: done\n");
 }
@@ -3926,6 +4094,7 @@ focusrg(struct swm_region *r, union arg *args)
 void
 cyclerg(struct swm_region *r, union arg *args)
 {
+       union arg               a;
        struct swm_region       *rr = NULL;
        int                     i, num_screens;
 
@@ -3939,11 +4108,13 @@ cyclerg(struct swm_region *r, union arg *args)
 
        switch (args->id) {
        case SWM_ARG_ID_CYCLERG_UP:
+       case SWM_ARG_ID_CYCLERG_MOVE_UP:
                rr = TAILQ_NEXT(r, entry);
                if (rr == NULL)
                        rr = TAILQ_FIRST(&screens[i].rl);
                break;
        case SWM_ARG_ID_CYCLERG_DOWN:
+       case SWM_ARG_ID_CYCLERG_MOVE_DOWN:
                rr = TAILQ_PREV(r, swm_region_list, entry);
                if (rr == NULL)
                        rr = TAILQ_LAST(&screens[i].rl, swm_region_list);
@@ -3954,8 +4125,22 @@ cyclerg(struct swm_region *r, union arg *args)
        if (rr == NULL)
                return;
 
-       focus_region(rr);
-       focus_flush();
+       switch (args->id) {
+       case SWM_ARG_ID_CYCLERG_UP:
+       case SWM_ARG_ID_CYCLERG_DOWN:
+               focus_region(rr);
+               center_pointer(rr);
+               focus_flush();
+               break;
+       case SWM_ARG_ID_CYCLERG_MOVE_UP:
+       case SWM_ARG_ID_CYCLERG_MOVE_DOWN:
+               a.id = rr->ws->idx;
+               switchws(r, &a);
+               break;
+       default:
+               return;
+       };
+
        DNPRINTF(SWM_D_FOCUS, "cyclerg: done\n");
 }
 
@@ -4088,6 +4273,7 @@ swapwin(struct swm_region *r, union arg *args)
        ewmh_update_client_list();
 
        stack();
+       center_pointer(r);
        focus_flush();
 out:
        DNPRINTF(SWM_D_MOVE, "swapwin: done\n");
@@ -4241,7 +4427,6 @@ focus(struct swm_region *r, union arg *args)
        struct workspace        *ws = NULL;
        union arg               a;
        int                     i;
-       xcb_icccm_wm_hints_t    hints;
 
        if (!(r && r->ws))
                goto out;
@@ -4278,7 +4463,8 @@ focus(struct swm_region *r, union arg *args)
                } while (winfocus && (ICONIC(winfocus) ||
                    winfocus->id == cur_focus->transient ||
                    (cur_focus->transient != XCB_WINDOW_NONE &&
-                   winfocus->transient == cur_focus->transient)));
+                   winfocus->transient == cur_focus->transient) ||
+                   (winfocus->quirks & SWM_Q_NOFOCUSCYCLE)));
                break;
        case SWM_ARG_ID_FOCUSNEXT:
                if (cur_focus == NULL)
@@ -4294,7 +4480,8 @@ focus(struct swm_region *r, union arg *args)
                } while (winfocus && (ICONIC(winfocus) ||
                    winfocus->id == cur_focus->transient ||
                    (cur_focus->transient != XCB_WINDOW_NONE &&
-                   winfocus->transient == cur_focus->transient)));
+                   winfocus->transient == cur_focus->transient) ||
+                   (winfocus->quirks & SWM_Q_NOFOCUSCYCLE)));
                break;
        case SWM_ARG_ID_FOCUSMAIN:
                if (cur_focus == NULL)
@@ -4314,27 +4501,26 @@ focus(struct swm_region *r, union arg *args)
                                head = TAILQ_FIRST(&r->s->ws[(ws->idx + i) %
                                    workspace_limit].winlist);
 
-                       while (head != NULL &&
-                           (head = TAILQ_NEXT(head, entry)) != NULL) {
+                       while (head) {
                                if (head == cur_focus) {
-                                       winfocus = cur_focus;
-                                       break;
-                               }
-                               if (xcb_icccm_get_wm_hints_reply(conn,
-                                   xcb_icccm_get_wm_hints(conn, head->id),
-                                   &hints, NULL) != 0 &&
-                                   xcb_icccm_wm_hints_get_urgency(&hints)) {
+                                       if (i > 0) {
+                                               winfocus = cur_focus;
+                                               break;
+                                       }
+                               } else if (get_urgent(head)) {
                                        winfocus = head;
                                        break;
                                }
+
+                               head = TAILQ_NEXT(head, entry);
                        }
 
-                       if (winfocus != NULL)
+                       if (winfocus)
                                break;
                }
 
                /* Switch ws if new focus is on a different ws. */
-               if (winfocus != NULL && winfocus->ws != ws) {
+               if (winfocus && winfocus->ws != ws) {
                        a.id = winfocus->ws->idx;
                        switchws(r, &a);
                }
@@ -4347,6 +4533,7 @@ focus(struct swm_region *r, union arg *args)
                stack();
 
        focus_win(get_focus_magic(winfocus));
+       center_pointer(r);
        focus_flush();
 
 out:
@@ -4374,6 +4561,7 @@ cycle_layout(struct swm_region *r, union arg *args)
 
        focus_win(get_region_focus(r));
 
+       center_pointer(r);
        focus_flush();
 }
 
@@ -4395,6 +4583,7 @@ stack_config(struct swm_region *r, union arg *args)
                stack();
        bar_draw();
 
+       center_pointer(r);
        focus_flush();
 }
 
@@ -4532,7 +4721,7 @@ update_floater(struct ws_win *win)
 
                win->g = r->g;
 
-               if (bar_enabled && ws->bar_enabled) {
+               if (bar_enabled && ws->bar_enabled && !maximize_hide_bar) {
                        if (!bar_at_bottom)
                                Y(win) += bar_height;
                        HEIGHT(win) -= bar_height;
@@ -4550,9 +4739,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)) {
@@ -4758,7 +4949,7 @@ stack_master(struct workspace *ws, struct swm_geometry *g, int rot, bool flip)
                        win_g.y += last_h + 2 * border_width + tile_gap;
 
                if (disable_border && !(bar_enabled && ws->bar_enabled) &&
-                   winno == 1){
+                   winno == 1) {
                        bordered = false;
                        win_g.w += 2 * border_width;
                        win_g.h += 2 * border_width;
@@ -4842,6 +5033,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;
@@ -4892,6 +5086,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;
@@ -4971,12 +5168,13 @@ max_stack(struct workspace *ws, struct swm_geometry *g)
                if (X(w) != gg.x || Y(w) != gg.y || WIDTH(w) != gg.w ||
                    HEIGHT(w) != gg.h) {
                        w->g = gg;
-                       if (bar_enabled && ws->bar_enabled){
-                               w->bordered = true;
-                       } else {
+
+                       if (disable_border && !(bar_enabled && ws->bar_enabled)) {
                                w->bordered = false;
                                WIDTH(w) += 2 * border_width;
                                HEIGHT(w) += 2 * border_width;
+                       } else {
+                               w->bordered = true;
                        }
 
                        update_window(w);
@@ -5106,6 +5304,7 @@ send_to_ws(struct swm_region *r, union arg *args)
                }
        }
 
+       center_pointer(r);
        focus_flush();
 }
 
@@ -5218,7 +5417,7 @@ pressbutton(struct swm_region *r, union arg *args)
 void
 raise_toggle(struct swm_region *r, union arg *args)
 {
-       /* suppress unused warning since var is needed */
+       /* Suppress warning. */
        (void)args;
 
        if (r == NULL || r->ws == NULL)
@@ -5229,9 +5428,8 @@ raise_toggle(struct swm_region *r, union arg *args)
 
        r->ws->always_raise = !r->ws->always_raise;
 
-       /* bring floaters back to top */
-       if (!r->ws->always_raise)
-               stack();
+       /* Update focused win stacking order based on new always_raise value. */
+       raise_window(r->ws->focus);
 
        focus_flush();
 }
@@ -5240,7 +5438,8 @@ void
 iconify(struct swm_region *r, union arg *args)
 {
        struct ws_win           *w;
-       /* suppress unused warning since var is needed */
+
+       /* Suppress warning. */
        (void)args;
 
        if ((w = r->ws->focus) == NULL)
@@ -5693,10 +5892,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
@@ -5921,6 +6123,7 @@ maximize_toggle(struct swm_region *r, union arg *args)
        if (w == w->ws->focus)
                focus_win(w);
 
+       center_pointer(r);
        focus_flush();
        DNPRINTF(SWM_D_MISC, "maximize_toggle: done\n");
 }
@@ -5952,6 +6155,7 @@ floating_toggle(struct swm_region *r, union arg *args)
        if (w == w->ws->focus)
                focus_win(w);
 
+       center_pointer(r);
        focus_flush();
        DNPRINTF(SWM_D_MISC, "floating_toggle: done\n");
 }
@@ -6300,6 +6504,7 @@ resize_step(struct swm_region *r, union arg *args)
                return;
 
        resize(win, args);
+       center_pointer(r);
        focus_flush();
 }
 
@@ -6499,6 +6704,7 @@ move_step(struct swm_region *r, union arg *args)
                return;
 
        move(win, args);
+       center_pointer(r);
        focus_flush();
 }
 
@@ -6575,6 +6781,8 @@ struct keyfunc {
        { "rg_7",               focusrg,        {.id = 6} },
        { "rg_8",               focusrg,        {.id = 7} },
        { "rg_9",               focusrg,        {.id = 8} },
+       { "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} },
@@ -6582,6 +6790,7 @@ struct keyfunc {
        { "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} },
@@ -6946,6 +7155,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)
 {
@@ -7110,6 +7329,8 @@ parsekeys(const char *keystr, unsigned int currmod, unsigned int *mod, KeySym *k
                        *mod |= XCB_MOD_MASK_3;
                else if (strncmp(name, "Mod4", SWM_MODNAME_SIZE) == 0)
                        *mod |= XCB_MOD_MASK_4;
+               else if (strncmp(name, "Mod5", SWM_MODNAME_SIZE) == 0)
+                       *mod |= XCB_MOD_MASK_5;
                else if (strncasecmp(name, "SHIFT", SWM_MODNAME_SIZE) == 0)
                        *mod |= XCB_MOD_MASK_SHIFT;
                else if (strncasecmp(name, "CONTROL", SWM_MODNAME_SIZE) == 0)
@@ -7557,12 +7778,14 @@ const char *quirkname[] = {
        "OBEYAPPFOCUSREQ",
        "IGNOREPID",
        "IGNORESPAWNWS",
+       "NOFOCUSCYCLE",
+       "MINIMALBORDER",
 };
 
-/* SWM_Q_WS: retain '|' for back compat for now (2009-08-11) */
-#define SWM_Q_WS               "\n|+ \t"
+/* SWM_Q_DELIM: retain '|' for back compat for now (2009-08-11) */
+#define SWM_Q_DELIM            "\n|+ \t"
 int
-parsequirks(const char *qstr, unsigned long *quirk)
+parsequirks(const char *qstr, uint32_t *quirk, int *ws)
 {
        char                    *str, *cp, *name;
        int                     i;
@@ -7575,9 +7798,16 @@ parsequirks(const char *qstr, unsigned long *quirk)
 
        cp = str;
        *quirk = 0;
-       while ((name = strsep(&cp, SWM_Q_WS)) != NULL) {
+       while ((name = strsep(&cp, SWM_Q_DELIM)) != NULL) {
                if (cp)
-                       cp += (long)strspn(cp, SWM_Q_WS);
+                       cp += (long)strspn(cp, SWM_Q_DELIM);
+
+               if (sscanf(name, "WS[%d]", ws) == 1) {
+                       if (*ws > 0)
+                               *ws -= 1;
+                       continue;
+               }
+
                for (i = 0; i < LENGTH(quirkname); i++) {
                        if (strncasecmp(name, quirkname[i],
                            SWM_QUIRK_LEN) == 0) {
@@ -7606,14 +7836,14 @@ parsequirks(const char *qstr, unsigned long *quirk)
 
 void
 quirk_insert(const char *class, const char *instance, const char *name,
-    unsigned long quirk)
+    uint32_t quirk, int ws)
 {
        struct quirk            *qp;
        char                    *str;
        bool                    failed = false;
 
        DNPRINTF(SWM_D_QUIRK, "quirk_insert: class: %s, instance: %s, name: %s,"
-           " value: %lu\n", class, instance, name, quirk);
+           " value: %u, ws: %d\n", class, instance, name, quirk, ws);
 
        if ((qp = malloc(sizeof *qp)) == NULL)
                err(1, "quirk_insert: malloc");
@@ -7660,6 +7890,7 @@ quirk_insert(const char *class, const char *instance, const char *name,
                quirk_free(qp);
        } else {
                qp->quirk = quirk;
+               qp->ws = ws;
                TAILQ_INSERT_TAIL(&quirks, qp, entry);
        }
        DNPRINTF(SWM_D_QUIRK, "quirk_insert: leave\n");
@@ -7668,7 +7899,7 @@ quirk_insert(const char *class, const char *instance, const char *name,
 void
 quirk_remove(struct quirk *qp)
 {
-       DNPRINTF(SWM_D_QUIRK, "quirk_remove: %s:%s [%lu]\n", qp->class,
+       DNPRINTF(SWM_D_QUIRK, "quirk_remove: %s:%s [%u]\n", qp->class,
            qp->name, qp->quirk);
 
        TAILQ_REMOVE(&quirks, qp, entry);
@@ -7689,45 +7920,56 @@ 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, unsigned long quirk)
+    const char *name, uint32_t quirk, int ws)
 {
-       DNPRINTF(SWM_D_QUIRK, "quirk_replace: %s:%s:%s [%lu]\n", qp->class,
-           qp->instance, qp->name, qp->quirk);
+       DNPRINTF(SWM_D_QUIRK, "quirk_replace: %s:%s:%s [%u], ws: %d\n", qp->class,
+           qp->instance, qp->name, qp->quirk, qp->ws);
 
        quirk_remove(qp);
-       quirk_insert(class, instance, name, quirk);
+       quirk_insert(class, instance, name, quirk, ws);
 
        DNPRINTF(SWM_D_QUIRK, "quirk_replace: leave\n");
 }
 
 void
 setquirk(const char *class, const char *instance, const char *name,
-    unsigned long quirk)
+    uint32_t quirk, int ws)
 {
        struct quirk            *qp;
 
-       DNPRINTF(SWM_D_QUIRK, "setquirk: enter %s:%s:%s [%lu]\n", class,
-           instance, name, quirk);
+       DNPRINTF(SWM_D_QUIRK, "setquirk: enter %s:%s:%s [%u], ws: %d\n", class,
+           instance, name, quirk, ws);
 
        /* Remove/replace existing quirk. */
        TAILQ_FOREACH(qp, &quirks, entry) {
                if (strcmp(qp->class, class) == 0 &&
                    strcmp(qp->instance, instance) == 0 &&
                    strcmp(qp->name, name) == 0) {
-                       if (quirk == 0)
+                       if (quirk == 0 && ws == -1)
                                quirk_remove(qp);
                        else
-                               quirk_replace(qp, class, instance, name, quirk);
+                               quirk_replace(qp, class, instance, name, quirk,
+                                   ws);
                        DNPRINTF(SWM_D_QUIRK, "setquirk: leave\n");
                        return;
                }
        }
 
-       /* Only insert if quirk is not NONE. */
-       if (quirk)
-               quirk_insert(class, instance, name, quirk);
+       /* Only insert if quirk is not NONE or forced ws is set. */
+       if (quirk || ws != -1)
+               quirk_insert(class, instance, name, quirk, ws);
 
        DNPRINTF(SWM_D_QUIRK, "setquirk: leave\n");
 }
@@ -7753,8 +7995,8 @@ setconfquirk(const char *selector, const char *value, int flags)
 {
        char                    *str, *cp, *class;
        char                    *instance = NULL, *name = NULL;
-       int                     retval, count = 0;
-       unsigned long           qrks;
+       int                     retval, count = 0, ws = -1;
+       uint32_t                qrks;
 
        /* suppress unused warning since var is needed */
        (void)flags;
@@ -7797,8 +8039,8 @@ setconfquirk(const char *selector, const char *value, int flags)
        DNPRINTF(SWM_D_CONF, "setconfquirk: class: %s, instance: %s, "
            "name: %s\n", class, instance, name);
 
-       if ((retval = parsequirks(value, &qrks)) == 0)
-               setquirk(class, instance, name, qrks);
+       if ((retval = parsequirks(value, &qrks, &ws)) == 0)
+               setquirk(class, instance, name, qrks, ws);
 
        free(str);
        return (retval);
@@ -7807,19 +8049,32 @@ setconfquirk(const char *selector, const char *value, int flags)
 void
 setup_quirks(void)
 {
-       setquirk("MPlayer",             "xv",           ".*",   SWM_Q_FLOAT | SWM_Q_FULLSCREEN | SWM_Q_FOCUSPREV);
-       setquirk("OpenOffice.org 3.2",  "VCLSalFrame",  ".*",   SWM_Q_FLOAT);
-       setquirk("Firefox-bin",         "firefox-bin",  ".*",   SWM_Q_TRANSSZ);
-       setquirk("Firefox",             "Dialog",       ".*",   SWM_Q_FLOAT);
-       setquirk("Gimp",                "gimp",         ".*",   SWM_Q_FLOAT | SWM_Q_ANYWHERE);
-       setquirk("XTerm",               "xterm",        ".*",   SWM_Q_XTERM_FONTADJ);
-       setquirk("xine",                "Xine Window",  ".*",   SWM_Q_FLOAT | SWM_Q_ANYWHERE);
-       setquirk("Xitk",                "Xitk Combo",   ".*",   SWM_Q_FLOAT | SWM_Q_ANYWHERE);
-       setquirk("xine",                "xine Panel",   ".*",   SWM_Q_FLOAT | SWM_Q_ANYWHERE);
-       setquirk("Xitk",                "Xine Window",  ".*",   SWM_Q_FLOAT | SWM_Q_ANYWHERE);
-       setquirk("xine",                "xine Video Fullscreen Window", ".*",   SWM_Q_FULLSCREEN | SWM_Q_FLOAT);
-       setquirk("pcb",                 "pcb",          ".*",   SWM_Q_FLOAT);
-       setquirk("SDL_App",             "SDL_App",      ".*",   SWM_Q_FLOAT | SWM_Q_FULLSCREEN);
+       setquirk("MPlayer",             "xv",           ".*",
+           SWM_Q_FLOAT | SWM_Q_FULLSCREEN | SWM_Q_FOCUSPREV, -1);
+       setquirk("OpenOffice.org 3.2",  "VCLSalFrame",  ".*",
+           SWM_Q_FLOAT, -1);
+       setquirk("Firefox-bin",         "firefox-bin",  ".*",
+           SWM_Q_TRANSSZ, -1);
+       setquirk("Firefox",             "Dialog",       ".*",
+           SWM_Q_FLOAT, -1);
+       setquirk("Gimp",                "gimp",         ".*",
+           SWM_Q_FLOAT | SWM_Q_ANYWHERE, -1);
+       setquirk("XTerm",               "xterm",        ".*",
+           SWM_Q_XTERM_FONTADJ, -1);
+       setquirk("xine",                "Xine Window",  ".*",
+           SWM_Q_FLOAT | SWM_Q_ANYWHERE, -1);
+       setquirk("Xitk",                "Xitk Combo",   ".*",
+           SWM_Q_FLOAT | SWM_Q_ANYWHERE, -1);
+       setquirk("xine",                "xine Panel",   ".*",
+           SWM_Q_FLOAT | SWM_Q_ANYWHERE, -1);
+       setquirk("Xitk",                "Xine Window",  ".*",
+           SWM_Q_FLOAT | SWM_Q_ANYWHERE, -1);
+       setquirk("xine",                "xine Video Fullscreen Window", ".*",
+           SWM_Q_FULLSCREEN | SWM_Q_FLOAT, -1);
+       setquirk("pcb",                 "pcb",          ".*",
+           SWM_Q_FLOAT, -1);
+       setquirk("SDL_App",             "SDL_App",      ".*",
+           SWM_Q_FLOAT | SWM_Q_FULLSCREEN, -1);
 }
 
 /* conf file stuff */
@@ -7850,6 +8105,7 @@ enum {
        SWM_S_FOCUS_MODE,
        SWM_S_ICONIC_ENABLED,
        SWM_S_JAVA_WORKAROUND,
+       SWM_S_MAXIMIZE_HIDE_BAR,
        SWM_S_REGION_PADDING,
        SWM_S_SPAWN_ORDER,
        SWM_S_SPAWN_TERM,
@@ -7858,11 +8114,15 @@ enum {
        SWM_S_STACK_ENABLED,
        SWM_S_TERM_WIDTH,
        SWM_S_TILE_GAP,
+       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,
 };
@@ -8021,6 +8281,9 @@ setconfvalue(const char *selector, const char *value, int flags)
        case SWM_S_JAVA_WORKAROUND:
                java_workaround = (atoi(value) != 0);
                break;
+       case SWM_S_MAXIMIZE_HIDE_BAR:
+               maximize_hide_bar = atoi(value);
+               break;
        case SWM_S_REGION_PADDING:
                region_padding = atoi(value);
                if (region_padding < 0)
@@ -8059,6 +8322,9 @@ setconfvalue(const char *selector, const char *value, int flags)
        case SWM_S_TILE_GAP:
                tile_gap = atoi(value);
                break;
+       case SWM_S_URGENT_COLLAPSE:
+               urgent_collapse = (atoi(value) != 0);
+               break;
        case SWM_S_URGENT_ENABLED:
                urgent_enabled = (atoi(value) != 0);
                break;
@@ -8071,6 +8337,12 @@ 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;
        case SWM_S_WINDOW_CLASS_ENABLED:
                window_class_enabled = (atoi(value) != 0);
                break;
@@ -8080,6 +8352,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)
@@ -8136,6 +8411,8 @@ setconfmodkey(const char *selector, const char *value, int flags)
                update_modkey(XCB_MOD_MASK_3);
        else if (strncasecmp(value, "Mod4", strlen("Mod4")) == 0)
                update_modkey(XCB_MOD_MASK_4);
+       else if (strncasecmp(value, "Mod5", strlen("Mod5")) == 0)
+               update_modkey(XCB_MOD_MASK_5);
        else
                return (1);
        return (0);
@@ -8388,6 +8665,7 @@ struct config_option configopt[] = {
        { "java_workaround",            setconfvalue,   SWM_S_JAVA_WORKAROUND },
        { "keyboard_mapping",           setkeymapping,  0 },
        { "layout",                     setlayout,      0 },
+       { "maximize_hide_bar",          setconfvalue,   SWM_S_MAXIMIZE_HIDE_BAR },
        { "modkey",                     setconfmodkey,  0 },
        { "program",                    setconfspawn,   0 },
        { "quirk",                      setconfquirk,   0 },
@@ -8402,11 +8680,15 @@ 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. */
+       { "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 },
 };
@@ -8725,7 +9007,7 @@ manage_window(xcb_window_t id, int spawn_pos, bool mapped)
        xcb_get_geometry_reply_t        *gr;
        xcb_window_t            trans = XCB_WINDOW_NONE;
        uint32_t                i, wa[2], new_flags;
-       int                     ws_idx;
+       int                     ws_idx, force_ws = -1;
        char                    *class, *instance, *name;
 
        if ((win = find_window(id)) != NULL) {
@@ -8835,9 +9117,11 @@ manage_window(xcb_window_t id, int spawn_pos, bool mapped)
                    regexec(&qp->regex_instance, instance, 0, NULL, 0) == 0 &&
                    regexec(&qp->regex_name, name, 0, NULL, 0) == 0) {
                        DNPRINTF(SWM_D_CLASS, "manage_window: matched "
-                           "quirk: %s:%s:%s mask: %#lx\n", qp->class,
-                           qp->instance, qp->name, qp->quirk);
+                           "quirk: %s:%s:%s mask: %#x, ws: %d\n", qp->class,
+                           qp->instance, qp->name, qp->quirk, qp->ws);
                        win->quirks = qp->quirk;
+                       if (qp->ws >= 0 && qp->ws < workspace_limit)
+                               force_ws = qp->ws;
                }
        }
 
@@ -8869,6 +9153,9 @@ manage_window(xcb_window_t id, int spawn_pos, bool mapped)
                win->ws = r->ws;
        }
 
+       if (force_ws != -1)
+               win->ws = &r->s->ws[force_ws];
+
        /* Set the _NET_WM_DESKTOP atom. */
        DNPRINTF(SWM_D_PROP, "manage_window: set _NET_WM_DESKTOP: %d\n",
            win->ws->idx);
@@ -8889,7 +9176,7 @@ manage_window(xcb_window_t id, int spawn_pos, bool mapped)
        }
 
 out:
-       /* Figure out where to stack the window in the workspace. */
+       /* 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);
        else if (win->ws->focus && spawn_pos == SWM_STACK_ABOVE)
@@ -8910,7 +9197,8 @@ out:
 
        ewmh_update_client_list();
 
-       TAILQ_INSERT_TAIL(&win->ws->stack, win, stack_entry);
+       TAILQ_INSERT_HEAD(&win->ws->stack, win, stack_entry);
+       lower_window(win);
 
        /* Get/apply initial _NET_WM_STATE */
        ewmh_get_wm_state(win);
@@ -9459,6 +9747,12 @@ enternotify(xcb_enter_notify_event_t *e)
                return;
        }
 
+       if (focus_mode != SWM_FOCUS_FOLLOW &&
+           e->mode == XCB_NOTIFY_MODE_UNGRAB) {
+               DNPRINTF(SWM_D_EVENT, "enternotify: ungrab; ignoring.\n");
+               return;
+       }
+
        last_event_time = e->time;
 
        if ((win = find_window(e->event)) == NULL) {
@@ -9536,6 +9830,7 @@ mapnotify(xcb_map_notify_event_t *e)
                if (ws->focus_pending == win) {
                        focus_win(win);
                        ws->focus_pending = NULL;
+                       center_pointer(win->ws->r);
                        focus_flush();
                }
        }
@@ -9798,6 +10093,7 @@ unmapnotify(xcb_unmap_notify_event_t *e)
                }
        }
 
+       center_pointer(ws->r);
        focus_flush();
 }
 
@@ -10523,10 +10819,17 @@ 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) {
+               struct swm_region       *r;
+               int j;
+
                xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT,
                    screens[i].root, XCB_CURRENT_TIME);
 
@@ -10538,13 +10841,45 @@ 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);
+               }
+
+               for (j = 0; j < SWM_WS_MAX; ++j) {
+                       struct ws_win           *win;
+
+                       free(screens[i].ws[j].name);
+
+                       while ((win = TAILQ_FIRST(&screens[i].ws[j].winlist)) != NULL) {
+                               TAILQ_REMOVE(&screens[i].ws[j].winlist, win, entry);
+                               free(win);
+                       }
+               }
+
+               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_legacy == false)
                XftFontClose(display, bar_font);
-       }
 
        xcb_key_symbols_free(syms);
        xcb_flush(conn);
@@ -10627,10 +10962,8 @@ main(int argc, char *argv[])
        int                     xfd, i, num_screens;
        struct sigaction        sact;
        xcb_generic_event_t     *evt;
-       struct timeval          tv;
-       fd_set                  rd;
-       int                     rd_max;
        int                     num_readable;
+       struct pollfd           pfd[2];
        bool                    stdin_ready = false, startup = true;
 
        /* suppress unused warning since var is needed */
@@ -10767,7 +11100,11 @@ noconfig:
                TAILQ_FOREACH(r, &screens[i].rl, entry)
                        r->ws->state = SWM_WS_STATE_MAPPED;
 
-       rd_max = xfd > STDIN_FILENO ? xfd : STDIN_FILENO;
+       memset(&pfd, 0, sizeof(pfd));
+       pfd[0].fd = xfd;
+       pfd[0].events = POLLIN;
+       pfd[1].fd = STDIN_FILENO;
+       pfd[1].events = POLLIN;
 
        while (running) {
                while ((evt = xcb_poll_for_event(conn))) {
@@ -10791,18 +11128,10 @@ noconfig:
                        }
                }
 
-               FD_ZERO(&rd);
-
-               if (bar_extra)
-                       FD_SET(STDIN_FILENO, &rd);
-
-               FD_SET(xfd, &rd);
-               tv.tv_sec = 1;
-               tv.tv_usec = 0;
-               num_readable = select(rd_max + 1, &rd, NULL, NULL, &tv);
-               if (num_readable == -1 && errno != EINTR) {
-                       DNPRINTF(SWM_D_MISC, "select failed");
-               } else if (num_readable > 0 && FD_ISSET(STDIN_FILENO, &rd)) {
+               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) {
                        stdin_ready = true;
                }