]> code.delx.au - spectrwm/blobdiff - spectrwm.c
Fix unmapnotify to update the win mapping and iconic state.
[spectrwm] / spectrwm.c
index 71c7d9c46edd3cc2330828e450c1d007318bfb8a..f550e1a27c07d68741e1367d2dc28a15732b1991 100644 (file)
@@ -128,6 +128,7 @@ static const char   *buildstr = SPECTRWM_VERSION;
 #define XCB_ICCCM_SIZE_HINT_P_MIN_SIZE         XCB_SIZE_HINT_P_MIN_SIZE
 #define XCB_ICCCM_SIZE_HINT_P_MAX_SIZE         XCB_SIZE_HINT_P_MAX_SIZE
 #define XCB_ICCCM_SIZE_HINT_P_RESIZE_INC       XCB_SIZE_HINT_P_RESIZE_INC
+#define XCB_ICCCM_WM_HINT_INPUT                        XCB_WM_HINT_INPUT
 #define XCB_ICCCM_WM_HINT_X_URGENCY            XCB_WM_HINT_X_URGENCY
 #define XCB_ICCCM_WM_STATE_ICONIC              XCB_WM_STATE_ICONIC
 #define XCB_ICCCM_WM_STATE_WITHDRAWN           XCB_WM_STATE_WITHDRAWN
@@ -268,8 +269,8 @@ xcb_atom_t          a_swm_iconic;
 xcb_atom_t             a_swm_ws;
 volatile sig_atomic_t   running = 1;
 volatile sig_atomic_t   restart_wm = 0;
+xcb_timestamp_t                last_event_time = 0;
 int                    outputs = 0;
-/*int                  last_focus_event = FocusOut;*/
 int                    other_wm;
 int                    ss_enabled = 0;
 int                    xrandr_support;
@@ -279,7 +280,6 @@ unsigned int                numlockmask = 0;
 Display                        *display;
 xcb_connection_t       *conn;
 xcb_key_symbols_t      *syms;
-xcb_timestamp_t                last_event_time;
 
 int                    cycle_empty = 0;
 int                    cycle_visible = 0;
@@ -490,6 +490,7 @@ struct workspace {
        struct layout           *cur_layout;    /* current layout handlers */
        struct ws_win           *focus;         /* may be NULL */
        struct ws_win           *focus_prev;    /* may be NULL */
+       struct ws_win           *focus_pending; /* may be NULL */
        struct swm_region       *r;             /* may be NULL */
        struct swm_region       *old_r;         /* may be NULL */
        struct ws_win_list      winlist;        /* list of windows in ws */
@@ -849,7 +850,6 @@ get_swm_iconic(struct ws_win *win)
        int32_t                         v = 0;
        xcb_get_property_reply_t        *pr = NULL;
 
-
        pr = xcb_get_property_reply(conn,
            xcb_get_property(conn, 0, win->id, a_swm_iconic,
            XCB_ATOM_INTEGER, 0, 1), NULL);
@@ -2146,7 +2146,8 @@ set_win_state(struct ws_win *win, uint16_t state)
 {
        uint16_t                data[2] = { state, XCB_ATOM_NONE };
 
-       DNPRINTF(SWM_D_EVENT, "set_win_state: window: 0x%x\n", win->id);
+       DNPRINTF(SWM_D_EVENT, "set_win_state: window: 0x%x, state: %u\n",
+           win->id, state);
 
        if (win == NULL)
                return;
@@ -2847,6 +2848,8 @@ switchws(struct swm_region *r, union arg *args)
        if (new_ws == old_ws)
                return;
 
+       unfocus_win(old_ws->focus);
+
        other_r = new_ws->r;
        if (other_r == NULL) {
                /* the other workspace is hidden, hide this one */
@@ -2862,8 +2865,6 @@ switchws(struct swm_region *r, union arg *args)
        this_r->ws = new_ws;
        new_ws->r = this_r;
 
-       unmap_window(old_ws->focus);
-
        stack();
 
        /* unmap old windows */
@@ -2871,20 +2872,16 @@ switchws(struct swm_region *r, union arg *args)
                TAILQ_FOREACH(win, &old_ws->winlist, entry)
                        unmap_window(win);
 
-       new_ws->focus = get_region_focus(new_ws->r);
+       new_ws->focus_pending = get_region_focus(new_ws->r);
 
-       /* unmap old windows */
-       if (unmap_old)
-               TAILQ_FOREACH(win, &old_ws->winlist, entry)
-                       unmap_window(win);
-
-       /* make sure bar gets updated if ws is empty */
-       if (!new_ws->focus)
-               bar_update();
-
-       /* make sure bar gets updated if ws is empty */
-       if (!new_ws->focus)
+       if (new_ws->focus_pending) {
+               /* if workspaces were swapped, then don't wait to set focus */
+               if (old_ws->r)
+                       focus_win(new_ws->focus);
+       } else {
+               /* make sure bar gets updated if ws is empty */
                bar_update();
+       }
 
        focus_flush();
 
@@ -3031,7 +3028,6 @@ swapwin(struct swm_region *r, union arg *args)
        struct ws_win           *cur_focus;
        struct ws_win_list      *wl;
 
-
        DNPRINTF(SWM_D_WS, "swapwin: id: %d, screen[%d]:%dx%d+%d+%d, ws: %d\n",
            args->id, r->s->idx, WIDTH(r), HEIGHT(r), X(r), Y(r), r->ws->idx);
 
@@ -3525,7 +3521,7 @@ stack_master(struct workspace *ws, struct swm_geometry *g, int rot, int flip)
        int                     w_inc = 1, h_inc, w_base = 1, h_base;
        int                     hrh, extra = 0, h_slice, last_h = 0;
        int                     split, colno, winno, mwin, msize, mscale;
-       int                     remain, missing, v_slice, reconfigure;
+       int                     remain, missing, v_slice, reconfigure = 0;
        int                     bordered = 1;
 
        DNPRINTF(SWM_D_STACK, "stack_master: workspace: %d, rot: %s, "
@@ -4436,7 +4432,6 @@ wkill(struct swm_region *r, union arg *args)
        xcb_flush(conn);
 }
 
-
 int
 floating_toggle_win(struct ws_win *win)
 {
@@ -4900,7 +4895,6 @@ move_step(struct swm_region *r, union arg *args)
        move(win, args);
 }
 
-
 /* user/key callable function IDs */
 enum keyfuncid {
        KF_BAR_TOGGLE,
@@ -6509,7 +6503,6 @@ struct config_option configopt[] = {
        { "layout",                     setlayout,      0 },
 };
 
-
 int
 conf_load(char *filename, int keymapping)
 {
@@ -6517,7 +6510,7 @@ conf_load(char *filename, int keymapping)
        char                    *line, *cp, *optsub, *optval;
        size_t                  linelen, lineno = 0;
        int                     wordlen, i, optidx;
-       struct config_option    *opt;
+       struct config_option    *opt = NULL;
 
        DNPRINTF(SWM_D_CONF, "conf_load: begin\n");
 
@@ -6566,7 +6559,7 @@ conf_load(char *filename, int keymapping)
                            filename, lineno, wordlen, cp);
                        goto out;
                }
-               if (keymapping && strcmp(opt->optname, "bind")) {
+               if (keymapping && opt && strcmp(opt->optname, "bind")) {
                        warnx("%s: line %zd: invalid option %.*s",
                            filename, lineno, wordlen, cp);
                        goto out;
@@ -6754,12 +6747,12 @@ manage_window(xcb_window_t id, uint16_t mapped)
 {
        xcb_window_t            trans = XCB_WINDOW_NONE;
        struct ws_win           *win, *ww;
-       int                     ws_idx, border_me = 0;
+       int                     ws_idx;
        char                    ws_idx_str[SWM_PROPLEN];
        struct swm_region       *r;
        struct pid_e            *p;
        struct quirk            *qp;
-       uint32_t                event_mask, i;
+       uint32_t                i, wa[2];
        xcb_icccm_get_wm_protocols_reply_t      wpr;
 
        if ((win = find_window(id)) != NULL) {
@@ -6800,9 +6793,9 @@ manage_window(xcb_window_t id, uint16_t mapped)
        /* Ignore window border if there is one. */
        WIDTH(win) = win->wa->width;
        HEIGHT(win) = win->wa->height;
-       X(win) = win->wa->x + win->wa->border_width;
-       Y(win) = win->wa->y + win->wa->border_width;
-       win->bordered = 0;
+       X(win) = win->wa->x + win->wa->border_width - border_width;
+       Y(win) = win->wa->y + win->wa->border_width - border_width;
+       win->bordered = 1;
        win->mapped = mapped;
        win->floatmaxed = 0;
        win->ewmh_flags = 0;
@@ -6857,7 +6850,6 @@ manage_window(xcb_window_t id, uint16_t mapped)
        } else if (trans && (ww = find_window(trans)) != NULL) {
                /* Launch transients in the same ws as parent. */
                win->ws = ww->ws;
-               border_me = 1;
        } else {
                win->ws = r->ws;
        }
@@ -6887,7 +6879,6 @@ manage_window(xcb_window_t id, uint16_t mapped)
                        DNPRINTF(SWM_D_CLASS, "manage_window: java window "
                            "detected.\n");
                        win->java = 1;
-                       border_me = 1;
                }
 
                TAILQ_FOREACH(qp, &quirks, entry) {
@@ -6895,20 +6886,16 @@ manage_window(xcb_window_t id, uint16_t mapped)
                            !strcmp(win->ch.instance_name, qp->name)) {
                                DNPRINTF(SWM_D_CLASS, "manage_window: on quirks"
                                    "list; mask: 0x%lx\n", qp->quirk);
-                               if (qp->quirk & SWM_Q_FLOAT) {
+                               if (qp->quirk & SWM_Q_FLOAT)
                                        win->floating = 1;
-                                       border_me = 1;
-                               }
                                win->quirks = qp->quirk;
                        }
                }
        }
 
        /* Alter window position if quirky */
-       if (win->quirks & SWM_Q_ANYWHERE) {
+       if (win->quirks & SWM_Q_ANYWHERE)
                win->manual = 1;
-               border_me = 1;
-       }
 
        /* Reset font sizes (the bruteforce way; no default keybinding). */
        if (win->quirks & SWM_Q_XTERM_FONTADJ) {
@@ -6918,28 +6905,22 @@ manage_window(xcb_window_t id, uint16_t mapped)
                        fake_keypress(win, XK_KP_Add, XCB_MOD_MASK_SHIFT);
        }
 
-       if (border_me) {
-               win->bordered = 1;
-               X(win) -= border_width;
-               Y(win) -= border_width;
-       }
-
        /* Make sure window is positioned inside its region, if its active. */
-       if (win->ws->r)
+       if (win->ws->r) {
                constrain_window(win, win->ws->r, 0);
-
-       if (win->ws->r || border_me)
                update_window(win);
+       }
 
-       /* Select which X events to monitor. */
-       event_mask = XCB_EVENT_MASK_ENTER_WINDOW | XCB_EVENT_MASK_FOCUS_CHANGE |
-           XCB_EVENT_MASK_PROPERTY_CHANGE | XCB_EVENT_MASK_STRUCTURE_NOTIFY;
+       /* Select which X events to monitor and set border pixel color. */
+       wa[0] = win->s->c[SWM_S_COLOR_UNFOCUS].pixel;
+       wa[1] = XCB_EVENT_MASK_ENTER_WINDOW | XCB_EVENT_MASK_PROPERTY_CHANGE |
+           XCB_EVENT_MASK_STRUCTURE_NOTIFY;
 #ifdef SWM_DEBUG
-       event_mask |= XCB_EVENT_MASK_LEAVE_WINDOW;
+       wa[1] |= XCB_EVENT_MASK_LEAVE_WINDOW | XCB_EVENT_MASK_FOCUS_CHANGE;
 #endif
 
-       xcb_change_window_attributes(conn, win->id, XCB_CW_EVENT_MASK,
-           &event_mask);
+       xcb_change_window_attributes(conn, win->id, XCB_CW_BORDER_PIXEL |
+           XCB_CW_EVENT_MASK, wa);
 
 out:
        /* Figure out where to stack the window in the workspace. */
@@ -7016,8 +6997,6 @@ unmanage_window(struct ws_win *win)
                        parent->focus_child = NULL;
        }
 
-       focus_win(get_focus_prev(win));
-
        TAILQ_REMOVE(&win->ws->winlist, win, entry);
        TAILQ_INSERT_TAIL(&win->ws->unmanagedlist, win, entry);
 }
@@ -7321,14 +7300,20 @@ destroynotify(xcb_destroy_notify_event_t *e)
                return;
        }
 
-       /* make sure we focus on something */
-       win->floating = 0;
+       /* If we were focused, make sure we focus on something else. */
+       if (win == win->ws->focus)
+               win->ws->focus_pending = get_focus_prev(win);
 
        unmanage_window(win);
-       free_window(win);
-
        stack();
 
+       if (win->ws->focus_pending) {
+               focus_win(win->ws->focus_pending);
+               win->ws->focus_pending = NULL;
+       }
+
+       free_window(win);
+
        focus_flush();
 }
 
@@ -7459,9 +7444,10 @@ mapnotify(xcb_map_notify_event_t *e)
        win->mapped = 1;
        set_win_state(win, XCB_ICCCM_WM_STATE_NORMAL);
 
-       /* Focus on window if it is selected. */
-       if (win->ws->focus == win)
+       if (win->ws->focus_pending == win) {
                focus_win(win);
+               win->ws->focus_pending = NULL;
+       }
 
        xcb_flush(conn);
 }
@@ -7506,7 +7492,7 @@ maprequest(xcb_map_request_event_t *e)
                stack();
 
        /* The new window should get focus. */
-       win->ws->focus = get_focus_magic(win);
+       win->ws->focus_pending = get_focus_magic(win);
 
        /* Ignore EnterNotify to handle the mapnotify without interference. */
        if (focus_mode == SWM_FOCUS_DEFAULT)
@@ -7554,7 +7540,6 @@ void
 propertynotify(xcb_property_notify_event_t *e)
 {
        struct ws_win           *win;
-       union arg a;
 #ifdef SWM_DEBUG
        char                    *name;
 
@@ -7572,30 +7557,30 @@ propertynotify(xcb_property_notify_event_t *e)
        if (e->atom == a_swm_iconic) {
                if (e->state == XCB_PROPERTY_DELETE) {
                        /* The window is no longer iconic, restack ws. */
+                       win->ws->focus_pending = get_focus_magic(win);
                        stack();
 
-                       /* The window should get focus. */
-                       win->ws->focus = get_focus_magic(win);
-
                        /* Flush EnterNotify for mapnotify, if needed. */
                        focus_flush();
                        return;
                } else if (e->state == XCB_PROPERTY_NEW_VALUE) {
-                       win->ws->focus = NULL;
-
+                       win->ws->focus_pending = get_focus_prev(win);
                        unfocus_win(win);
                        unmap_window(win);
 
                        if (win->ws->r) {
-                               focus_win(get_focus_prev(win));
                                stack();
+                               focus_win(win->ws->focus_pending);
+                               win->ws->focus_pending = NULL;
                                focus_flush();
                        }
                }
        } else if (e->atom == a_state && e->state == XCB_PROPERTY_NEW_VALUE) {
-               /* Focus on window if it is selected. */
-               if (win->ws->focus == win)
+               /* State just changed, make sure it gets focused if mapped. */
+               if (win->mapped && win->ws->focus_pending == win) {
+                       win->ws->focus_pending = NULL;
                        focus_win(win);
+               }
        }
 
        switch (e->atom) {
@@ -7623,8 +7608,21 @@ unmapnotify(xcb_unmap_notify_event_t *e)
                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)
+                       win->ws->focus_pending = get_focus_prev(win);
+
+               win->mapped = 0;
+               set_win_state(win, XCB_ICCCM_WM_STATE_ICONIC);
+
                unmanage_window(win);
                stack();
+
+               if (win->ws->focus_pending) {
+                       focus_win(win->ws->focus_pending);
+                       win->ws->focus_pending = NULL;
+               }
+
                focus_flush();
        }
 }
@@ -8101,6 +8099,8 @@ setup_screens(void)
                        ws->idx = j;
                        ws->name = NULL;
                        ws->focus = NULL;
+                       ws->focus_prev = NULL;
+                       ws->focus_pending = NULL;
                        ws->r = NULL;
                        ws->old_r = NULL;
                        TAILQ_INIT(&ws->winlist);