JasonWoof Got questions, comments, patches, etc.? Contact Jason Woofenden
Fix mouse and keyboard binds to work regardless of caps/numlock state.
[spectrwm.git] / spectrwm.c
index f997442..665d020 100644 (file)
@@ -348,7 +348,7 @@ double                      dialog_ratio = 0.6;
 
 char           *bar_argv[] = { NULL, NULL };
 int             bar_pipe[2];
-unsigned char   bar_ext[SWM_BAR_MAX];
+char            bar_ext[SWM_BAR_MAX];
 char            bar_vertext[SWM_BAR_MAX];
 int             bar_version = 0;
 sig_atomic_t    bar_alarm = 0;
@@ -709,6 +709,110 @@ struct spawn_prog {
 TAILQ_HEAD(spawn_list, spawn_prog);
 struct spawn_list              spawns = TAILQ_HEAD_INITIALIZER(spawns);
 
+/* user/key callable function IDs */
+enum keyfuncid {
+       KF_BAR_TOGGLE,
+       KF_BUTTON2,
+       KF_CYCLE_LAYOUT,
+       KF_FLIP_LAYOUT,
+       KF_FLOAT_TOGGLE,
+       KF_FOCUS_MAIN,
+       KF_FOCUS_NEXT,
+       KF_FOCUS_PREV,
+       KF_HEIGHT_GROW,
+       KF_HEIGHT_SHRINK,
+       KF_ICONIFY,
+       KF_MASTER_SHRINK,
+       KF_MASTER_GROW,
+       KF_MASTER_ADD,
+       KF_MASTER_DEL,
+       KF_MOVE_DOWN,
+       KF_MOVE_LEFT,
+       KF_MOVE_RIGHT,
+       KF_MOVE_UP,
+       KF_MVWS_1,
+       KF_MVWS_2,
+       KF_MVWS_3,
+       KF_MVWS_4,
+       KF_MVWS_5,
+       KF_MVWS_6,
+       KF_MVWS_7,
+       KF_MVWS_8,
+       KF_MVWS_9,
+       KF_MVWS_10,
+       KF_MVWS_11,
+       KF_MVWS_12,
+       KF_MVWS_13,
+       KF_MVWS_14,
+       KF_MVWS_15,
+       KF_MVWS_16,
+       KF_MVWS_17,
+       KF_MVWS_18,
+       KF_MVWS_19,
+       KF_MVWS_20,
+       KF_MVWS_21,
+       KF_MVWS_22,
+       KF_NAME_WORKSPACE,
+       KF_QUIT,
+       KF_RAISE_TOGGLE,
+       KF_RESTART,
+       KF_SCREEN_NEXT,
+       KF_SCREEN_PREV,
+       KF_SEARCH_WIN,
+       KF_SEARCH_WORKSPACE,
+       KF_SPAWN_CUSTOM,
+       KF_STACK_INC,
+       KF_STACK_DEC,
+       KF_STACK_RESET,
+       KF_SWAP_MAIN,
+       KF_SWAP_NEXT,
+       KF_SWAP_PREV,
+       KF_UNICONIFY,
+       KF_VERSION,
+       KF_WIDTH_GROW,
+       KF_WIDTH_SHRINK,
+       KF_WIND_DEL,
+       KF_WIND_KILL,
+       KF_WS_1,
+       KF_WS_2,
+       KF_WS_3,
+       KF_WS_4,
+       KF_WS_5,
+       KF_WS_6,
+       KF_WS_7,
+       KF_WS_8,
+       KF_WS_9,
+       KF_WS_10,
+       KF_WS_11,
+       KF_WS_12,
+       KF_WS_13,
+       KF_WS_14,
+       KF_WS_15,
+       KF_WS_16,
+       KF_WS_17,
+       KF_WS_18,
+       KF_WS_19,
+       KF_WS_20,
+       KF_WS_21,
+       KF_WS_22,
+       KF_WS_NEXT,
+       KF_WS_NEXT_ALL,
+       KF_WS_PREV,
+       KF_WS_PREV_ALL,
+       KF_WS_PRIOR,
+       KF_DUMPWINS, /* MUST BE LAST */
+       KF_INVALID
+};
+
+struct key {
+        RB_ENTRY(key)           entry;
+        unsigned int            mod;
+        KeySym                  keysym;
+        enum keyfuncid          funcid;
+        char                    *spawn_name;
+};
+RB_HEAD(key_tree, key);
+
 /* function prototypes */
 void    adjust_font(struct ws_win *);
 void    bar_class_name(char *, size_t, struct swm_region *);
@@ -767,7 +871,7 @@ char        *expand_tilde(const char *);
 void    expose(xcb_expose_event_t *);
 void    fake_keypress(struct ws_win *, xcb_keysym_t, uint16_t);
 struct pid_e   *find_pid(pid_t);
-struct ws_win  *find_unmanaged_window(xcb_window_t); 
+struct ws_win  *find_unmanaged_window(xcb_window_t);
 struct ws_win  *find_window(xcb_window_t);
 void    floating_toggle(struct swm_region *, union arg *);
 int     floating_toggle_win(struct ws_win *);
@@ -806,6 +910,12 @@ void        grab_windows(void);
 void    iconify(struct swm_region *, union arg *);
 int     isxlfd(char *);
 void    keypress(xcb_key_press_event_t *);
+int     key_cmp(struct key *, struct key *);
+void    key_insert(unsigned int, KeySym, enum keyfuncid, const char *);
+struct key     *key_lookup(unsigned int, KeySym);
+void    key_remove(struct key *);
+void    key_replace(struct key *, unsigned int, KeySym, enum keyfuncid,
+            const char *);
 void    kill_refs(struct ws_win *);
 #ifdef SWM_DEBUG
 void    leavenotify(xcb_leave_notify_event_t *);
@@ -859,6 +969,7 @@ int  setconfquirk(char *, char *, int);
 int     setconfregion(char *, char *, int);
 int     setconfspawn(char *, char *, int);
 int     setconfvalue(char *, char *, int);
+void    setkeybinding(unsigned int, KeySym, enum keyfuncid, const char *);
 int     setkeymapping(char *, char *, int);
 int     setlayout(char *, char *, int);
 void    setquirk(const char *, const char *, unsigned long);
@@ -910,6 +1021,10 @@ void       wkill(struct swm_region *, union arg *);
 void    workaround(void);
 void    xft_init(struct swm_region *);
 
+RB_PROTOTYPE(key_tree, key, entry, key_cmp);
+RB_GENERATE(key_tree, key, entry, key_cmp);
+struct key_tree                 keys;
+
 void
 cursors_load(void)
 {
@@ -953,7 +1068,8 @@ char *
 expand_tilde(const char *s)
 {
        struct passwd           *ppwd;
-       int                     i, max;
+       int                     i;
+       long                    max;
        char                    *user;
        const char              *sc = s;
        char                    *result;
@@ -1736,7 +1852,7 @@ bar_extra_stop(void)
                kill(bar_pid, SIGTERM);
                bar_pid = 0;
        }
-       strlcpy((char *)bar_ext, "", sizeof bar_ext);
+       strlcpy(bar_ext, "", sizeof bar_ext);
        bar_extra = 0;
 }
 
@@ -2082,14 +2198,14 @@ bar_update(void)
                while ((b = fgetln(stdin, &len)) != NULL)
                        if (b && b[len - 1] == '\n') {
                                b[len - 1] = '\0';
-                               strlcpy((char *)bar_ext, b, sizeof bar_ext);
+                               strlcpy(bar_ext, b, sizeof bar_ext);
                        }
                if (b == NULL && errno != EAGAIN) {
                        warn("bar_update: bar_extra failed");
                        bar_extra_stop();
                }
        } else
-               strlcpy((char *)bar_ext, "", sizeof bar_ext);
+               strlcpy(bar_ext, "", sizeof bar_ext);
 
        bar_fmt_print();
        alarm(bar_delay);
@@ -2289,7 +2405,7 @@ xft_init(struct swm_region *r)
 
        if (!XftColorAllocValue(display, DefaultVisual(display, r->s->idx),
            DefaultColormap(display, r->s->idx), &color, &bar_font_color))
-               warn("unable to allocate Xft color");
+               warn("Xft error: unable to allocate color.");
 
        bar_height = bar_font->height + 2 * bar_border_width;
 
@@ -2686,6 +2802,7 @@ get_pointer_win(xcb_window_t root)
                } else {
                        DNPRINTF(SWM_D_EVENT, "get_pointer_win: none.\n");
                }
+               free(r);
        }
 
        return win;
@@ -4372,24 +4489,28 @@ get_win_name(xcb_window_t win)
            XCB_GET_PROPERTY_TYPE_ANY, 0, UINT_MAX);
        r = xcb_get_property_reply(conn, c, NULL);
 
-       if (!r || r->type == XCB_NONE) {
-               free(r);
-               /* Use WM_NAME instead; no UTF-8. */
-               c = xcb_get_property(conn, 0, win, XCB_ATOM_WM_NAME,
-                   XCB_GET_PROPERTY_TYPE_ANY, 0, UINT_MAX);
-               r = xcb_get_property_reply(conn, c, NULL);
-
-               if(!r || r->type == XCB_NONE) {
+       if (r) {
+               if (r->type == XCB_NONE) {
                        free(r);
-                       return NULL;
+                       /* Use WM_NAME instead; no UTF-8. */
+                       c = xcb_get_property(conn, 0, win, XCB_ATOM_WM_NAME,
+                               XCB_GET_PROPERTY_TYPE_ANY, 0, UINT_MAX);
+                       r = xcb_get_property_reply(conn, c, NULL);
+
+                       if (!r)
+                               return (NULL);
+                       if (r->type == XCB_NONE) {
+                               free(r);
+                               return (NULL);
+                       }
                }
-       }
+               if (r->length > 0)
+                       name = strndup(xcb_get_property_value(r),
+                                  xcb_get_property_value_length(r));
 
-       if (r->length > 0)
-               name = strndup(xcb_get_property_value(r),
-                   xcb_get_property_value_length(r));
+               free(r);
+       }
 
-       free(r);
        return (name);
 }
 
@@ -4936,7 +5057,7 @@ resize(struct ws_win *win, union arg *args)
 {
        xcb_timestamp_t         timestamp = 0;
        struct swm_region       *r = NULL;
-       int                     resize_step = 0;
+       int                     resize_stp = 0;
        struct swm_geometry     g;
        int                     top = 0, left = 0, resizing;
        int                     dx, dy;
@@ -4974,24 +5095,24 @@ resize(struct ws_win *win, union arg *args)
        switch (args->id) {
        case SWM_ARG_ID_WIDTHSHRINK:
                WIDTH(win) -= SWM_RESIZE_STEPS;
-               resize_step = 1;
+               resize_stp = 1;
                break;
        case SWM_ARG_ID_WIDTHGROW:
                WIDTH(win) += SWM_RESIZE_STEPS;
-               resize_step = 1;
+               resize_stp = 1;
                break;
        case SWM_ARG_ID_HEIGHTSHRINK:
                HEIGHT(win) -= SWM_RESIZE_STEPS;
-               resize_step = 1;
+               resize_stp = 1;
                break;
        case SWM_ARG_ID_HEIGHTGROW:
                HEIGHT(win) += SWM_RESIZE_STEPS;
-               resize_step = 1;
+               resize_stp = 1;
                break;
        default:
                break;
        }
-       if (resize_step) {
+       if (resize_stp) {
                constrain_window(win, r, 1);
                update_window(win);
                store_float_geom(win,r);
@@ -5125,7 +5246,7 @@ void
 move(struct ws_win *win, union arg *args)
 {
        xcb_timestamp_t         timestamp = 0;
-       int                     move_step = 0, moving;
+       int                     move_stp = 0, moving;
        struct swm_region       *r = NULL;
        xcb_query_pointer_reply_t       *qpr;
        xcb_generic_event_t             *evt;
@@ -5158,28 +5279,28 @@ move(struct ws_win *win, union arg *args)
 
        focus_flush();
 
-       move_step = 0;
+       move_stp = 0;
        switch (args->id) {
        case SWM_ARG_ID_MOVELEFT:
                X(win) -= (SWM_MOVE_STEPS - border_width);
-               move_step = 1;
+               move_stp = 1;
                break;
        case SWM_ARG_ID_MOVERIGHT:
                X(win) += (SWM_MOVE_STEPS - border_width);
-               move_step = 1;
+               move_stp = 1;
                break;
        case SWM_ARG_ID_MOVEUP:
                Y(win) -= (SWM_MOVE_STEPS - border_width);
-               move_step = 1;
+               move_stp = 1;
                break;
        case SWM_ARG_ID_MOVEDOWN:
                Y(win) += (SWM_MOVE_STEPS - border_width);
-               move_step = 1;
+               move_stp = 1;
                break;
        default:
                break;
        }
-       if (move_step) {
+       if (move_stp) {
                constrain_window(win, r, 0);
                update_window(win);
                store_float_geom(win, r);
@@ -5253,101 +5374,6 @@ move_step(struct swm_region *r, union arg *args)
        focus_flush();
 }
 
-/* user/key callable function IDs */
-enum keyfuncid {
-       KF_BAR_TOGGLE,
-       KF_BUTTON2,
-       KF_CYCLE_LAYOUT,
-       KF_FLIP_LAYOUT,
-       KF_FLOAT_TOGGLE,
-       KF_FOCUS_MAIN,
-       KF_FOCUS_NEXT,
-       KF_FOCUS_PREV,
-       KF_HEIGHT_GROW,
-       KF_HEIGHT_SHRINK,
-       KF_ICONIFY,
-       KF_MASTER_SHRINK,
-       KF_MASTER_GROW,
-       KF_MASTER_ADD,
-       KF_MASTER_DEL,
-       KF_MOVE_DOWN,
-       KF_MOVE_LEFT,
-       KF_MOVE_RIGHT,
-       KF_MOVE_UP,
-       KF_MVWS_1,
-       KF_MVWS_2,
-       KF_MVWS_3,
-       KF_MVWS_4,
-       KF_MVWS_5,
-       KF_MVWS_6,
-       KF_MVWS_7,
-       KF_MVWS_8,
-       KF_MVWS_9,
-       KF_MVWS_10,
-       KF_MVWS_11,
-       KF_MVWS_12,
-       KF_MVWS_13,
-       KF_MVWS_14,
-       KF_MVWS_15,
-       KF_MVWS_16,
-       KF_MVWS_17,
-       KF_MVWS_18,
-       KF_MVWS_19,
-       KF_MVWS_20,
-       KF_MVWS_21,
-       KF_MVWS_22,
-       KF_NAME_WORKSPACE,
-       KF_QUIT,
-       KF_RAISE_TOGGLE,
-       KF_RESTART,
-       KF_SCREEN_NEXT,
-       KF_SCREEN_PREV,
-       KF_SEARCH_WIN,
-       KF_SEARCH_WORKSPACE,
-       KF_SPAWN_CUSTOM,
-       KF_STACK_INC,
-       KF_STACK_DEC,
-       KF_STACK_RESET,
-       KF_SWAP_MAIN,
-       KF_SWAP_NEXT,
-       KF_SWAP_PREV,
-       KF_UNICONIFY,
-       KF_VERSION,
-       KF_WIDTH_GROW,
-       KF_WIDTH_SHRINK,
-       KF_WIND_DEL,
-       KF_WIND_KILL,
-       KF_WS_1,
-       KF_WS_2,
-       KF_WS_3,
-       KF_WS_4,
-       KF_WS_5,
-       KF_WS_6,
-       KF_WS_7,
-       KF_WS_8,
-       KF_WS_9,
-       KF_WS_10,
-       KF_WS_11,
-       KF_WS_12,
-       KF_WS_13,
-       KF_WS_14,
-       KF_WS_15,
-       KF_WS_16,
-       KF_WS_17,
-       KF_WS_18,
-       KF_WS_19,
-       KF_WS_20,
-       KF_WS_21,
-       KF_WS_22,
-       KF_WS_NEXT,
-       KF_WS_NEXT_ALL,
-       KF_WS_PREV,
-       KF_WS_PREV_ALL,
-       KF_WS_PRIOR,
-       KF_DUMPWINS, /* MUST BE LAST */
-       KF_INVALID
-};
-
 /* key definitions */
 struct keyfunc {
        char                    name[SWM_FUNCNAME_LEN];
@@ -5447,14 +5473,6 @@ struct keyfunc {
        { "dumpwins",           dumpwins,       {0} }, /* MUST BE LAST */
        { "invalid key func",   NULL,           {0} },
 };
-struct key {
-       RB_ENTRY(key)           entry;
-       unsigned int            mod;
-       KeySym                  keysym;
-       enum keyfuncid          funcid;
-       char                    *spawn_name;
-};
-RB_HEAD(key_tree, key);
 
 int
 key_cmp(struct key *kp1, struct key *kp2)
@@ -5472,9 +5490,6 @@ key_cmp(struct key *kp1, struct key *kp2)
        return (0);
 }
 
-RB_GENERATE(key_tree, key, entry, key_cmp);
-struct key_tree                        keys;
-
 /* mouse */
 enum { client_click, root_click };
 struct button {
@@ -5930,7 +5945,7 @@ setkeybinding(unsigned int mod, KeySym ks, enum keyfuncid kfid,
                return;
        }
        if (kfid == KF_INVALID) {
-               warnx("error: setkeybinding: cannot find mod/key combination");
+               warnx("bind: Key combination already unbound.");
                DNPRINTF(SWM_D_KEY, "setkeybinding: leave\n");
                return;
        }
@@ -6167,7 +6182,7 @@ grabkeys(void)
 {
        struct key              *kp;
        int                     num_screens, k, j;
-       unsigned int            modifiers[3];
+       unsigned int            modifiers[4];
        xcb_keycode_t           *code;
 
        DNPRINTF(SWM_D_MISC, "grabkeys\n");
@@ -6175,7 +6190,8 @@ grabkeys(void)
 
        modifiers[0] = 0;
        modifiers[1] = numlockmask;
-       modifiers[2] = numlockmask | XCB_MOD_MASK_LOCK;
+       modifiers[2] = XCB_MOD_MASK_LOCK;
+       modifiers[3] = numlockmask | XCB_MOD_MASK_LOCK;
 
        num_screens = xcb_setup_roots_length(xcb_get_setup(conn));
        for (k = 0; k < num_screens; k++) {
@@ -6201,16 +6217,25 @@ grabkeys(void)
 void
 grabbuttons(struct ws_win *win)
 {
-       int             i;
+       unsigned int    modifiers[4];
+       int             i, j;
 
        DNPRINTF(SWM_D_MOUSE, "grabbuttons: win 0x%x\n", win->id);
+       updatenumlockmask();
+
+       modifiers[0] = 0;
+       modifiers[1] = numlockmask;
+       modifiers[2] = XCB_MOD_MASK_LOCK;
+       modifiers[3] = numlockmask | XCB_MOD_MASK_LOCK;
 
        for (i = 0; i < LENGTH(buttons); i++)
                if (buttons[i].action == client_click)
-                       xcb_grab_button(conn, 0, win->id, BUTTONMASK,
-                           XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC,
-                           XCB_WINDOW_NONE, XCB_CURSOR_NONE,
-                           buttons[i].button, buttons[i].mask);
+                       for (j = 0; j < LENGTH(modifiers); ++j)
+                               xcb_grab_button(conn, 0, win->id, BUTTONMASK,
+                                   XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC,
+                                   XCB_WINDOW_NONE, XCB_CURSOR_NONE,
+                                   buttons[i].button, buttons[i].mask |
+                                   modifiers[j]);
 }
 
 const char *quirkname[] = {
@@ -7432,7 +7457,13 @@ buttonpress(xcb_button_press_event_t *e)
                        /* Focus on empty region */
                        /* If no windows on region if its empty. */
                        r = root_to_region(e->root, SWM_CK_POINTER);
-                       if (r && TAILQ_EMPTY(&r->ws->winlist)) {
+                       if (r == NULL) {
+                               DNPRINTF(SWM_D_EVENT, "buttonpress: "
+                                   "NULL region; ignoring.\n");
+                               goto out;
+                       }
+
+                       if (TAILQ_EMPTY(&r->ws->winlist)) {
                                old_r = root_to_region(e->root, SWM_CK_FOCUS);
                                if (old_r && old_r != r)
                                        unfocus_win(old_r->ws->focus);
@@ -7641,9 +7672,10 @@ configurerequest(xcb_configure_request_event_t *e)
                        WIDTH(win) = win->g_float.w;
                        HEIGHT(win) = win->g_float.h;
 
-                       stack_floater(win, win->ws->r);
-
-                       focus_flush();
+                       if (r) {
+                               stack_floater(win, r);
+                               focus_flush();
+                       }
                } else {
                        config_win(win, e);
                        xcb_flush(conn);
@@ -7806,6 +7838,12 @@ enternotify(xcb_enter_notify_event_t *e)
                if (e->event == e->root) {
                        /* If no windows on pointer region, then focus root. */
                        r = root_to_region(e->root, SWM_CK_POINTER);
+                       if (r == NULL) {
+                               DNPRINTF(SWM_D_EVENT, "enterntoify: "
+                                   "NULL region; ignoring.\n");
+                               return;
+                       }
+
                        if (TAILQ_EMPTY(&r->ws->winlist)) {
                                old_r = root_to_region(e->root, SWM_CK_FOCUS);
                                if (old_r && old_r != r)
@@ -7846,9 +7884,6 @@ leavenotify(xcb_leave_notify_event_t *e)
 }
 #endif
 
-/* lets us use one switch statement for arbitrary mode/detail combinations */
-#define MERGE_MEMBERS(a,b)     (((a & 0xffff) << 16) | (b & 0xffff))
-
 void
 mapnotify(xcb_map_notify_event_t *e)
 {