]> code.delx.au - spectrwm/blobdiff - spectrwm.c
Mouse button mapping config option, by levaidaniel
[spectrwm] / spectrwm.c
index 0ca00bb425d08a93df4cf17dac71d2079dc53856..2c2d858b4b2db20f4ac0216656b7dc13d65faa04 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>
@@ -332,6 +333,8 @@ 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;
@@ -1041,6 +1044,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 *);
@@ -1063,6 +1067,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 *);
@@ -1116,6 +1121,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);
@@ -1163,6 +1170,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 *);
@@ -2168,6 +2176,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)
 {
@@ -2175,8 +2199,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;
@@ -2184,14 +2206,9 @@ 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]) {
@@ -3006,14 +3023,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;
 
@@ -3035,23 +3112,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)),
@@ -3073,10 +3152,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]);
@@ -3220,6 +3303,9 @@ center_pointer(struct swm_region *r)
                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);
@@ -3506,6 +3592,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);
 
@@ -4113,6 +4203,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");
@@ -4266,7 +4357,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;
@@ -4339,27 +4429,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);
                }
@@ -4400,6 +4489,7 @@ cycle_layout(struct swm_region *r, union arg *args)
 
        focus_win(get_region_focus(r));
 
+       center_pointer(r);
        focus_flush();
 }
 
@@ -4421,6 +4511,7 @@ stack_config(struct swm_region *r, union arg *args)
                stack();
        bar_draw();
 
+       center_pointer(r);
        focus_flush();
 }
 
@@ -4784,7 +4875,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;
@@ -4997,12 +5088,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);
@@ -5245,7 +5337,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)
@@ -5256,9 +5348,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();
 }
@@ -5267,7 +5358,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)
@@ -5948,6 +6040,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");
 }
@@ -6328,6 +6421,7 @@ resize_step(struct swm_region *r, union arg *args)
                return;
 
        resize(win, args);
+       center_pointer(r);
        focus_flush();
 }
 
@@ -6527,6 +6621,7 @@ move_step(struct swm_region *r, union arg *args)
                return;
 
        move(win, args);
+       center_pointer(r);
        focus_flush();
 }
 
@@ -6708,6 +6803,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)
@@ -7138,6 +7258,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)
@@ -8194,11 +8316,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)
 {
@@ -8447,6 +8613,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 },
@@ -8612,6 +8780,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);
@@ -8954,7 +9128,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)
@@ -8975,7 +9149,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);
@@ -9524,6 +9699,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) {
@@ -10694,10 +10875,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 */
@@ -10834,7 +11013,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))) {
@@ -10858,18 +11041,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;
                }