]> code.delx.au - spectrwm/blobdiff - spectrwm.c
pull latest tag + commit digest
[spectrwm] / spectrwm.c
index b60cbc734e86078e2e122fd71aa0ca2e981284ba..30ee09fc972e19900220b241057a3868aec9d8f8 100644 (file)
@@ -77,6 +77,7 @@
 #include <ctype.h>
 #include <err.h>
 #include <errno.h>
+#include <poll.h>
 #include <fcntl.h>
 #include <locale.h>
 #include <paths.h>
@@ -331,6 +332,7 @@ bool                        cycle_visible = false;
 int                    term_width = 0;
 int                    font_adjusted = 0;
 unsigned int           mod_key = MODKEY;
+bool                   warp_pointer = false;
 
 /* dmenu search */
 struct swm_region      *search_r;
@@ -401,6 +403,7 @@ bool                 stack_enabled = true;
 bool            clock_enabled = true;
 bool            iconic_enabled = 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 +481,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;
@@ -662,7 +665,8 @@ 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 */
@@ -964,6 +968,7 @@ 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 *);
@@ -1037,6 +1042,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 +1065,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 +1079,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 +1088,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 +1126,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 +2171,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 +2194,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,21 +2201,17 @@ 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);
+               }
        }
 }
 
@@ -3001,14 +3018,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 +3107,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 +3147,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 +3289,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 +3587,10 @@ unfocus_win(struct ws_win *win)
 
        update_window_color(win);
 
+       /* Raise window to "top unfocused position." */
+       if (win->ws->always_raise)
+               raise_window(win);
+
        xcb_change_property(conn, XCB_PROP_MODE_REPLACE, win->s->root,
            ewmh[_NET_ACTIVE_WINDOW].atom, XCB_ATOM_WINDOW, 32, 1, &none);
 
@@ -3823,6 +3930,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 +4027,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");
 }
@@ -3955,6 +4064,7 @@ cyclerg(struct swm_region *r, union arg *args)
                return;
 
        focus_region(rr);
+       center_pointer(rr);
        focus_flush();
        DNPRINTF(SWM_D_FOCUS, "cyclerg: done\n");
 }
@@ -4088,6 +4198,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 +4352,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;
@@ -4314,27 +4424,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 +4456,7 @@ focus(struct swm_region *r, union arg *args)
                stack();
 
        focus_win(get_focus_magic(winfocus));
+       center_pointer(r);
        focus_flush();
 
 out:
@@ -4374,6 +4484,7 @@ cycle_layout(struct swm_region *r, union arg *args)
 
        focus_win(get_region_focus(r));
 
+       center_pointer(r);
        focus_flush();
 }
 
@@ -4395,6 +4506,7 @@ stack_config(struct swm_region *r, union arg *args)
                stack();
        bar_draw();
 
+       center_pointer(r);
        focus_flush();
 }
 
@@ -4758,7 +4870,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;
@@ -4971,12 +5083,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 +5219,7 @@ send_to_ws(struct swm_region *r, union arg *args)
                }
        }
 
+       center_pointer(r);
        focus_flush();
 }
 
@@ -5218,7 +5332,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 +5343,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 +5353,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)
@@ -5921,6 +6035,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 +6067,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 +6416,7 @@ resize_step(struct swm_region *r, union arg *args)
                return;
 
        resize(win, args);
+       center_pointer(r);
        focus_flush();
 }
 
@@ -6499,6 +6616,7 @@ move_step(struct swm_region *r, union arg *args)
                return;
 
        move(win, args);
+       center_pointer(r);
        focus_flush();
 }
 
@@ -7559,10 +7677,10 @@ const char *quirkname[] = {
        "IGNORESPAWNWS",
 };
 
-/* 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 +7693,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 +7731,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 +7785,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 +7794,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);
@@ -7691,43 +7817,44 @@ quirk_free(struct quirk *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 +7880,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 +7924,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 +7934,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 */
@@ -7858,8 +7998,10 @@ 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_POINTER,
        SWM_S_WINDOW_CLASS_ENABLED,
        SWM_S_WINDOW_INSTANCE_ENABLED,
        SWM_S_WINDOW_NAME_ENABLED,
@@ -8059,6 +8201,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 +8216,9 @@ setconfvalue(const char *selector, const char *value, int flags)
                                layouts[i].l_string = plain_stacker;
                }
                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;
@@ -8402,8 +8550,10 @@ 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_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 },
@@ -8725,7 +8875,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 +8985,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 +9021,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 +9044,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 +9065,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 +9615,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 +9698,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 +9961,7 @@ unmapnotify(xcb_unmap_notify_event_t *e)
                }
        }
 
+       center_pointer(ws->r);
        focus_flush();
 }
 
@@ -10627,10 +10791,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 +10929,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 +10957,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;
                }