]> code.delx.au - spectrwm/blobdiff - spectrwm.c
Implement quirk NOFOCUSCYCLE to remove a window from the normal focus cycle.
[spectrwm] / spectrwm.c
index b60cbc734e86078e2e122fd71aa0ca2e981284ba..680f5c1b6242be19590aeaed7782e6208cf09bd5 100644 (file)
@@ -1,14 +1,14 @@
 /*
- * 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>
  *
  * 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 +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,9 @@ bool                        cycle_visible = false;
 int                    term_width = 0;
 int                    font_adjusted = 0;
 unsigned int           mod_key = MODKEY;
+bool                   warp_pointer = false;
+unsigned int           mouse_button_move = XCB_BUTTON_INDEX_1;
+unsigned int           mouse_button_resize = XCB_BUTTON_INDEX_3;
 
 /* dmenu search */
 struct swm_region      *search_r;
@@ -401,6 +405,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 +483,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 +627,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)
@@ -662,7 +668,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 */
@@ -674,6 +681,7 @@ struct quirk {
 #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. */
 };
 TAILQ_HEAD(quirk_list, quirk);
 struct quirk_list              quirks = TAILQ_HEAD_INITIALIZER(quirks);
@@ -887,6 +895,7 @@ enum keyfuncid {
        KF_STACK_INC,
        KF_STACK_DEC,
        KF_STACK_RESET,
+       KF_STACK_BALANCE,
        KF_SWAP_MAIN,
        KF_SWAP_NEXT,
        KF_SWAP_PREV,
@@ -964,6 +973,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 +1047,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 +1070,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 +1084,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 +1093,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 *);
@@ -1112,6 +1124,8 @@ 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);
@@ -1119,7 +1133,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);
@@ -1159,6 +1173,7 @@ 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 *);
@@ -2164,6 +2179,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 +2202,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 +2209,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 +3026,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 +3115,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 +3155,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 +3297,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 +3595,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 +3938,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 +4035,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 +4072,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 +4206,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 +4360,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 +4396,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 +4413,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 +4434,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 +4466,7 @@ focus(struct swm_region *r, union arg *args)
                stack();
 
        focus_win(get_focus_magic(winfocus));
+       center_pointer(r);
        focus_flush();
 
 out:
@@ -4374,6 +4494,7 @@ cycle_layout(struct swm_region *r, union arg *args)
 
        focus_win(get_region_focus(r));
 
+       center_pointer(r);
        focus_flush();
 }
 
@@ -4395,6 +4516,7 @@ stack_config(struct swm_region *r, union arg *args)
                stack();
        bar_draw();
 
+       center_pointer(r);
        focus_flush();
 }
 
@@ -4758,7 +4880,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;
@@ -4827,6 +4949,9 @@ 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--;
@@ -4877,6 +5002,9 @@ 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--;
@@ -4971,12 +5099,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 +5235,7 @@ send_to_ws(struct swm_region *r, union arg *args)
                }
        }
 
+       center_pointer(r);
        focus_flush();
 }
 
@@ -5218,7 +5348,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 +5359,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 +5369,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 +6051,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 +6083,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 +6432,7 @@ resize_step(struct swm_region *r, union arg *args)
                return;
 
        resize(win, args);
+       center_pointer(r);
        focus_flush();
 }
 
@@ -6499,6 +6632,7 @@ move_step(struct swm_region *r, union arg *args)
                return;
 
        move(win, args);
+       center_pointer(r);
        focus_flush();
 }
 
@@ -6585,6 +6719,7 @@ struct keyfunc {
        { "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} },
@@ -6680,6 +6815,31 @@ 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)
@@ -7110,6 +7270,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)
@@ -7360,6 +7522,7 @@ 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);
@@ -7557,12 +7720,13 @@ const char *quirkname[] = {
        "OBEYAPPFOCUSREQ",
        "IGNOREPID",
        "IGNORESPAWNWS",
+       "NOFOCUSCYCLE",
 };
 
-/* 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 +7739,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 +7777,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 +7831,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 +7840,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 +7863,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 +7926,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 +7970,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 +7980,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 +8044,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 +8247,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 +8262,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;
@@ -8136,11 +8330,55 @@ 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);
 }
 
+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)
 {
@@ -8389,6 +8627,8 @@ struct config_option configopt[] = {
        { "keyboard_mapping",           setkeymapping,  0 },
        { "layout",                     setlayout,      0 },
        { "modkey",                     setconfmodkey,  0 },
+       { "move_button",                setconfmousebuttonmove, 0 },
+       { "resize_button",              setconfmousebuttonresize, 0 },
        { "program",                    setconfspawn,   0 },
        { "quirk",                      setconfquirk,   0 },
        { "region",                     setconfregion,  0 },
@@ -8402,8 +8642,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 },
@@ -8552,6 +8794,12 @@ 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);
@@ -8725,7 +8973,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 +9083,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 +9119,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 +9142,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 +9163,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 +9713,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 +9796,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 +10059,7 @@ unmapnotify(xcb_unmap_notify_event_t *e)
                }
        }
 
+       center_pointer(ws->r);
        focus_flush();
 }
 
@@ -10627,10 +10889,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 +11027,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 +11055,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;
                }