]> code.delx.au - spectrwm/blobdiff - spectrwm.c
Add new focus_urgent binding.
[spectrwm] / spectrwm.c
index 5c880594d58c4f50528c58aa6ef1682d12ebdea3..aef7c6088789d933c4d266f2f0726fdba4b30bd0 100644 (file)
@@ -244,6 +244,7 @@ u_int32_t           swm_debug = 0
 #define SH_INC_H(w)            (w)->sh.height_inc
 #define SWM_MAX_FONT_STEPS     (3)
 #define WINID(w)               ((w) ? (w)->id : XCB_WINDOW_NONE)
+#define WS_FOCUSED(ws)         ((ws)->r && (ws)->r->s->r_focus == (ws)->r)
 #define YESNO(x)               ((x) ? "yes" : "no")
 
 /* Constrain Window flags */
@@ -568,6 +569,7 @@ union arg {
 #define SWM_ARG_ID_FOCUSNEXT   (0)
 #define SWM_ARG_ID_FOCUSPREV   (1)
 #define SWM_ARG_ID_FOCUSMAIN   (2)
+#define SWM_ARG_ID_FOCUSURGENT (3)
 #define SWM_ARG_ID_SWAPNEXT    (10)
 #define SWM_ARG_ID_SWAPPREV    (11)
 #define SWM_ARG_ID_SWAPMAIN    (12)
@@ -743,6 +745,7 @@ enum keyfuncid {
        KF_FOCUS_MAIN,
        KF_FOCUS_NEXT,
        KF_FOCUS_PREV,
+       KF_FOCUS_URGENT,
        KF_HEIGHT_GROW,
        KF_HEIGHT_SHRINK,
        KF_ICONIFY,
@@ -3494,10 +3497,11 @@ focus_region(struct swm_region *r)
 void
 switchws(struct swm_region *r, union arg *args)
 {
-       int                     wsid = args->id, unmap_old = 0;
        struct swm_region       *this_r, *other_r;
        struct ws_win           *win;
        struct workspace        *new_ws, *old_ws;
+       xcb_window_t            none = XCB_WINDOW_NONE;
+       int                     wsid = args->id, unmap_old = 0;
 
        if (!(r && r->s))
                return;
@@ -3517,7 +3521,14 @@ switchws(struct swm_region *r, union arg *args)
        if (new_ws == old_ws)
                return;
 
-       unfocus_win(old_ws->focus);
+       if ((win = old_ws->focus) != NULL) {
+               xcb_change_window_attributes(conn, win->id, XCB_CW_BORDER_PIXEL,
+                   &win->ws->r->s->c[SWM_S_COLOR_UNFOCUS].pixel);
+
+               xcb_change_property(conn, XCB_PROP_MODE_REPLACE, win->s->root,
+                   ewmh[_NET_ACTIVE_WINDOW].atom, XCB_ATOM_WINDOW, 32, 1,
+                   &none);
+       }
 
        other_r = new_ws->r;
        if (other_r == NULL) {
@@ -3536,8 +3547,11 @@ switchws(struct swm_region *r, union arg *args)
 
        /* Set focus_pending before stacking, if needed. */
        if (focus_mode != SWM_FOCUS_FOLLOW && (!new_ws->focus_pending ||
-           validate_win(new_ws->focus_pending)))
+           validate_win(new_ws->focus_pending))) {
                new_ws->focus_pending = get_region_focus(new_ws->r);
+               new_ws->focus = new_ws->focus_prev;
+               new_ws->focus_prev = NULL;
+       }
 
        stack();
 
@@ -3647,7 +3661,7 @@ focusrg(struct swm_region *r, union arg *args)
        DNPRINTF(SWM_D_FOCUS, "focusrg: id: %d\n", ridx);
 
        rr = TAILQ_FIRST(&r->s->rl);
-       for (i = 0; i < ridx; ++i)
+       for (i = 0; i < ridx && rr != NULL; ++i)
                rr = TAILQ_NEXT(rr, entry);
 
        if (rr == NULL)
@@ -3933,86 +3947,107 @@ focus(struct swm_region *r, union arg *args)
        struct ws_win           *head, *cur_focus = NULL, *winfocus = NULL;
        struct ws_win_list      *wl = NULL;
        struct workspace        *ws = NULL;
-       int                     all_iconics;
+       union arg               a;
+       int                     i;
+       xcb_icccm_wm_hints_t    hints;
 
        if (!(r && r->ws))
-               return;
+               goto out;
 
        DNPRINTF(SWM_D_FOCUS, "focus: id: %d\n", args->id);
 
-       if ((cur_focus = r->ws->focus) == NULL)
-               return;
+       cur_focus = r->ws->focus;
        ws = r->ws;
        wl = &ws->winlist;
-       if (TAILQ_EMPTY(wl))
-               return;
-       /* make sure there is at least one uniconified window */
-       all_iconics = 1;
-       TAILQ_FOREACH(winfocus, wl, entry)
-               if (!winfocus->iconic) {
-                       all_iconics = 0;
-                       break;
-               }
-       if (all_iconics)
-               return;
+
+       /* Make sure an uniconified window has focus, if one exists. */
+       if (cur_focus == NULL) {
+               cur_focus = TAILQ_FIRST(wl);
+               while (cur_focus != NULL && cur_focus->iconic)
+                       cur_focus = TAILQ_NEXT(cur_focus, entry);
+       }
 
        switch (args->id) {
        case SWM_ARG_ID_FOCUSPREV:
-               head = TAILQ_PREV(cur_focus, ws_win_list, entry);
-               if (head == NULL)
-                       head = TAILQ_LAST(wl, ws_win_list);
-               winfocus = head;
-               if (WINID(winfocus) == cur_focus->transient) {
-                       head = TAILQ_PREV(winfocus, ws_win_list, entry);
-                       if (head == NULL)
-                               head = TAILQ_LAST(wl, ws_win_list);
-                       winfocus = head;
-               }
+               if (cur_focus == NULL)
+                       goto out;
 
-               /* skip iconics */
-               if (winfocus && winfocus->iconic) {
-                       while (winfocus != cur_focus) {
-                               if (winfocus == NULL)
-                                       winfocus = TAILQ_LAST(wl, ws_win_list);
-                               if (!winfocus->iconic)
-                                       break;
-                               winfocus = TAILQ_PREV(winfocus, ws_win_list,
-                                   entry);
-                       }
-               }
+               winfocus = cur_focus;
+               do {
+                       winfocus = TAILQ_PREV(winfocus, ws_win_list, entry);
+                       if (winfocus == NULL)
+                               winfocus = TAILQ_LAST(wl, ws_win_list);
+                       if (winfocus == cur_focus)
+                               break;
+               } while (winfocus != NULL &&
+                   (winfocus->iconic || winfocus->id == cur_focus->transient));
                break;
-
        case SWM_ARG_ID_FOCUSNEXT:
-               head = TAILQ_NEXT(cur_focus, entry);
-               if (head == NULL)
-                       head = TAILQ_FIRST(wl);
-               winfocus = head;
-
-               /* skip iconics */
-               if (winfocus && winfocus->iconic) {
-                       while (winfocus != cur_focus) {
-                               if (winfocus == NULL)
-                                       winfocus = TAILQ_FIRST(wl);
-                               if (!winfocus->iconic)
-                                       break;
-                               winfocus = TAILQ_NEXT(winfocus, entry);
-                       }
-               }
-               break;
+               if (cur_focus == NULL)
+                       goto out;
 
+               winfocus = cur_focus;
+               do {
+                       winfocus = TAILQ_NEXT(winfocus, entry);
+                       if (winfocus == NULL)
+                               winfocus = TAILQ_FIRST(wl);
+                       if (winfocus == cur_focus)
+                               break;
+               } while (winfocus != NULL &&
+                   (winfocus->iconic || winfocus->id == cur_focus->transient));
+               break;
        case SWM_ARG_ID_FOCUSMAIN:
+               if (cur_focus == NULL)
+                       goto out;
+
                winfocus = TAILQ_FIRST(wl);
                if (winfocus == cur_focus)
                        winfocus = cur_focus->ws->focus_prev;
                break;
+       case SWM_ARG_ID_FOCUSURGENT:
+               /* Search forward for the next urgent window. */
+               winfocus = NULL;
+               head = cur_focus;
+
+               for (i = 0; i <= workspace_limit; ++i) {
+                       if (head == NULL)
+                               head = TAILQ_FIRST(&r->s->ws[(ws->idx + i) %
+                                   workspace_limit].winlist);
+
+                       while (head != NULL &&
+                           (head = TAILQ_NEXT(head, entry)) != NULL) {
+                               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)) {
+                                       winfocus = head;
+                                       break;
+                               }
+                       }
+
+                       if (winfocus != NULL)
+                               break;
+               }
 
+               /* Switch ws if new focus is on a different ws. */
+               if (winfocus != NULL && winfocus->ws != ws) {
+                       a.id = winfocus->ws->idx;
+                       switchws(r, &a);
+               }
+               break;
        default:
-               return;
+               goto out;
        }
 
        focus_win(get_focus_magic(winfocus));
-
        focus_flush();
+
+out:
+       DNPRINTF(SWM_D_FOCUS, "focus: done\n");
 }
 
 void
@@ -4659,7 +4694,7 @@ send_to_rg(struct swm_region *r, union arg *args)
        DNPRINTF(SWM_D_FOCUS, "send_to_rg: id: %d\n", ridx);
 
        rr = TAILQ_FIRST(&r->s->rl);
-       for (i = 0; i < ridx; ++i)
+       for (i = 0; i < ridx && rr != NULL; ++i)
                rr = TAILQ_NEXT(rr, entry);
 
        if (rr == NULL)
@@ -5867,6 +5902,7 @@ struct keyfunc {
        { "focus_main",         focus,          {.id = SWM_ARG_ID_FOCUSMAIN} },
        { "focus_next",         focus,          {.id = SWM_ARG_ID_FOCUSNEXT} },
        { "focus_prev",         focus,          {.id = SWM_ARG_ID_FOCUSPREV} },
+       { "focus_urgent",       focus,          {.id = SWM_ARG_ID_FOCUSURGENT} },
        { "height_grow",        resize_step,    {.id = SWM_ARG_ID_HEIGHTGROW} },
        { "height_shrink",      resize_step,    {.id = SWM_ARG_ID_HEIGHTSHRINK} },
        { "iconify",            iconify,        {0} },
@@ -6566,6 +6602,7 @@ setup_keys(void)
        setkeybinding(MODKEY,           XK_Tab,         KF_FOCUS_NEXT,  NULL);
        setkeybinding(MODKEY,           XK_k,           KF_FOCUS_PREV,  NULL);
        setkeybinding(MODKEY_SHIFT,     XK_Tab,         KF_FOCUS_PREV,  NULL);
+       setkeybinding(MODKEY,           XK_u,           KF_FOCUS_URGENT,NULL);
        setkeybinding(MODKEY_SHIFT,     XK_equal,       KF_HEIGHT_GROW,NULL);
        setkeybinding(MODKEY_SHIFT,     XK_minus,       KF_HEIGHT_SHRINK,NULL);
        setkeybinding(MODKEY,           XK_w,           KF_ICONIFY,     NULL);
@@ -8406,11 +8443,11 @@ destroynotify(xcb_destroy_notify_event_t *e)
        unmanage_window(win);
        stack();
 
-       if (focus_mode != SWM_FOCUS_FOLLOW) {
+       if (focus_mode != SWM_FOCUS_FOLLOW && WS_FOCUSED(win->ws)) {
                if (win->ws->focus_pending) {
                        focus_win(win->ws->focus_pending);
                        win->ws->focus_pending = NULL;
-               } else if (win == win->ws->focus && win->ws->r) {
+               } else if (win == win->ws->focus) {
                        xcb_set_input_focus(conn, XCB_INPUT_FOCUS_PARENT,
                            win->ws->r->id, XCB_CURRENT_TIME);
                }
@@ -8559,7 +8596,7 @@ mapnotify(xcb_map_notify_event_t *e)
        win->mapped = 1;
        set_win_state(win, XCB_ICCCM_WM_STATE_NORMAL);
 
-       if (focus_mode != SWM_FOCUS_FOLLOW) {
+       if (focus_mode != SWM_FOCUS_FOLLOW && WS_FOCUSED(win->ws)) {
                if (win->ws->focus_pending == win) {
                        focus_win(win);
                        win->ws->focus_pending = NULL;
@@ -8741,7 +8778,8 @@ propertynotify(xcb_property_notify_event_t *e)
                        if (ws->r) {
                                stack();
 
-                               if (focus_mode != SWM_FOCUS_FOLLOW) {
+                               if (focus_mode != SWM_FOCUS_FOLLOW &&
+                                   WS_FOCUSED(ws)) {
                                        if (ws->focus_pending) {
                                                focus_win(ws->focus_pending);
                                                ws->focus_pending = NULL;
@@ -8768,7 +8806,7 @@ propertynotify(xcb_property_notify_event_t *e)
        } else if (e->atom == a_state) {
                /* State just changed, make sure it gets focused if mapped. */
                if (e->state == XCB_PROPERTY_NEW_VALUE) {
-                       if (focus_mode != SWM_FOCUS_FOLLOW) {
+                       if (focus_mode != SWM_FOCUS_FOLLOW && WS_FOCUSED(ws)) {
                                if (win->mapped &&
                                    ws->focus_pending == win) {
                                        focus_win(ws->focus_pending);
@@ -8824,15 +8862,16 @@ unmapnotify(xcb_unmap_notify_event_t *e)
                if (ws->r)
                        stack();
 
-               if (focus_mode == SWM_FOCUS_FOLLOW) {
-                       if (ws->r)
+               if (WS_FOCUSED(ws)) {
+                       if (focus_mode == SWM_FOCUS_FOLLOW) {
                                focus_win(get_pointer_win(ws->r->s->root));
-               } else if (ws->focus_pending) {
-                       focus_win(ws->focus_pending);
-                       ws->focus_pending = NULL;
-               } else if (ws->focus == NULL && ws->r) {
-                       xcb_set_input_focus(conn, XCB_INPUT_FOCUS_PARENT,
-                           ws->r->id, XCB_CURRENT_TIME);
+                       } else if (ws->focus_pending) {
+                               focus_win(ws->focus_pending);
+                               ws->focus_pending = NULL;
+                       } else if (ws->focus == NULL) {
+                               xcb_set_input_focus(conn, XCB_INPUT_FOCUS_PARENT,
+                                   ws->r->id, XCB_CURRENT_TIME);
+                       }
                }
        }
 
@@ -8879,7 +8918,10 @@ clientmessage(xcb_client_message_event_t *e)
 
        if (e->type == ewmh[_NET_ACTIVE_WINDOW].atom) {
                DNPRINTF(SWM_D_EVENT, "clientmessage: _NET_ACTIVE_WINDOW\n");
-               focus_win(win);
+               if (WS_FOCUSED(win->ws))
+                       focus_win(win);
+               else
+                       win->ws->focus_pending = win;
        }
        if (e->type == ewmh[_NET_CLOSE_WINDOW].atom) {
                DNPRINTF(SWM_D_EVENT, "clientmessage: _NET_CLOSE_WINDOW\n");