JasonWoof Got questions, comments, patches, etc.? Contact Jason Woofenden
Fix new windows not always being handled in a prompt fashion.
[spectrwm.git] / spectrwm.c
index fb07f0e..e45a28d 100644 (file)
@@ -249,6 +249,12 @@ u_int32_t          swm_debug = 0
 #define SWM_FOCUS_FOLLOW       (1)
 #define SWM_FOCUS_MANUAL       (2)
 
+#define SWM_CK_NONE            0
+#define SWM_CK_ALL             0x7
+#define SWM_CK_FOCUS           0x1
+#define SWM_CK_POINTER         0x2
+#define SWM_CK_FALLBACK                0x4
+
 #define SWM_CONF_DEFAULT       (0)
 #define SWM_CONF_KEYMAPPING    (1)
 
@@ -1643,7 +1649,7 @@ bar_fmt(const char *fmtexp, char *fmtnew, struct swm_region *r, size_t sz)
 
        if (title_class_enabled) {
                strlcat(fmtnew, "+C", sz);
-               if (title_name_enabled == 0)
+               if (!title_name_enabled)
                        strlcat(fmtnew, "+4<", sz);
        }
 
@@ -1849,7 +1855,7 @@ bar_update(void)
        size_t                  len;
        char                    *b;
 
-       if (bar_enabled == 0)
+       if (!bar_enabled)
                return;
        if (bar_extra && bar_extra_running) {
                /* ignore short reads; it'll correct itself */
@@ -1921,7 +1927,7 @@ bar_refresh(void)
        int                     i, num_screens;
 
        /* do this here because the conf file is in memory */
-       if (bar_extra && bar_extra_running == 0 && bar_argv[0]) {
+       if (bar_extra && !bar_extra_running && bar_argv[0]) {
                /* launch external status app */
                bar_extra_running = 1;
                if (pipe(bar_pipe) == -1)
@@ -2313,9 +2319,9 @@ count_win(struct workspace *ws, int count_transient)
        int                     count = 0;
 
        TAILQ_FOREACH(win, &ws->winlist, entry) {
-               if (count_transient == 0 && win->floating)
+               if (!count_transient && win->floating)
                        continue;
-               if (count_transient == 0 && win->transient)
+               if (!count_transient && win->transient)
                        continue;
                if (win->iconic)
                        continue;
@@ -2444,11 +2450,13 @@ restart(struct swm_region *r, union arg *args)
 }
 
 struct swm_region *
-root_to_region(xcb_window_t root)
+root_to_region(xcb_window_t root, int check)
 {
+       struct ws_win                   *cfw;
        struct swm_region               *r = NULL;
        int                             i, num_screens;
        xcb_query_pointer_reply_t       *qpr;
+       xcb_get_input_focus_reply_t     *gifr;
 
        DNPRINTF(SWM_D_MISC, "root_to_region: window: 0x%x\n", root);
 
@@ -2457,21 +2465,39 @@ root_to_region(xcb_window_t root)
                if (screens[i].root == root)
                        break;
 
-       qpr = xcb_query_pointer_reply(conn, xcb_query_pointer(conn,
-           screens[i].root), NULL);
+       if (check & SWM_CK_FOCUS) {
+               /* Try to find an actively focused window */
+               gifr = xcb_get_input_focus_reply(conn,
+                   xcb_get_input_focus(conn), NULL);
+               if (gifr) {
+                       cfw = find_window(gifr->focus);
+                       if (cfw && cfw->ws->r)
+                               r = cfw->ws->r;
 
-       if (qpr) {
-               DNPRINTF(SWM_D_MISC, "root_to_region: pointer: (%d,%d)\n",
-                   qpr->root_x, qpr->root_y);
-               /* choose a region based on pointer location */
-               TAILQ_FOREACH(r, &screens[i].rl, entry)
-                       if (X(r) <= qpr->root_x && qpr->root_x < MAX_X(r) &&
-                           Y(r) <= qpr->root_y && qpr->root_y < MAX_Y(r))
-                               break;
-               free(qpr);
+                       free(gifr);
+               }
        }
 
-       if (r == NULL)
+       if (r == NULL && check & SWM_CK_POINTER) {
+               /* No region with an active focus; try to use pointer. */
+               qpr = xcb_query_pointer_reply(conn, xcb_query_pointer(conn,
+                   screens[i].root), NULL);
+
+               if (qpr) {
+                       DNPRINTF(SWM_D_MISC, "root_to_region: pointer: "
+                           "(%d,%d)\n", qpr->root_x, qpr->root_y);
+                       TAILQ_FOREACH(r, &screens[i].rl, entry)
+                               if (X(r) <= qpr->root_x &&
+                                   qpr->root_x < MAX_X(r) &&
+                                   Y(r) <= qpr->root_y &&
+                                   qpr->root_y < MAX_Y(r))
+                                       break;
+                       free(qpr);
+               }
+       }
+
+       /* Last resort. */
+       if (r == NULL && check & SWM_CK_FALLBACK)
                r = TAILQ_FIRST(&screens[i].rl);
 
        return (r);
@@ -2779,6 +2805,7 @@ struct ws_win *
 get_focus_magic(struct ws_win *win)
 {
        struct ws_win   *parent = NULL;
+       struct ws_win   *child = NULL;
 
        DNPRINTF(SWM_D_FOCUS, "get_focus_magic: window: 0x%x\n", WINID(win));
        if (win == NULL)
@@ -2787,19 +2814,19 @@ get_focus_magic(struct ws_win *win)
        if (win->transient) {
                parent = find_window(win->transient);
 
-               /* If parent prefers focus elsewhere, then do so. */
-               if (parent && parent->focus_child) {
-                       if (validate_win(parent->focus_child) == 0)
-                               win = parent->focus_child;
+               /* If parent prefers focus elsewhere, then try to do so. */
+               if (parent && (child = parent->focus_child)) {
+                       if (validate_win(child) == 0 && child->mapped)
+                               win = child;
                        else
                                parent->focus_child = NULL;
                }
        }
 
-       /* If this window prefers focus elsewhere, then do so. */
-       if (win->focus_child) {
-               if (validate_win(win->focus_child) == 0)
-                       win = win->focus_child;
+       /* If this window prefers focus elsewhere, then try to do so. */
+       if ((child = win->focus_child)) {
+               if (validate_win(child) == 0 && child->mapped)
+                       win = child;
                else
                        win->focus_child = NULL;
        }
@@ -2872,18 +2899,26 @@ switchws(struct swm_region *r, union arg *args)
                TAILQ_FOREACH(win, &old_ws->winlist, entry)
                        unmap_window(win);
 
-       if (focus_mode != SWM_FOCUS_FOLLOW)
+       if (focus_mode != SWM_FOCUS_FOLLOW) {
                new_ws->focus_pending = get_region_focus(new_ws->r);
 
-       if (new_ws->focus_pending && focus_mode != SWM_FOCUS_FOLLOW) {
                /* if workspaces were swapped, then don't wait to set focus */
-               if (old_ws->r)
-                       focus_win(new_ws->focus_pending);
-       } else {
-               /* make sure bar gets updated if ws is empty */
-               bar_update();
+               if (old_ws->r) {
+                       if (new_ws->focus_pending) {
+                               focus_win(new_ws->focus_pending);
+                       } else {
+                               /* Empty region, focus on root. */
+                               xcb_set_input_focus(conn, XCB_INPUT_FOCUS_PARENT,
+                                   new_ws->r->s[new_ws->r->s->idx].root,
+                                   XCB_CURRENT_TIME);
+                       }
+               }
        }
 
+       /* Clear bar if new ws is empty. */
+       if (new_ws->focus_pending == NULL)
+               bar_update();
+
        focus_flush();
 
        DNPRINTF(SWM_D_WS, "switchws: done.\n");
@@ -2925,9 +2960,9 @@ cyclews(struct swm_region *r, union arg *args)
                };
 
                if (!cycle_all &&
-                   (cycle_empty == 0 && TAILQ_EMPTY(&s->ws[a.id].winlist)))
+                   (!cycle_empty && TAILQ_EMPTY(&s->ws[a.id].winlist)))
                        continue;
-               if (cycle_visible == 0 && s->ws[a.id].r != NULL)
+               if (!cycle_visible && s->ws[a.id].r != NULL)
                        continue;
 
                switchws(r, &a);
@@ -2954,8 +2989,9 @@ priorws(struct swm_region *r, union arg *args)
 void
 cyclescr(struct swm_region *r, union arg *args)
 {
+       struct ws_win           *nfw;
        struct swm_region       *rr = NULL;
-       int                     i, x, y, num_screens;
+       int                     i, num_screens;
 
        num_screens = xcb_setup_roots_length(xcb_get_setup(conn));
        /* do nothing if we don't have more than one screen */
@@ -2980,20 +3016,17 @@ cyclescr(struct swm_region *r, union arg *args)
        if (rr == NULL)
                return;
 
-       /* move mouse to region */
-       x = X(rr) + 1;
-       y = Y(rr) + 1 + (bar_enabled ? bar_height : 0);
-       xcb_warp_pointer(conn, XCB_WINDOW_NONE, rr->s[i].root, 0, 0, 0, 0,
-           x, y);
-
-       rr->ws->focus = get_region_focus(rr);
+       nfw = get_region_focus(rr);
+       if (nfw) {
+               focus_win(nfw);
+       } else {
+               /* New region is empty; unfocus old region and warp pointer. */
+               unfocus_win(r->ws->focus);
+               xcb_set_input_focus(conn, XCB_INPUT_FOCUS_PARENT,
+                               rr->s[i].root, XCB_CURRENT_TIME);
 
-       if (rr->ws->focus) {
-               /* move to focus window */
-               x = X(rr->ws->focus) + 1;
-               y = Y(rr->ws->focus) + 1;
-               xcb_warp_pointer(conn, XCB_WINDOW_NONE, rr->s[i].root, 0, 0, 0,
-                   0, x, y);
+               /* Clear bar since empty. */
+               bar_update();
        }
 
        focus_flush();
@@ -3225,7 +3258,7 @@ focus(struct swm_region *r, union arg *args)
        /* make sure there is at least one uniconified window */
        all_iconics = 1;
        TAILQ_FOREACH(winfocus, wl, entry)
-               if (winfocus->iconic == 0) {
+               if (!winfocus->iconic) {
                        all_iconics = 0;
                        break;
                }
@@ -3250,7 +3283,7 @@ focus(struct swm_region *r, union arg *args)
                        while (winfocus != cur_focus) {
                                if (winfocus == NULL)
                                        winfocus = TAILQ_LAST(wl, ws_win_list);
-                               if (winfocus->iconic == 0)
+                               if (!winfocus->iconic)
                                        break;
                                winfocus = TAILQ_PREV(winfocus, ws_win_list,
                                    entry);
@@ -3269,7 +3302,7 @@ focus(struct swm_region *r, union arg *args)
                        while (winfocus != cur_focus) {
                                if (winfocus == NULL)
                                        winfocus = TAILQ_FIRST(wl);
-                               if (winfocus->iconic == 0)
+                               if (!winfocus->iconic)
                                        break;
                                winfocus = TAILQ_NEXT(winfocus, entry);
                        }
@@ -3533,8 +3566,7 @@ stack_master(struct workspace *ws, struct swm_geometry *g, int rot, int flip)
                return;
 
        TAILQ_FOREACH(win, &ws->winlist, entry)
-               if (win->transient == 0 && win->floating == 0
-                   && win->iconic == 0)
+               if (!win->transient && !win->floating && !win->iconic)
                        break;
 
        if (win == NULL)
@@ -3591,9 +3623,9 @@ stack_master(struct workspace *ws, struct swm_geometry *g, int rot, int flip)
        /*  stack all the tiled windows */
        i = j = 0, s = stacks;
        TAILQ_FOREACH(win, &ws->winlist, entry) {
-               if (win->transient != 0 || win->floating != 0)
+               if (win->transient || win->floating)
                        continue;
-               if (win->iconic != 0)
+               if (win->iconic)
                        continue;
 
                if (win->ewmh_flags & EWMH_F_FULLSCREEN) {
@@ -3649,7 +3681,7 @@ stack_master(struct workspace *ws, struct swm_geometry *g, int rot, int flip)
                else
                        win_g.y += last_h + 2 * border_width;
 
-               if (disable_border && bar_enabled == 0 && winno == 1){
+               if (disable_border && !bar_enabled && winno == 1){
                        bordered = 0;
                        win_g.w += 2 * border_width;
                        win_g.h += 2 * border_width;
@@ -3696,9 +3728,9 @@ stack_master(struct workspace *ws, struct swm_geometry *g, int rot, int flip)
 notiles:
        /* now, stack all the floaters and transients */
        TAILQ_FOREACH(win, &ws->winlist, entry) {
-               if (win->transient == 0 && win->floating == 0)
+               if (!win->transient && !win->floating)
                        continue;
-               if (win->iconic == 1)
+               if (win->iconic)
                        continue;
                if (win->ewmh_flags & EWMH_F_FULLSCREEN) {
                        fs_win = win;
@@ -3841,7 +3873,7 @@ max_stack(struct workspace *ws, struct swm_geometry *g)
                        continue;
                }
 
-               if (win->floating && win->floatmaxed == 0 ) {
+               if (win->floating && !win->floatmaxed ) {
                        /*
                         * retain geometry for retrieval on exit from
                         * max_stack mode
@@ -4005,7 +4037,7 @@ 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 == 0)
+       if (!r->ws->always_raise)
                stack();
 
        focus_flush();
@@ -4075,7 +4107,7 @@ uniconify(struct swm_region *r, union arg *args)
        TAILQ_FOREACH(win, &r->ws->winlist, entry) {
                if (win->ws == NULL)
                        continue; /* should never happen */
-               if (win->iconic == 0)
+               if (!win->iconic)
                        continue;
                count++;
        }
@@ -4093,7 +4125,7 @@ uniconify(struct swm_region *r, union arg *args)
        TAILQ_FOREACH(win, &r->ws->winlist, entry) {
                if (win->ws == NULL)
                        continue; /* should never happen */
-               if (win->iconic == 0)
+               if (!win->iconic)
                        continue;
 
                name = get_win_name(win->id);
@@ -4202,7 +4234,7 @@ search_win(struct swm_region *r, union arg *args)
 
        i = 1;
        TAILQ_FOREACH(win, &r->ws->winlist, entry) {
-               if (win->iconic == 1)
+               if (win->iconic)
                        continue;
 
                sw = calloc(1, sizeof(struct search_window));
@@ -4289,7 +4321,7 @@ search_resp_uniconify(char *resp, unsigned long len)
        DNPRINTF(SWM_D_MISC, "search_resp_uniconify: resp: %s\n", resp);
 
        TAILQ_FOREACH(win, &search_r->ws->winlist, entry) {
-               if (win->iconic == 0)
+               if (!win->iconic)
                        continue;
                name = get_win_name(win->id);
                if (name == NULL)
@@ -4473,7 +4505,7 @@ wkill(struct swm_region *r, union arg *args)
                if (r->ws->focus->can_delete)
                        client_msg(r->ws->focus, a_delete, 0);
 
-       xcb_flush(conn);
+       focus_flush();
 }
 
 int
@@ -4624,7 +4656,7 @@ resize(struct ws_win *win, union arg *args)
            "transient: 0x%x\n", win->id, YESNO(win->floating),
            win->transient);
 
-       if (!(win->transient != 0 || win->floating != 0))
+       if (!win->transient && !win->floating)
                return;
 
        /* reject resizes in max mode for floaters (transient ok) */
@@ -4822,7 +4854,7 @@ move(struct ws_win *win, union arg *args)
                return;
 
        win->manual = 1;
-       if (win->floating == 0 && !win->transient) {
+       if (!win->floating && !win->transient) {
                store_float_geom(win, r);
                ewmh_update_win_state(win, ewmh[_NET_WM_STATE_ABOVE].atom,
                    _NET_WM_STATE_ADD);
@@ -4933,7 +4965,7 @@ move_step(struct swm_region *r, union arg *args)
        else
                return;
 
-       if (!(win->transient != 0 || win->floating != 0))
+       if (!win->transient && !win->floating)
                return;
 
        move(win, args);
@@ -6675,7 +6707,7 @@ set_child_transient(struct ws_win *win, xcb_window_t *trans)
                DNPRINTF(SWM_D_MISC, "set_child_transient: parent doesn't exist"
                    " for 0x%x trans 0x%x\n", win->id, win->transient);
 
-               r = root_to_region(win->wa->root);
+               r = root_to_region(win->wa->root, SWM_CK_ALL);
                ws = r->ws;
                /* parent doen't exist in our window list */
                TAILQ_FOREACH(w, &ws->winlist, entry) {
@@ -6833,7 +6865,7 @@ manage_window(xcb_window_t id, uint16_t mapped)
            NULL);
 
        /* Figure out which region the window belongs to. */
-       r = root_to_region(win->wa->root);
+       r = root_to_region(win->wa->root, SWM_CK_ALL);
 
        /* Ignore window border if there is one. */
        WIDTH(win) = win->wa->width;
@@ -6889,7 +6921,7 @@ manage_window(xcb_window_t id, uint16_t mapped)
                free(p);
                p = NULL;
        } else if ((ws_idx = get_ws_idx(win->id)) != -1 &&
-           win->transient == 0) {
+           !win->transient) {
                /* _SWM_WS is set; use that. */
                win->ws = &r->s->ws[ws_idx];
        } else if (trans && (ww = find_window(trans)) != NULL) {
@@ -7097,10 +7129,10 @@ keypress(xcb_key_press_event_t *e)
        last_event_time = e->time;
 
        if (kp->funcid == KF_SPAWN_CUSTOM)
-               spawn_custom(root_to_region(e->root),
+               spawn_custom(root_to_region(e->root, SWM_CK_ALL),
                    &(keyfuncs[kp->funcid].args), kp->spawn_name);
        else if (keyfuncs[kp->funcid].func)
-               keyfuncs[kp->funcid].func(root_to_region(e->root),
+               keyfuncs[kp->funcid].func(root_to_region(e->root, SWM_CK_ALL),
                    &(keyfuncs[kp->funcid].args));
 }
 
@@ -7261,8 +7293,10 @@ configurerequest(xcb_configure_request_event_t *e)
                        wc[i++] = e->stack_mode;
                }
 
-               if (mask != 0)
+               if (mask != 0) {
                        xcb_configure_window(conn, e->window, mask, wc);
+                       xcb_flush(conn);
+               }
        } else if ((!win->manual || win->quirks & SWM_Q_ANYWHERE) &&
            !(win->ewmh_flags & EWMH_F_FULLSCREEN)) {
                if (win->ws->r)
@@ -7315,8 +7349,11 @@ configurenotify(xcb_configure_notify_event_t *e)
 {
        struct ws_win           *win;
 
-       DNPRINTF(SWM_D_EVENT, "configurenotify: window: 0x%x\n",
-           e->window);
+       DNPRINTF(SWM_D_EVENT, "configurenotify: win 0x%x, event win: 0x%x, "
+           "(x,y) WxH: (%d,%d) %ux%u, border: %u, above_sibling: 0x%x, "
+           "override_redirect: %s\n", e->window, e->event, e->x, e->y,
+           e->width, e->height, e->border_width, e->above_sibling,
+           YESNO(e->override_redirect));
 
        win = find_window(e->window);
        if (win) {
@@ -7434,6 +7471,7 @@ void
 enternotify(xcb_enter_notify_event_t *e)
 {
        struct ws_win           *win;
+       struct swm_region       *old_r, *r;
 
        DNPRINTF(SWM_D_FOCUS, "enternotify: time: %u, win (x,y): 0x%x "
            "(%d,%d), mode: %s(%d), detail: %s(%d), root (x,y): 0x%x (%d,%d), "
@@ -7450,13 +7488,32 @@ enternotify(xcb_enter_notify_event_t *e)
                return;
        }
 
+       last_event_time = e->time;
+
        if ((win = find_window(e->event)) == NULL) {
-               DNPRINTF(SWM_D_EVENT, "enternotify: window is NULL; ignoring\n");
+               if (e->event == e->root) {
+                       /* If no windows on pointer region, then focus root. */
+                       r = root_to_region(e->root, SWM_CK_POINTER);
+                       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);
+
+                               xcb_set_input_focus(conn,
+                                   XCB_INPUT_FOCUS_PARENT, e->root, e->time);
+
+                               /* Clear bar since empty. */
+                               bar_update();
+
+                               focus_flush();
+                       }
+               } else {
+                       DNPRINTF(SWM_D_EVENT, "enternotify: window is NULL; "
+                           "ignoring\n");
+               }
                return;
        }
 
-       last_event_time = e->time;
-
        focus_win(get_focus_magic(win));
 
        xcb_flush(conn);
@@ -7655,6 +7712,7 @@ void
 unmapnotify(xcb_unmap_notify_event_t *e)
 {
        struct ws_win           *win;
+       struct workspace        *ws;
 
        DNPRINTF(SWM_D_EVENT, "unmapnotify: window: 0x%x\n", e->window);
 
@@ -7663,22 +7721,28 @@ unmapnotify(xcb_unmap_notify_event_t *e)
        if (win == NULL)
                return;
 
-       if (getstate(e->window) == XCB_ICCCM_WM_STATE_NORMAL) {
-               /* If we were focused, make sure we focus on something else. */
-               if (win == win->ws->focus)
-                       if (focus_mode != SWM_FOCUS_FOLLOW)
-                               win->ws->focus_pending = get_focus_prev(win);
+       ws = win->ws;
 
+       if (getstate(e->window) == XCB_ICCCM_WM_STATE_NORMAL) {
                win->mapped = 0;
                set_win_state(win, XCB_ICCCM_WM_STATE_ICONIC);
 
+               /* If we were focused, make sure we focus on something else. */
+               if (win == ws->focus)
+                       if (focus_mode != SWM_FOCUS_FOLLOW)
+                               ws->focus_pending = get_focus_prev(win);
+
+               unfocus_win(win);
                unmanage_window(win);
                stack();
 
+               DNPRINTF(SWM_D_EVENT, "unmapnotify: focus_pending: 0x%x\n",
+                   ws->focus_pending->id);
+
                if (focus_mode != SWM_FOCUS_FOLLOW) {
-                       if (win->ws->focus_pending) {
-                               focus_win(win->ws->focus_pending);
-                               win->ws->focus_pending = NULL;
+                       if (ws->focus_pending) {
+                               focus_win(ws->focus_pending);
+                               ws->focus_pending = NULL;
                        }
                }
 
@@ -7802,7 +7866,8 @@ int
 enable_wm(void)
 {
        int                     num_screens, i;
-       const uint32_t          val = XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT;
+       const uint32_t          val = XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT |
+           XCB_EVENT_MASK_ENTER_WINDOW;
        xcb_screen_t            *sc;
        xcb_void_cookie_t       wac;
        xcb_generic_error_t     *error;
@@ -8479,7 +8544,7 @@ noconfig:
 
        while (running) {
                while ((evt = xcb_poll_for_event(conn))) {
-                       if (running == 0)
+                       if (!running)
                                goto done;
                        event_handle(evt);
                        free(evt);
@@ -8513,11 +8578,11 @@ noconfig:
                        if (errno != EINTR) {
                                DNPRINTF(SWM_D_MISC, "select failed");
                        }
-               if (restart_wm == 1)
+               if (restart_wm)
                        restart(NULL, NULL);
-               if (search_resp == 1)
+               if (search_resp)
                        search_do_resp();
-               if (running == 0)
+               if (!running)
                        goto done;
                if (bar_alarm) {
                        bar_alarm = 0;