JasonWoof Got questions, comments, patches, etc.? Contact Jason Woofenden
Fix bug where a transient destroy would not give back the focus to the
[spectrwm.git] / scrotwm.c
index a4e3f61..d31cde5 100644 (file)
--- a/scrotwm.c
+++ b/scrotwm.c
@@ -338,6 +338,7 @@ struct workspace {
        struct ws_win           *focus;         /* may be NULL */
        struct ws_win           *focus_prev;    /* 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 */
 
        /* stacker state */
@@ -547,7 +548,7 @@ void                        destroynotify(XEvent *);
 void                   enternotify(XEvent *);
 void                   focusin(XEvent *);
 void                   focusout(XEvent *);
-void                   mappingnotify(XEvent *);
+void                   mapnotify(XEvent *);
 void                   maprequest(XEvent *);
 void                   propertynotify(XEvent *);
 void                   unmapnotify(XEvent *);
@@ -563,7 +564,7 @@ void                        (*handler[LASTEvent])(XEvent *) = {
                                [EnterNotify] = enternotify,
                                [FocusIn] = focusin,
                                [FocusOut] = focusout,
-                               [MappingNotify] = mappingnotify,
+                               [MapNotify] = mapnotify,
                                [MapRequest] = maprequest,
                                [PropertyNotify] = propertynotify,
                                [UnmapNotify] = unmapnotify,
@@ -578,7 +579,7 @@ sighdlr(int sig)
        switch (sig) {
        case SIGCHLD:
                while ((pid = waitpid(WAIT_ANY, NULL, WNOHANG)) != -1) {
-                       DNPRINTF(SWM_D_MISC, stderr, "reaping: %d\n", pid);
+                       DNPRINTF(SWM_D_MISC, "reaping: %d\n", pid);
                        if (pid <= 0)
                                break;
                }
@@ -908,6 +909,36 @@ bar_setup(struct swm_region *r)
 }
 
 void
+set_win_state(struct ws_win *win, long state)
+{
+       long                    data[] = {state, None};
+
+       DNPRINTF(SWM_D_EVENT, "set_win_state: window: %lu\n", win->id);
+
+       XChangeProperty(display, win->id, astate, astate, 32, PropModeReplace,
+           (unsigned char *)data, 2);
+}
+
+long
+getstate(Window w)
+{
+       int                     format, status;
+       long                    result = -1;
+       unsigned char           *p = NULL;
+       unsigned long           n, extra;
+       Atom                    real;
+
+       status = XGetWindowProperty(display, w, astate, 0L, 2L, False, astate,
+           &real, &format, &n, &extra, (unsigned char **)&p);
+       if (status != Success)
+               return (-1);
+       if (n != 0)
+               result = *((long *)p);
+       XFree(p);
+       return (result);
+}
+
+void
 version(struct swm_region *r, union arg *args)
 {
        bar_version = !bar_version;
@@ -981,6 +1012,16 @@ quit(struct swm_region *r, union arg *args)
 }
 
 void
+unmap_window(struct ws_win *win)
+{
+       if (win == NULL)
+               return;
+
+       set_win_state(win, IconicState);
+       XUnmapWindow(display, win->id);
+}
+
+void
 unmap_all(void)
 {
        struct ws_win           *win;
@@ -989,7 +1030,7 @@ unmap_all(void)
        for (i = 0; i < ScreenCount(display); i++)
                for (j = 0; j < SWM_WS_MAX; j++)
                        TAILQ_FOREACH(win, &screens[i].ws[j].winlist, entry)
-                               XUnmapWindow(display, win->id);
+                               unmap_window(win);
 }
 
 void
@@ -1217,15 +1258,21 @@ switchws(struct swm_region *r, union arg *args)
        other_r = new_ws->r;
        if (other_r == NULL) {
                /* if the other workspace is hidden, switch windows */
-               /* map new window first to prevent ugly blinking */
+               if (old_ws->r != NULL)
+                       old_ws->old_r = old_ws->r;
                old_ws->r = NULL;
                old_ws->restack = 1;
 
-               TAILQ_FOREACH(win, &new_ws->winlist, entry)
-                       XMapRaised(display, win->id);
+               /*
+                * Map new windows first if they were here before
+                * to minimize ugly blinking.
+                */
+               if (new_ws->old_r == this_r)
+                       TAILQ_FOREACH(win, &new_ws->winlist, entry)
+                               XMapRaised(display, win->id);
 
                TAILQ_FOREACH(win, &old_ws->winlist, entry)
-                       XUnmapWindow(display, win->id);
+                       unmap_window(win);
        } else {
                other_r->ws = old_ws;
                old_ws->r = other_r;
@@ -1487,6 +1534,9 @@ stack_floater(struct ws_win *win, struct swm_region *r)
        unsigned int            mask;
        XWindowChanges          wc;
 
+       if (win == NULL)
+               return;
+
        bzero(&wc, sizeof wc);
        mask = CWX | CWY | CWBorderWidth | CWWidth | CWHeight;
        if ((win->quirks & SWM_Q_FULLSCREEN) && (win->g.w == WIDTH(r)) &&
@@ -1815,32 +1865,19 @@ max_stack(struct workspace *ws, struct swm_geometry *g)
 {
        XWindowChanges          wc;
        struct swm_geometry     gg = *g;
-       struct ws_win           *win, *winfocus;
+       struct ws_win           *win, *wintrans = NULL;
        unsigned int            mask;
        int                     winno;
 
-       /* XXX this function needs to be rewritten it sucks crap */
-
        DNPRINTF(SWM_D_STACK, "max_stack: workspace: %d\n", ws->idx);
 
        winno = count_win(ws, 0);
        if (winno == 0 && count_win(ws, 1) == 0)
                return;
 
-       if (ws->focus == NULL)
-               ws->focus = TAILQ_FIRST(&ws->winlist);
-       winfocus = ws->focus;
-
        TAILQ_FOREACH(win, &ws->winlist, entry) {
-               if (win->transient != 0 || win->floating != 0) {
-                       if (win == ws->focus) {
-                               /* XXX maximize? */
-                               stack_floater(win, ws->r);
-                               XMapRaised(display, win->id);
-                       } else {
-                               /* XXX this sucks */
-                               XUnmapWindow(display, win->id);
-                       }
+               if (win->transient != 0) {
+                       wintrans = win;
                } else {
                        bzero(&wc, sizeof wc);
                        wc.border_width = 1;
@@ -1850,16 +1887,17 @@ max_stack(struct workspace *ws, struct swm_geometry *g)
                        win->g.h = wc.height = gg.h;
                        mask = CWX | CWY | CWWidth | CWHeight | CWBorderWidth;
                        XConfigureWindow(display, win->id, mask, &wc);
-
-                       if (win == ws->focus) {
-                               XMapRaised(display, win->id);
-                       } else
-                               XUnmapWindow(display, win->id);
+                       if (win != ws->focus)
+                               unmap_window(win);
                }
        }
 
-       if (winfocus)
-               focus_win(winfocus); /* has to be done outside of the loop */
+       /* put the last transient on top */
+       if (wintrans) {
+               stack_floater(wintrans, ws->r);
+               XMapRaised(display, wintrans->id);
+               focus_win(wintrans); /* override */
+       }
 }
 
 void
@@ -1892,7 +1930,7 @@ send_to_ws(struct swm_region *r, union arg *args)
        if (winfocus == NULL)
                winfocus = win;
 
-       XUnmapWindow(display, win->id);
+       unmap_window(win);
 
        TAILQ_REMOVE(&ws->winlist, win, entry);
 
@@ -2809,17 +2847,6 @@ buttonpress(XEvent *e)
                        buttons[i].func(win, &buttons[i].args);
 }
 
-void
-set_win_state(struct ws_win *win, long state)
-{
-       long                    data[] = {state, None};
-
-       DNPRINTF(SWM_D_EVENT, "set_win_state: window: %lu\n", win->id);
-
-       XChangeProperty(display, win->id, astate, astate, 32, PropModeReplace,
-           (unsigned char *)data, 2);
-}
-
 const char *quirkname[] = {
        "NONE",         /* config string for "no value" */
        "FLOAT",
@@ -3202,10 +3229,10 @@ conf_load(char *filename)
 struct ws_win *
 manage_window(Window id)
 {
-       Window                  trans;
+       Window                  trans = 0;
        struct workspace        *ws;
        struct ws_win           *win, *ww;
-       int                     format, i, ws_idx, n;
+       int                     format, i, ws_idx, n, border_me = 0;
        unsigned long           nitems, bytes;
        Atom                    ws_idx_atom = 0, type;
        Atom                    *prot = NULL, *pp;
@@ -3227,8 +3254,8 @@ manage_window(Window id)
                XGetWindowProperty(display, id, ws_idx_atom, 0, SWM_PROPLEN,
                    False, XA_STRING, &type, &format, &nitems, &bytes, &prop);
        XGetWindowAttributes(display, id, &win->wa);
+       XGetWMNormalHints(display, id, &win->sh, &mask);
        XGetTransientForHint(display, id, &trans);
-       XGetWMNormalHints(display, id, &win->sh, &mask); /* XXX function? */
        if (trans) {
                win->transient = trans;
                DNPRINTF(SWM_D_MISC, "manage_window: win %u transient %u\n",
@@ -3260,19 +3287,19 @@ manage_window(Window id)
        } else {
                ws = r->ws;
                /* this should launch transients in the same ws as parent */
-               /* XXX doesn't work for intel xrandr */
                if (id && trans)
                        if ((ww = find_window(trans)) != NULL)
                                if (ws->r) {
                                        ws = ww->ws;
-                                       r = ww->ws->r;
+                                       if (ww->ws->r)
+                                               r = ww->ws->r;
+                                       else
+                                               fprintf(stderr,
+                                                   "fix this bug mcbride\n");
+                                       border_me = 1;
                                }
        }
 
-       /* shouldn't happen but does... */
-       if (ws->r == NULL)
-               ws->r = r; /* use found r since it isn't filled in */
-
        /* set up the window layout */
        win->id = id;
        win->ws = ws;
@@ -3319,12 +3346,10 @@ manage_window(Window id)
                        mask |= CWY;
                }
                if (win->g.w + win->g.x > WIDTH(r)) {
-                       win->g.x = wc.x = WIDTH(win->ws->r) - win->g.w - 2;
+                       win->g.x = wc.x = WIDTH(r) - win->g.w - 2;
                        mask |= CWX;
                }
-               wc.border_width = 1;
-               mask |= CWBorderWidth;
-               XConfigureWindow(display, win->id, mask, &wc);
+               border_me = 1;
        }
 
        /* Reset font sizes (the bruteforce way; no default keybinding). */
@@ -3335,13 +3360,21 @@ manage_window(Window id)
                        fake_keypress(win, XK_KP_Add, ShiftMask);
        }
 
+       /* border me */
+       if (border_me) {
+               bzero(&wc, sizeof wc);
+               wc.border_width = 1;
+               mask = CWBorderWidth;
+               XConfigureWindow(display, win->id, mask, &wc);
+       }
+
        XSelectInput(display, id, EnterWindowMask | FocusChangeMask |
            PropertyChangeMask | StructureNotifyMask);
 
        set_win_state(win, NormalState);
 
        /* floaters need to be mapped if they are in the current workspace */
-       if (win->floating && (ws->idx == r->ws->idx))
+       if ((win->floating || win->transient) && (ws->idx == r->ws->idx))
                XMapRaised(display, win->id);
 
        return (win);
@@ -3461,7 +3494,11 @@ destroynotify(XEvent *e)
                /* find a window to focus */
                ws = win->ws;
                wl = &ws->winlist;
-               if (ws->focus == win) {
+
+               /* if we are transient give focus to parent */
+               if (win->transient)
+                       winfocus = find_window(win->transient);
+               else if (ws->focus == win) {
                        if (TAILQ_FIRST(wl) == win)
                                winfocus = TAILQ_NEXT(win, entry);
                        else {
@@ -3488,7 +3525,7 @@ enternotify(XEvent *e)
 
        if (ignore_enter) {
                /* eat event(r) to prevent autofocus */
-               ignore_enter--;
+               ignore_enter = 0;
                return;
        }
        /*
@@ -3515,11 +3552,16 @@ focusout(XEvent *e)
 }
 
 void
-mappingnotify(XEvent *e)
+mapnotify(XEvent *e)
 {
+       struct ws_win           *win;
        XMappingEvent           *ev = &e->xmapping;
 
-       DNPRINTF(SWM_D_EVENT, "mappingnotify: window: %lu\n", ev->window);
+       DNPRINTF(SWM_D_EVENT, "mapnotify: window: %lu\n", ev->window);
+
+       win = find_window(ev->window);
+       if (win)
+               set_win_state(win, NormalState);
 
        XRefreshKeyboardMapping(ev);
        if (ev->request == MappingKeyboard)
@@ -3550,7 +3592,8 @@ maprequest(XEvent *e)
        /* make new win focused */
        win = find_window(ev->window);
        r = root_to_region(win->wa.root);
-       if (win->ws == r->ws) /* XXX this probably breaks multi screen */
+
+       if (win->ws == r->ws)
                focus_win(win);
 }
 
@@ -3592,7 +3635,40 @@ propertynotify(XEvent *e)
 void
 unmapnotify(XEvent *e)
 {
+       struct ws_win           *win, *winfocus;
+       struct workspace        *ws;
+
        DNPRINTF(SWM_D_EVENT, "unmapnotify: window: %lu\n", e->xunmap.window);
+
+       /* determine if we need to help unmanage this window */
+       win = find_window(e->xunmap.window);
+       if (win == NULL)
+               return;
+       if (win->transient)
+               return;
+
+       if (getstate(e->xunmap.window) == NormalState) {
+               /*
+                * this window does not have a destroy event but but it is no
+                * longer visible due to the app unmapping it so unmanage it
+                */
+
+               /* find something to focus */
+               ws = win->ws;
+               winfocus = TAILQ_PREV(win, ws_win_list, entry);
+               if (TAILQ_FIRST(&ws->winlist) == win)
+                       winfocus = TAILQ_NEXT(win, entry);
+               else {
+                       winfocus = TAILQ_PREV(ws->focus, ws_win_list, entry);
+                       if (winfocus == NULL)
+                               winfocus = TAILQ_LAST(&ws->winlist, ws_win_list);
+               }
+
+               /* trash window and refocus */
+               unmanage_window(win);
+               stack();
+               focus_win(winfocus);
+       }
 }
 
 void
@@ -3642,25 +3718,6 @@ active_wm(void)
        return (0);
 }
 
-long
-getstate(Window w)
-{
-       int                     format, status;
-       long                    result = -1;
-       unsigned char           *p = NULL;
-       unsigned long           n, extra;
-       Atom                    real;
-
-       status = XGetWindowProperty(display, w, astate, 0L, 2L, False, astate,
-           &real, &format, &n, &extra, (unsigned char **)&p);
-       if (status != Success)
-               return (-1);
-       if (n != 0)
-               result = *((long *)p);
-       XFree(p);
-       return (result);
-}
-
 void
 new_region(struct swm_screen *s, int x, int y, int w, int h)
 {
@@ -3747,7 +3804,7 @@ scan_xrandr(int i)
 
        /* remove any old regions */
        while ((r = TAILQ_FIRST(&screens[i].rl)) != NULL) {
-               r->ws->r = NULL;
+               r->ws->old_r = r->ws->r = NULL;
                XDestroyWindow(display, r->bar_window);
                TAILQ_REMOVE(&screens[i].rl, r, entry);
                TAILQ_INSERT_TAIL(&screens[i].orl, r, entry);
@@ -3815,7 +3872,7 @@ screenchange(XEvent *e) {
        /* hide any windows that went away */
        TAILQ_FOREACH(r, &screens[i].rl, entry)
                TAILQ_FOREACH(win, &r->ws->winlist, entry)
-                       XUnmapWindow(display, win->id);
+                       unmap_window(win);
 
        /* add bars to all regions */
        for (i = 0; i < ScreenCount(display); i++)
@@ -3834,7 +3891,7 @@ setup_screens(void)
        int                     errorbase, major, minor;
        struct workspace        *ws;
        int                     ws_idx_atom;
-
+       long                    state, manage;
 
        if ((screens = calloc(ScreenCount(display),
             sizeof(struct swm_screen))) == NULL)
@@ -3872,6 +3929,7 @@ setup_screens(void)
                        ws->restack = 1;
                        ws->focus = NULL;
                        ws->r = NULL;
+                       ws->old_r = NULL;
                        TAILQ_INIT(&ws->winlist);
 
                        for (k = 0; layouts[k].l_stack != NULL; k++)
@@ -3899,8 +3957,9 @@ setup_screens(void)
                            XGetTransientForHint(display, wins[j], &d1))
                                continue;
 
-                       if (wa.map_state == IsViewable ||
-                           getstate(wins[j]) == NormalState)
+                       state = getstate(wins[j]);
+                       manage = state == NormalState || state == IconicState;
+                       if (wa.map_state == IsViewable || manage)
                                manage_window(wins[j]);
                }
                /* transient windows */
@@ -3908,9 +3967,10 @@ setup_screens(void)
                        if (!XGetWindowAttributes(display, wins[j], &wa))
                                continue;
 
+                       state = getstate(wins[j]);
+                       manage = state == NormalState || state == IconicState;
                        if (XGetTransientForHint(display, wins[j], &d1) &&
-                           (wa.map_state == IsViewable || getstate(wins[j]) ==
-                           NormalState))
+                           manage)
                                manage_window(wins[j]);
                 }
                 if (wins) {