JasonWoof Got questions, comments, patches, etc.? Contact Jason Woofenden
work around windows that lie about their parent. try to find a window
[spectrwm.git] / scrotwm.c
index 3279e7f..958f652 100644 (file)
--- a/scrotwm.c
+++ b/scrotwm.c
@@ -1,9 +1,10 @@
 /* $scrotwm$ */
 /*
- * Copyright (c) 2009-2010 Marco Peereboom <marco@peereboom.us>
- * Copyright (c) 2009 Ryan McBride <mcbride@countersiege.com>
+ * Copyright (c) 2009-2010-2011 Marco Peereboom <marco@peereboom.us>
+ * Copyright (c) 2009-2010-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) 2011 Jason L. Wright <jason@thought.net>
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -99,7 +100,7 @@ static const char    *cvstag = "$scrotwm$";
 #endif
 #endif
 
-/* #define SWM_DEBUG */
+/*#define SWM_DEBUG*/
 #ifdef SWM_DEBUG
 #define DPRINTF(x...)          do { if (swm_debug) fprintf(stderr, x); } while (0)
 #define DNPRINTF(n,x...)       do { if (swm_debug & n) fprintf(stderr, x); } while (0)
@@ -286,6 +287,7 @@ struct ws_win {
        XWindowAttributes       wa;
        XSizeHints              sh;
        XClassHint              ch;
+       XWMHints                *hints;
 };
 TAILQ_HEAD(ws_win_list, ws_win);
 
@@ -402,9 +404,8 @@ union arg {
 };
 
 void   focus(struct swm_region *, union arg *);
-void   focus_magic(struct ws_win *, int);
-#define SWM_F_GENERIC          (0)
-#define SWM_F_TRANSIENT                (1)
+void   focus_magic(struct ws_win *);
+
 /* quirks */
 struct quirk {
        char                    *class;
@@ -656,7 +657,7 @@ ewmh_set_win_fullscreen(struct ws_win *win, int fs)
                win->g.y = rg.y;
                win->g.w = rg.w;
                win->g.h = rg.h;
-       }       else {
+       } else {
                if (win->g_floatvalid) {
                        /* refloat at last floating relative position */
                        win->g.x = win->g_float.x - win->rg_float.x + rg.x;
@@ -933,8 +934,9 @@ dumpwins(struct swm_region *r, union arg *args)
                if (!XGetWindowAttributes(display, win->id, &wa))
                        fprintf(stderr, "window: %lu failed "
                            "XGetWindowAttributes\n", win->id);
-               fprintf(stderr, "window: %lu map_state: %d state: %d\n",
-                   win->id, wa.map_state, state);
+               fprintf(stderr, "window: %lu map_state: %d state: %d "
+                   "transient: %lu\n",
+                   win->id, wa.map_state, state, win->transient);
        }
 
        fprintf(stderr, "===== unmanaged window list =====\n");
@@ -943,8 +945,9 @@ dumpwins(struct swm_region *r, union arg *args)
                if (!XGetWindowAttributes(display, win->id, &wa))
                        fprintf(stderr, "window: %lu failed "
                            "XGetWindowAttributes\n", win->id);
-               fprintf(stderr, "window: %lu map_state: %d state: %d\n",
-                   win->id, wa.map_state, state);
+               fprintf(stderr, "window: %lu map_state: %d state: %d "
+                   "transient: %lu\n",
+                   win->id, wa.map_state, state, win->transient);
        }
 
        fprintf(stderr, "=================================\n");
@@ -1379,6 +1382,18 @@ bar_setup(struct swm_region *r)
 }
 
 void
+drain_enter_notify(void)
+{
+       int                     i = 0;
+       XEvent                  cne;
+
+       while (XCheckMaskEvent(display, EnterWindowMask, &cne))
+               i++;
+
+       DNPRINTF(SWM_D_MISC, "drain_enter_notify: drained %d\n", i);
+}
+
+void
 set_win_state(struct ws_win *win, long state)
 {
        long                    data[] = {state, None};
@@ -1898,6 +1913,7 @@ focus_win(struct ws_win *win)
                if (win->java == 0)
                        XSetInputFocus(display, win->id,
                            RevertToParent, CurrentTime);
+               XMapRaised(display, win->id);
                grabbuttons(win, 1);
                XSetWindowBorder(display, win->id,
                    win->ws->r->s->c[SWM_S_COLOR_FOCUS].color);
@@ -1956,12 +1972,16 @@ switchws(struct swm_region *r, union arg *args)
        stack();
        a.id = SWM_ARG_ID_FOCUSCUR;
        focus(new_ws->r, &a);
+
        bar_update();
 
        /* unmap old windows */
        if (unmap_old)
                TAILQ_FOREACH(win, &old_ws->winlist, entry)
                        unmap_window(win);
+
+       if (focus_mode == SWM_FOCUS_DEFAULT)
+               drain_enter_notify();
 }
 
 void
@@ -2064,6 +2084,29 @@ cyclescr(struct swm_region *r, union arg *args)
 }
 
 void
+sort_windows(struct ws_win_list *wl)
+{
+       struct ws_win           *win, *parent, *nxt;
+
+       if (wl == NULL)
+               return;
+
+       for (win = TAILQ_FIRST(wl); win != TAILQ_END(wl); win = nxt) {
+               nxt = TAILQ_NEXT(win, entry);
+               if (win->transient) {
+                       parent = find_window(win->transient);
+                       if (parent == NULL) {
+                               fprintf(stderr, "not possible bug\n");
+                               continue;
+                       }
+                       TAILQ_REMOVE(wl, win, entry);
+                       TAILQ_INSERT_AFTER(wl, parent, win, entry);
+               }
+       }
+
+}
+
+void
 swapwin(struct swm_region *r, union arg *args)
 {
        struct ws_win           *target, *source;
@@ -2084,8 +2127,12 @@ swapwin(struct swm_region *r, union arg *args)
 
        switch (args->id) {
        case SWM_ARG_ID_SWAPPREV:
+               if (source->transient)
+                       source = find_window(source->transient);
                target = TAILQ_PREV(source, ws_win_list, entry);
-               TAILQ_REMOVE(wl, cur_focus, entry);
+               if (target && target->transient)
+                       target = find_window(target->transient);
+               TAILQ_REMOVE(wl, source, entry);
                if (target == NULL)
                        TAILQ_INSERT_TAIL(wl, source, entry);
                else
@@ -2093,6 +2140,9 @@ swapwin(struct swm_region *r, union arg *args)
                break;
        case SWM_ARG_ID_SWAPNEXT:
                target = TAILQ_NEXT(source, entry);
+               /* move the parent and let the sort handle the move */
+               if (source->transient)
+                       source = find_window(source->transient);
                TAILQ_REMOVE(wl, source, entry);
                if (target == NULL)
                        TAILQ_INSERT_HEAD(wl, source, entry);
@@ -2126,6 +2176,8 @@ swapwin(struct swm_region *r, union arg *args)
                return;
        }
 
+       sort_windows(wl);
+
        stack();
 }
 
@@ -2186,7 +2238,7 @@ done:
        if (winfocus == winlostfocus || winfocus == NULL)
                return;
 
-       focus_magic(winfocus, SWM_F_GENERIC);
+       focus_magic(winfocus);
 }
 
 void
@@ -2213,7 +2265,7 @@ focus(struct swm_region *r, union arg *args)
                                if (winfocus->iconic == 0)
                                        break;
 
-               focus_magic(winfocus, SWM_F_GENERIC);
+               focus_magic(winfocus);
                return;
        }
 
@@ -2230,18 +2282,18 @@ focus(struct swm_region *r, union arg *args)
                if (head == NULL)
                        head = TAILQ_LAST(wl, ws_win_list);
                winfocus = head;
-               for (;;) {
-                       if (winfocus == NULL)
-                               break;
-                       if (!winfocus->iconic)
-                               break;
-                       winfocus = TAILQ_PREV(winfocus, ws_win_list, entry);
-                       if (winfocus == NULL)
-                               winfocus = TAILQ_LAST(wl, ws_win_list);
-                       if (winfocus == head) {
-                               winfocus = NULL;
-                               break;
-                       }
+               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;
+               }
+
+               /* skip iconics */
+               if (winfocus && winfocus->iconic) {
+                       TAILQ_FOREACH_REVERSE(winfocus, wl, ws_win_list, entry)
+                               if (winfocus->iconic == 0)
+                                       break;
                }
                break;
 
@@ -2250,18 +2302,12 @@ focus(struct swm_region *r, union arg *args)
                if (head == NULL)
                        head = TAILQ_FIRST(wl);
                winfocus = head;
-               for (;;) {
-                       if (winfocus == NULL)
-                               break;
-                       if (!winfocus->iconic)
-                               break;
-                       winfocus = TAILQ_NEXT(winfocus, entry);
-                       if (winfocus == NULL)
-                               winfocus = TAILQ_FIRST(wl);
-                       if (winfocus == head) {
-                               winfocus = NULL;
-                               break;
-                       }
+
+               /* skip iconics */
+               if (winfocus && winfocus->iconic) {
+                       TAILQ_FOREACH(winfocus, wl, entry)
+                               if (winfocus->iconic == 0)
+                                       break;
                }
                break;
 
@@ -2274,11 +2320,10 @@ focus(struct swm_region *r, union arg *args)
        default:
                return;
        }
-
        if (winfocus == winlostfocus || winfocus == NULL)
                return;
 
-       focus_magic(winfocus, SWM_F_GENERIC);
+       focus_magic(winfocus);
 }
 
 void
@@ -2347,6 +2392,9 @@ stack(void) {
        }
        if (font_adjusted)
                font_adjusted--;
+
+       if (focus_mode == SWM_FOCUS_DEFAULT)
+               drain_enter_notify();
 }
 
 void
@@ -2411,22 +2459,22 @@ stack_floater(struct ws_win *win, struct swm_region *r)
                 * floaters and transients are auto-centred unless moved
                 * or resized
                 */
-               win->g.x = r->g.x + (WIDTH(r) - win->g.w) / 2 - border_width;
-               win->g.y = r->g.y + (HEIGHT(r) - win->g.h) / 2 - border_width;
+               win->g.x = r->g.x + (WIDTH(r) - win->g.w) / 2 - wc.border_width;
+               win->g.y = r->g.y + (HEIGHT(r) - win->g.h) / 2 - wc.border_width;
        }
 
        /* win can be outside r if new r smaller than old r */
        /* Ensure top left corner inside r (move probs otherwise) */
-       if (win->g.x < r->g.x - border_width)
-               win->g.x = r->g.x - border_width;
+       if (win->g.x < r->g.x - wc.border_width)
+               win->g.x = r->g.x - wc.border_width;
        if (win->g.x > r->g.x + r->g.w - 1)
                win->g.x = (win->g.w > r->g.w) ? r->g.x :
-                   (r->g.x + r->g.w - win->g.w - 2 * border_width);
-       if (win->g.y < r->g.y - border_width)
-               win->g.y = r->g.y - border_width;
+                   (r->g.x + r->g.w - win->g.w - 2 * wc.border_width);
+       if (win->g.y < r->g.y - wc.border_width)
+               win->g.y = r->g.y - wc.border_width;
        if (win->g.y > r->g.y + r->g.h - 1)
                win->g.y = (win->g.h > r->g.h) ? r->g.y :
-                   (r->g.y + r->g.h - win->g.h - 2 * border_width);
+                   (r->g.y + r->g.h - win->g.h - 2 * wc.border_width);
 
        wc.x = win->g.x;
        wc.y = win->g.y;
@@ -2659,14 +2707,13 @@ stack_master(struct workspace *ws, struct swm_geometry *g, int rot, int flip)
                j++;
        }
 
- notiles:
+notiles:
        /* now, stack all the floaters and transients */
        TAILQ_FOREACH(win, &ws->winlist, entry) {
                if (win->transient == 0 && win->floating == 0)
                        continue;
-               if (win->iconic == 0)
+               if (win->iconic == 1)
                        continue;
-
                if (win->ewmh_flags & EWMH_F_FULLSCREEN) {
                        fs_win = win;
                        continue;
@@ -2840,7 +2887,7 @@ max_stack(struct workspace *ws, struct swm_geometry *g)
                if (parent)
                        XMapRaised(display, parent->id);
                stack_floater(wintrans, ws->r);
-               focus_magic(wintrans, SWM_F_TRANSIENT);
+               focus_magic(wintrans);
        }
 }
 
@@ -2848,7 +2895,7 @@ void
 send_to_ws(struct swm_region *r, union arg *args)
 {
        int                     wsid = args->id;
-       struct ws_win           *win = win;
+       struct ws_win           *win = NULL, *parent;
        struct workspace        *ws, *nws;
        Atom                    ws_idx_atom = 0;
        unsigned char           ws_idx_str[SWM_PROPLEN];
@@ -2870,6 +2917,15 @@ send_to_ws(struct swm_region *r, union arg *args)
 
        a.id = SWM_ARG_ID_FOCUSPREV;
        focus(r, &a);
+       if (win->transient) {
+               parent = find_window(win->transient);
+               if (parent) {
+                       unmap_window(parent);
+                       TAILQ_REMOVE(&ws->winlist, parent, entry);
+                       TAILQ_INSERT_TAIL(&nws->winlist, parent, entry);
+                       parent->ws = nws;
+               }
+       }
        unmap_window(win);
        TAILQ_REMOVE(&ws->winlist, win, entry);
        TAILQ_INSERT_TAIL(&nws->winlist, win, entry);
@@ -3221,7 +3277,7 @@ resize(struct ws_win *win, union arg *args)
        XUngrabPointer(display, CurrentTime);
 
        /* drain events */
-       while (XCheckMaskEvent(display, EnterWindowMask, &ev));
+       drain_enter_notify();
 }
 
 void
@@ -3315,7 +3371,7 @@ move(struct ws_win *win, union arg *args)
        XUngrabPointer(display, CurrentTime);
 
        /* drain events */
-       while (XCheckMaskEvent(display, EnterWindowMask, &ev));
+       drain_enter_notify();
 }
 
 /* user/key callable function IDs */
@@ -4265,6 +4321,7 @@ setup_quirks(void)
        setquirk("Xitk",                "Xine Window",  SWM_Q_FLOAT | SWM_Q_ANYWHERE);
        setquirk("xine",                "xine Video Fullscreen Window", SWM_Q_FULLSCREEN | SWM_Q_FLOAT);
        setquirk("pcb",                 "pcb",          SWM_Q_FLOAT);
+       setquirk("SDL_App",             "SDL_App",      SWM_Q_FLOAT | SWM_Q_FULLSCREEN);
 }
 
 /* conf file stuff */
@@ -4542,13 +4599,52 @@ conf_load(char *filename)
 }
 
 void
-set_child_transient(struct ws_win *win)
+set_child_transient(struct ws_win *win, Window *trans)
 {
-       struct ws_win           *parent;
+       struct ws_win           *parent, *w;
+       XWMHints                *wmh = NULL;
+       struct swm_region       *r;
+       struct workspace        *ws;
 
        parent = find_window(win->transient);
        if (parent)
                parent->child_trans = win;
+       else {
+               DNPRINTF(SWM_D_MISC, "set_child_transient: parent doesn't exist"
+                   " for %lu trans %lu\n", win->id, win->transient);
+
+               if (win->hints == NULL) {
+                       fprintf(stderr, "no hints for %lu\n", win->id);
+                       return;
+               }
+
+               r = root_to_region(win->wa.root);
+               ws = r->ws;
+               /* parent doen't exist in our window list */
+               TAILQ_FOREACH(w, &ws->winlist, entry) {
+                       if (wmh)
+                               XFree(wmh);
+
+                       if ((wmh = XGetWMHints(display, w->id)) == NULL) {
+                               fprintf(stderr, "can't get hints for %lu\n",
+                                   w->id);
+                               continue;
+                       }
+
+                       if (win->hints->window_group != wmh->window_group)
+                               continue;
+
+                       w->child_trans = win;
+                       win->transient = w->id;
+                       *trans = w->id;
+                       DNPRINTF(SWM_D_MISC, "set_child_transient: asjusting "
+                           "transient to %lu\n", win->transient);
+                       break;
+               }
+       }
+
+       if (wmh)
+               XFree(wmh);
 }
 
 struct ws_win *
@@ -4575,9 +4671,12 @@ manage_window(Window id)
                DNPRINTF(SWM_D_MISC, "manage previously unmanaged window "
                    "%lu\n", win->id);
                TAILQ_REMOVE(&win->ws->unmanagedlist, win, entry);
-               TAILQ_INSERT_TAIL(&win->ws->winlist, win, entry);
-               if (win->transient)
-                       set_child_transient(win);
+               if (win->transient) {
+                       set_child_transient(win, &trans);
+               } if (trans && (ww = find_window(trans)))
+                       TAILQ_INSERT_AFTER(&win->ws->winlist, ww, win, entry);
+               else
+                       TAILQ_INSERT_TAIL(&win->ws->winlist, win, entry);
                ewmh_update_actions(win);
                return (win);
        }
@@ -4594,10 +4693,11 @@ manage_window(Window id)
                    False, XA_STRING, &type, &format, &nitems, &bytes, &prop);
        XGetWindowAttributes(display, id, &win->wa);
        XGetWMNormalHints(display, id, &win->sh, &mask);
+       win->hints = XGetWMHints(display, id);
        XGetTransientForHint(display, id, &trans);
        if (trans) {
                win->transient = trans;
-               set_child_transient(win);
+               set_child_transient(win, &trans);
                DNPRINTF(SWM_D_MISC, "manage_window: win %lu transient %lu\n",
                    win->id, win->transient);
        }
@@ -4650,7 +4750,10 @@ manage_window(Window id)
        win->id = id;
        win->ws = ws;
        win->s = r->s;  /* this never changes */
-       TAILQ_INSERT_TAIL(&ws->winlist, win, entry);
+       if (trans && (ww = find_window(trans)))
+               TAILQ_INSERT_AFTER(&ws->winlist, ww, win, entry);
+       else
+               TAILQ_INSERT_TAIL(&ws->winlist, win, entry);
 
        win->g.w = win->wa.width;
        win->g.h = win->wa.height;
@@ -4735,10 +4838,6 @@ manage_window(Window id)
 
        XSelectInput(display, id, EnterWindowMask | FocusChangeMask |
            PropertyChangeMask | StructureNotifyMask);
-       if (win->iconic)
-               set_win_state(win, IconicState);
-       else
-               set_win_state(win, NormalState);
 
        /* floaters need to be mapped if they are in the current workspace */
        if ((win->floating || win->transient) && (ws->idx == r->ws->idx))
@@ -4792,8 +4891,7 @@ unmanage_window(struct ws_win *win)
        /* focus on root just in case */
        XSetInputFocus(display, PointerRoot, PointerRoot, CurrentTime);
 
-       if (!win->floating)
-               focus_prev(win);
+       focus_prev(win);
 
        TAILQ_REMOVE(&win->ws->winlist, win, entry);
        TAILQ_INSERT_TAIL(&win->ws->unmanagedlist, win, entry);
@@ -4802,14 +4900,14 @@ unmanage_window(struct ws_win *win)
 }
 
 void
-focus_magic(struct ws_win *win, int do_trans)
+focus_magic(struct ws_win *win)
 {
-       DNPRINTF(SWM_D_FOCUS, "focus_magic: %lu %d\n", WINID(win), do_trans);
+       DNPRINTF(SWM_D_FOCUS, "focus_magic: %lu\n", WINID(win));
 
        if (win == NULL)
                return;
 
-       if (do_trans == SWM_F_TRANSIENT && win->child_trans) {
+       if (win->child_trans) {
                /* win = parent & has a transient so focus on that */
                if (win->java) {
                        focus_win(win->child_trans);
@@ -4883,7 +4981,7 @@ buttonpress(XEvent *e)
        if ((win = find_window(ev->window)) == NULL)
                return;
 
-       focus_magic(win, SWM_F_TRANSIENT);
+       focus_magic(win);
        action = client_click;
 
        for (i = 0; i < LENGTH(buttons); i++)
@@ -4983,11 +5081,6 @@ enternotify(XEvent *e)
 
        switch (focus_mode) {
        case SWM_FOCUS_DEFAULT:
-               if (QLength(display)) {
-                       DNPRINTF(SWM_D_EVENT, "ignore enternotify %d\n",
-                           QLength(display));
-                       return;
-               }
                break;
        case SWM_FOCUS_FOLLOW:
                break;
@@ -5078,7 +5171,7 @@ enternotify(XEvent *e)
                return;
        }
 
-       focus_magic(win, SWM_F_TRANSIENT);
+       focus_magic(win);
 }
 
 /* lets us use one switch statement for arbitrary mode/detail combinations */
@@ -5176,7 +5269,7 @@ maprequest(XEvent *e)
        /* make new win focused */
        r = root_to_region(win->wa.root);
        if (win->ws == r->ws)
-               focus_magic(win, SWM_F_GENERIC);
+               focus_magic(win);
 }
 
 void
@@ -5237,6 +5330,21 @@ unmapnotify(XEvent *e)
        if (getstate(e->xunmap.window) == NormalState) {
                unmanage_window(win);
                stack();
+
+               /* giant hack for apps that don't destroy transient windows */
+               /* eat a bunch of events to prevent remanaging the window */
+               XEvent                  cne;
+               while (XCheckWindowEvent(display, e->xunmap.window,
+                   EnterWindowMask, &cne))
+                       ;
+               while (XCheckWindowEvent(display, e->xunmap.window,
+                   StructureNotifyMask, &cne))
+                       ;
+               while (XCheckWindowEvent(display, e->xunmap.window,
+                   SubstructureNotifyMask, &cne))
+                       ;
+               /* resend unmap because we ated it */
+               XUnmapWindow(display, e->xunmap.window);
        }
 }