JasonWoof Got questions, comments, patches, etc.? Contact Jason Woofenden
Unfuck java by working around several java issues.
[spectrwm.git] / scrotwm.c
index 2770962..3f1566a 100644 (file)
--- a/scrotwm.c
+++ b/scrotwm.c
@@ -52,7 +52,7 @@
 
 static const char      *cvstag = "$scrotwm$";
 
-#define        SWM_VERSION     "0.9.9"
+#define        SWM_VERSION     "1.0"
 
 #include <stdio.h>
 #include <stdlib.h>
@@ -96,8 +96,8 @@ static const char     *cvstag = "$scrotwm$";
 
 /* #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)
+#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)
 #define        SWM_D_MISC              0x0001
 #define        SWM_D_EVENT             0x0002
 #define        SWM_D_WS                0x0004
@@ -157,6 +157,7 @@ char                        **start_argv;
 Atom                   astate;
 Atom                   aprot;
 Atom                   adelete;
+Atom                   takefocus;
 volatile sig_atomic_t   running = 1;
 int                    outputs = 0;
 int                    (*xerrorxlib)(Display *, XErrorEvent *);
@@ -190,6 +191,7 @@ int                 bar_extra = 1;
 int                    bar_extra_running = 0;
 int                    bar_verbose = 1;
 int                    bar_height = 0;
+int                    stack_enabled = 1;
 int                    clock_enabled = 1;
 int                    title_name_enabled = 0;
 int                    title_class_enabled = 0;
@@ -231,14 +233,17 @@ TAILQ_HEAD(swm_region_list, swm_region);
 struct ws_win {
        TAILQ_ENTRY(ws_win)     entry;
        Window                  id;
+       Window                  transient;
+       struct ws_win           *child_trans;   /* transient child window */
        struct swm_geometry     g;
        int                     floating;
-       int                     transient;
        int                     manual;
        int                     font_size_boundary[SWM_MAX_FONT_STEPS];
        int                     font_steps;
        int                     last_inc;
        int                     can_delete;
+       int                     take_focus;
+       int                     java;
        unsigned long           quirks;
        struct workspace        *ws;    /* always valid */
        struct swm_screen       *s;     /* always valid, never changes */
@@ -315,16 +320,22 @@ void      max_stack(struct workspace *, struct swm_geometry *);
 
 void   grabbuttons(struct ws_win *, int);
 void   new_region(struct swm_screen *, int, int, int, int);
+void   unmanage_window(struct ws_win *);
 
 struct layout {
        void            (*l_stack)(struct workspace *, struct swm_geometry *);
        void            (*l_config)(struct workspace *, int);
+       u_int32_t       flags;
+#define SWM_L_FOCUSPREV                (1<<0)
+#define SWM_L_MAPONFOCUS       (1<<1)
+       char            *name;
 } layouts[] =  {
        /* stack,               configure */
-       { vertical_stack,       vertical_config},
-       { horizontal_stack,     horizontal_config},
-       { max_stack,            NULL},
-       { NULL,                 NULL},
+       { vertical_stack,       vertical_config,        0,      "[|]" },
+       { horizontal_stack,     horizontal_config,      0,      "[-]" },
+       { max_stack,            NULL,
+         SWM_L_FOCUSPREV | SWM_L_MAPONFOCUS,                   "[ ]"},
+       { NULL,                 NULL,                   0},
 };
 
 #define SWM_H_SLICE            (32)
@@ -338,6 +349,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,6 +559,7 @@ void                        destroynotify(XEvent *);
 void                   enternotify(XEvent *);
 void                   focusin(XEvent *);
 void                   focusout(XEvent *);
+void                   mapnotify(XEvent *);
 void                   mappingnotify(XEvent *);
 void                   maprequest(XEvent *);
 void                   propertynotify(XEvent *);
@@ -563,6 +576,7 @@ void                        (*handler[LASTEvent])(XEvent *) = {
                                [EnterNotify] = enternotify,
                                [FocusIn] = focusin,
                                [FocusOut] = focusout,
+                               [MapNotify] = mapnotify,
                                [MappingNotify] = mappingnotify,
                                [MapRequest] = maprequest,
                                [PropertyNotify] = propertynotify,
@@ -578,7 +592,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;
                }
@@ -752,6 +766,7 @@ bar_update(void)
        char                    s[SWM_BAR_MAX];
        char                    loc[SWM_BAR_MAX];
        char                    *b;
+       char                    *stack = "";
 
        if (bar_enabled == 0)
                return;
@@ -784,12 +799,15 @@ bar_update(void)
                        if (r && r->ws)
                                bar_class_name(s, sizeof s, r->ws->focus);
 
-                       snprintf(loc, sizeof loc, "%d:%d    %s %s    %s",
-                           x++, r->ws->idx + 1, s, bar_ext, bar_vertext);
+                       if (stack_enabled)
+                               stack = r->ws->cur_layout->name;
+
+                       snprintf(loc, sizeof loc, "%d:%d %s   %s %s    %s",
+                           x++, r->ws->idx + 1, stack, s, bar_ext,
+                           bar_vertext);
                        bar_print(r, loc);
                }
        }
-       XSync(display, False);
        alarm(bar_delay);
 }
 
@@ -907,6 +925,61 @@ bar_setup(struct swm_region *r)
        bar_refresh();
 }
 
+Bool
+set_win_notify_cb(Display *d, XEvent *e, char *arg)
+{
+       struct ws_win           *win = (struct ws_win *)arg;
+
+       if (win && win->id == e->xany.window && e->xany.type == PropertyNotify)
+                       return (True);
+       return (False);
+}
+
+void
+set_win_state(struct ws_win *win, long state)
+{
+       long                    data[] = {state, None};
+       XEvent                  ev;
+       XWindowAttributes       wa;
+
+       DNPRINTF(SWM_D_EVENT, "set_win_state: window: %lu\n", win->id);
+
+       if (win == NULL)
+               return;
+       /* make sure we drain everything */
+       XSync(display, True);
+
+       /* make sure we still exist too */
+       if (XGetWindowAttributes(display, win->id, &wa) == BadWindow)
+               return;
+
+       XChangeProperty(display, win->id, astate, astate, 32, PropModeReplace,
+           (unsigned char *)data, 2);
+
+       /* wait for completion of XChangeProperty */
+       while (XCheckIfEvent(display, &ev, set_win_notify_cb, (char *)win))
+               ;
+}
+
+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)
 {
@@ -960,6 +1033,21 @@ count_win(struct workspace *ws, int count_transient)
 {
        struct ws_win           *win;
        int                     count = 0;
+       int                     state;
+
+       /*
+        * Under stress conditions windows sometimes do not get removed from
+        * the managed list quickly enough.  Use a very large hammer to get rid
+        * of them.  A smaller hammer would be nice.
+        */
+       TAILQ_FOREACH(win, &ws->winlist, entry) {
+               state = getstate(win->id);
+               if (state == -1) {
+                       DNPRINTF(SWM_D_MISC, "count_win:removing: %lu\n",
+                           win->id);
+                       unmanage_window(win);
+               }
+       }
 
        TAILQ_FOREACH(win, &ws->winlist, entry) {
                if (count_transient == 0 && win->floating)
@@ -980,6 +1068,44 @@ quit(struct swm_region *r, union arg *args)
        running = 0;
 }
 
+Bool
+unmap_window_cb(Display *d, XEvent *e, char *arg)
+{
+       struct ws_win           *win = (struct ws_win *)arg;
+
+       if (win && win->id == e->xany.window && e->xany.type == UnmapNotify)
+                       return (True);
+       return (False);
+}
+
+void
+unmap_window(struct ws_win *win)
+{
+       XEvent                  ev;
+       XWindowAttributes       wa;
+
+       if (win == NULL)
+               return;
+
+       /* make sure we still exist too */
+       if (XGetWindowAttributes(display, win->id, &wa) == BadWindow)
+               return;
+
+       /* don't unmap again */
+       if (wa.map_state == IsUnmapped && getstate(win->id) == IconicState)
+               return;
+
+       /* java shits itself when windows are set to iconic state */
+       if (win->java == 0)
+               set_win_state(win, IconicState);
+
+       XUnmapWindow(display, win->id);
+
+       /* make sure we wait for XUnmapWindow completion */
+       while (XCheckIfEvent(display, &ev, unmap_window_cb, (char *)win))
+               ;
+}
+
 void
 unmap_all(void)
 {
@@ -989,7 +1115,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
@@ -1118,7 +1244,6 @@ spawn(struct swm_region *r, union arg *args)
                }
                exit(0);
        }
-       wait(0);
 }
 
 void
@@ -1140,14 +1265,14 @@ unfocus_win(struct ws_win *win)
        if (win->ws->r == NULL)
                return;
 
-       grabbuttons(win, 0);
-       XSetWindowBorder(display, win->id,
-           win->ws->r->s->c[SWM_S_COLOR_UNFOCUS].color);
-
        if (win->ws->focus == win) {
                win->ws->focus = NULL;
                win->ws->focus_prev = win;
        }
+
+       grabbuttons(win, 0);
+       XSetWindowBorder(display, win->id,
+           win->ws->r->s->c[SWM_S_COLOR_UNFOCUS].color);
 }
 
 void
@@ -1162,7 +1287,6 @@ unfocus_all(void)
                for (j = 0; j < SWM_WS_MAX; j++)
                        TAILQ_FOREACH(win, &screens[i].ws[j].winlist, entry)
                                unfocus_win(win);
-       XSync(display, False);
 }
 
 void
@@ -1181,9 +1305,11 @@ focus_win(struct ws_win *win)
                XSetWindowBorder(display, win->id,
                    win->ws->r->s->c[SWM_S_COLOR_FOCUS].color);
                grabbuttons(win, 1);
-               XSetInputFocus(display, win->id,
-                   RevertToPointerRoot, CurrentTime);
-               XSync(display, False);
+               if (win->ws->cur_layout->flags & SWM_L_MAPONFOCUS)
+                       XMapRaised(display, win->id);
+               if (win->java == 0)
+                       XSetInputFocus(display, win->id,
+                           RevertToPointerRoot, CurrentTime);
        }
 }
 
@@ -1192,7 +1318,7 @@ switchws(struct swm_region *r, union arg *args)
 {
        int                     wsid = args->id;
        struct swm_region       *this_r, *other_r;
-       struct ws_win           *win, *winfocus = NULL;
+       struct ws_win           *win, *winfocus = NULL, *parent = NULL;
        struct workspace        *new_ws, *old_ws;
 
        this_r = r;
@@ -1217,15 +1343,23 @@ 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)
+                               if (!(win->ws->cur_layout->flags &
+                                   SWM_L_MAPONFOCUS))
+                                       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;
@@ -1235,7 +1369,17 @@ switchws(struct swm_region *r, union arg *args)
 
        ignore_enter = 1;
        stack();
-       focus_win(winfocus);
+       if (winfocus) {
+               /* make sure we see the parent window */
+               if (winfocus->transient) {
+                       parent = find_window(winfocus->transient);
+                       if (parent)
+                               focus_win(parent);
+               }
+
+               focus_win(winfocus);
+       }
+       ignore_enter = 0;
        bar_update();
 }
 
@@ -1280,8 +1424,14 @@ cyclews(struct swm_region *r, union arg *args)
 void
 cyclescr(struct swm_region *r, union arg *args)
 {
-       struct swm_region       *rr;
-       int                     i;
+       struct swm_region       *rr = NULL;
+       struct workspace        *ws = NULL;
+       struct ws_win           *winfocus = NULL;
+       int                     i, x, y;
+
+       /* do nothing if we don't have more than one screen */
+       if (!(ScreenCount(display) > 1 || outputs > 1))
+               return;
 
        i = r->s->idx;
        switch (args->id) {
@@ -1298,10 +1448,28 @@ cyclescr(struct swm_region *r, union arg *args)
        default:
                return;
        };
+       if (rr == NULL)
+               return;
+
+       ws = rr->ws;
+       winfocus = ws->focus;
+       if (winfocus == NULL)
+               winfocus = ws->focus_prev;
+       if (winfocus) {
+               /* use window coordinates */
+               x = winfocus->g.x + 1;
+               y = winfocus->g.y + 1;
+       } else {
+               /* use region coordinates */
+               x = rr->g.x + 1;
+               y = rr->g.y + 1 + bar_enabled ? bar_height : 0;
+       }
+
        unfocus_all();
        XSetInputFocus(display, PointerRoot, RevertToPointerRoot, CurrentTime);
-       XWarpPointer(display, None, rr->s[i].root, 0, 0, 0, 0, rr->g.x,
-           rr->g.y + bar_enabled ? bar_height : 0);
+       XWarpPointer(display, None, rr->s[i].root, 0, 0, 0, 0, x, y);
+
+       focus_win(winfocus);
 }
 
 void
@@ -1410,16 +1578,14 @@ focus(struct swm_region *r, union arg *args)
        if (winfocus == winlostfocus || winfocus == NULL)
                return;
 
-       XMapRaised(display, winfocus->id);
        focus_win(winfocus);
-       XSync(display, False);
 }
 
 void
 cycle_layout(struct swm_region *r, union arg *args)
 {
        struct workspace        *ws = r->ws;
-       struct ws_win           *winfocus;
+       struct ws_win           *winfocus, *parent = NULL;
 
        DNPRINTF(SWM_D_EVENT, "cycle_layout: workspace: %d\n", ws->idx);
 
@@ -1431,7 +1597,15 @@ cycle_layout(struct swm_region *r, union arg *args)
 
        ignore_enter = 1;
        stack();
-       focus_win(winfocus);
+       /* make sure we see the parent window */
+       if (winfocus) {
+               if (winfocus->transient)
+                       parent = find_window(winfocus->transient);
+               if (parent)
+                       focus_win(parent);
+               focus_win(winfocus);
+       }
+       ignore_enter = 0;
 }
 
 void
@@ -1478,7 +1652,6 @@ stack(void) {
        }
        if (font_adjusted)
                font_adjusted--;
-       XSync(display, False);
 }
 
 void
@@ -1487,6 +1660,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,25 +1991,22 @@ max_stack(struct workspace *ws, struct swm_geometry *g)
 {
        XWindowChanges          wc;
        struct swm_geometry     gg = *g;
-       struct ws_win           *win;
+       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);
 
+       if (ws == NULL)
+               return;
+
        winno = count_win(ws, 0);
        if (winno == 0 && count_win(ws, 1) == 0)
                return;
 
        TAILQ_FOREACH(win, &ws->winlist, entry) {
-               if (win->transient != 0 || win->floating != 0) {
-                       if (win == ws->focus) {
-                               stack_floater(win, ws->r);
-                               XMapRaised(display, win->id);
-                               focus_win(win); /* override */
-                       }
+               if (win->transient != 0) {
+                       wintrans = win;
                } else {
                        bzero(&wc, sizeof wc);
                        wc.border_width = 1;
@@ -1843,19 +2016,34 @@ 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);
+
+                       /* unmap only if we don't have multi screen */
+                       if (win != ws->focus)
+                               if (!(ScreenCount(display) > 1 || outputs > 1))
+                                       unmap_window(win);
                }
        }
+
+       /* put the last transient on top */
+       if (wintrans) {
+               stack_floater(wintrans, ws->r);
+               focus_win(wintrans); /* override */
+       }
 }
 
 void
 send_to_ws(struct swm_region *r, union arg *args)
 {
        int                     wsid = args->id;
-       struct ws_win           *win = r->ws->focus, *winfocus = NULL;
+       struct ws_win           *win = win, *winfocus = NULL;
        struct workspace        *ws, *nws;
        Atom                    ws_idx_atom = 0;
        unsigned char           ws_idx_str[SWM_PROPLEN];
 
+       if (r && r->ws)
+               win = r->ws->focus;
+       else
+               return;
        if (win == NULL)
                return;
 
@@ -1869,18 +2057,17 @@ send_to_ws(struct swm_region *r, union arg *args)
        if (TAILQ_FIRST(&ws->winlist) == win)
                winfocus = TAILQ_NEXT(win, entry);
        else {
-               winfocus = TAILQ_PREV(ws->focus, ws_win_list, entry);
+               winfocus = TAILQ_PREV(win, ws_win_list, entry);
                if (winfocus == NULL)
                        winfocus = TAILQ_LAST(&ws->winlist, ws_win_list);
        }
-       /* out of windows in ws so focus on nws instead */
+       /* out of windows in ws so focus on nws instead if we multi screen */
        if (winfocus == NULL)
-               winfocus = win;
-
-       XUnmapWindow(display, win->id);
+               if (ScreenCount(display) > 1 || outputs > 1)
+                       winfocus = win;
 
+       unmap_window(win);
        TAILQ_REMOVE(&ws->winlist, win, entry);
-
        TAILQ_INSERT_TAIL(&nws->winlist, win, entry);
        win->ws = nws;
 
@@ -1900,7 +2087,8 @@ send_to_ws(struct swm_region *r, union arg *args)
        nws->restack = 1;
 
        stack();
-       focus_win(winfocus);
+       if (winfocus)
+               focus_win(winfocus);
 }
 
 void
@@ -2088,8 +2276,15 @@ move(struct ws_win *win, union arg *args)
 }
 
 /* key definitions */
-void dummykeyfunc(struct swm_region *r, union arg *args) {};
-void legacyfunc(struct swm_region *r, union arg *args) {};
+void
+dummykeyfunc(struct swm_region *r, union arg *args)
+{
+};
+
+void
+legacyfunc(struct swm_region *r, union arg *args)
+{
+};
 
 struct keyfunc {
        char                    name[SWM_FUNCNAME_LEN];
@@ -2477,6 +2672,7 @@ parsekeys(char *keystr, unsigned int currmod, unsigned int *mod, KeySym *ks)
        DNPRINTF(SWM_D_KEY, "parsekeys: leave ok\n");
        return (0);
 }
+
 char *
 strdupsafe(char *str)
 {
@@ -2485,6 +2681,7 @@ strdupsafe(char *str)
        else
                return (strdup(str));
 }
+
 void
 setkeybinding(unsigned int mod, KeySym ks, enum keyfuncid kfid, char *spawn_name)
 {
@@ -2565,6 +2762,7 @@ setkeybinding(unsigned int mod, KeySym ks, enum keyfuncid kfid, char *spawn_name
        }
        DNPRINTF(SWM_D_KEY, "setkeybinding: leave\n");
 }
+
 int
 setconfbinding(char *selector, char *value, int flags)
 {
@@ -2611,6 +2809,7 @@ setconfbinding(char *selector, char *value, int flags)
        DNPRINTF(SWM_D_KEY, "setconfbinding: no match\n");
        return (1);
 }
+
 void
 setup_keys(void)
 {
@@ -2668,6 +2867,7 @@ setup_keys(void)
        setkeybinding(MODKEY|ShiftMask, XK_Delete,      kf_spawn_custom,        "lock");
        setkeybinding(MODKEY|ShiftMask, XK_i,           kf_spawn_custom,        "initscr");
 }
+
 void
 updatenumlockmask(void)
 {
@@ -2735,76 +2935,6 @@ grabbuttons(struct ws_win *win, int focused)
                    BUTTONMASK, GrabModeAsync, GrabModeSync, None, None);
 }
 
-void
-expose(XEvent *e)
-{
-       DNPRINTF(SWM_D_EVENT, "expose: window: %lu\n", e->xexpose.window);
-}
-
-void
-keypress(XEvent *e)
-{
-       unsigned int            i;
-       KeySym                  keysym;
-       XKeyEvent               *ev = &e->xkey;
-
-       DNPRINTF(SWM_D_EVENT, "keypress: window: %lu\n", ev->window);
-
-       keysym = XKeycodeToKeysym(display, (KeyCode)ev->keycode, 0);
-       for (i = 0; i < keys_length; i++)
-               if (keysym == keys[i].keysym
-                  && CLEANMASK(keys[i].mod) == CLEANMASK(ev->state)
-                  && keyfuncs[keys[i].funcid].func) {
-                       if (keys[i].funcid == kf_spawn_custom)
-                               spawn_custom(
-                                   root_to_region(ev->root),
-                                   &(keyfuncs[keys[i].funcid].args),
-                                   keys[i].spawn_name
-                                   );
-                       else
-                               keyfuncs[keys[i].funcid].func(
-                                   root_to_region(ev->root),
-                                   &(keyfuncs[keys[i].funcid].args)
-                                   );
-               }
-}
-
-void
-buttonpress(XEvent *e)
-{
-       XButtonPressedEvent     *ev = &e->xbutton;
-
-       struct ws_win           *win;
-       int                     i, action;
-
-       DNPRINTF(SWM_D_EVENT, "buttonpress: window: %lu\n", ev->window);
-
-       action = root_click;
-       if ((win = find_window(ev->window)) == NULL)
-               return;
-       else {
-               focus_win(win);
-               action = client_click;
-       }
-
-       for (i = 0; i < LENGTH(buttons); i++)
-               if (action == buttons[i].action && buttons[i].func &&
-                   buttons[i].button == ev->button &&
-                   CLEANMASK(buttons[i].mask) == CLEANMASK(ev->state))
-                       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",
@@ -2821,8 +2951,10 @@ parsequirks(char *qstr, unsigned long *quirk)
 {
        char                    *cp, *name;
        int                     i;
+
        if (quirk == NULL)
                return (1);
+
        cp = qstr;
        *quirk = 0;
        while ((name = strsep(&cp, SWM_Q_WS)) != NULL) {
@@ -2847,10 +2979,12 @@ parsequirks(char *qstr, unsigned long *quirk)
        }
        return (0);
 }
+
 void
 setquirk(const char *class, const char *name, const int quirk)
 {
        int                     i, j;
+
        /* find existing */
        for (i = 0; i < quirks_length; i++) {
                if (!strcmp(quirks[i].class, class) &&
@@ -2920,6 +3054,7 @@ setquirk(const char *class, const char *name, const int quirk)
                }
        }
 }
+
 int
 setconfquirk(char *selector, char *value, int flags)
 {
@@ -2959,11 +3094,11 @@ setup_quirks(void)
 /* conf file stuff */
 #define SWM_CONF_FILE  "scrotwm.conf"
 
-enum   { SWM_S_BAR_DELAY, SWM_S_BAR_ENABLED, SWM_S_CLOCK_ENABLED,
-         SWM_S_CYCLE_EMPTY, SWM_S_CYCLE_VISIBLE, SWM_S_SS_ENABLED,
-         SWM_S_TERM_WIDTH, SWM_S_TITLE_CLASS_ENABLED, SWM_S_TITLE_NAME_ENABLED,
-         SWM_S_BAR_FONT, SWM_S_BAR_ACTION, SWM_S_SPAWN_TERM, SWM_S_SS_APP,
-         SWM_S_DIALOG_RATIO };
+enum   { SWM_S_BAR_DELAY, SWM_S_BAR_ENABLED, SWM_S_STACK_ENABLED,
+         SWM_S_CLOCK_ENABLED, SWM_S_CYCLE_EMPTY, SWM_S_CYCLE_VISIBLE,
+         SWM_S_SS_ENABLED, SWM_S_TERM_WIDTH, SWM_S_TITLE_CLASS_ENABLED,
+         SWM_S_TITLE_NAME_ENABLED, SWM_S_BAR_FONT, SWM_S_BAR_ACTION,
+         SWM_S_SPAWN_TERM, SWM_S_SS_APP, SWM_S_DIALOG_RATIO };
 
 int
 setconfvalue(char *selector, char *value, int flags)
@@ -2975,6 +3110,9 @@ setconfvalue(char *selector, char *value, int flags)
        case SWM_S_BAR_ENABLED:
                bar_enabled = atoi(value);
                break;
+       case SWM_S_STACK_ENABLED:
+               stack_enabled = atoi(value);
+               break;
        case SWM_S_CLOCK_ENABLED:
                clock_enabled = atoi(value);
                break;
@@ -3069,6 +3207,7 @@ struct config_option configopt[] = {
        { "bar_action",                 setconfvalue,   SWM_S_BAR_ACTION },
        { "bar_delay",                  setconfvalue,   SWM_S_BAR_DELAY },
        { "bind",                       setconfbinding, 0 },
+       { "stack_enabled",              setconfvalue,   SWM_S_STACK_ENABLED },
        { "clock_enabled",              setconfvalue,   SWM_S_CLOCK_ENABLED },
        { "color_focus",                setconfcolor,   SWM_S_COLOR_FOCUS },
        { "color_unfocus",              setconfcolor,   SWM_S_COLOR_UNFOCUS },
@@ -3187,9 +3326,9 @@ conf_load(char *filename)
 struct ws_win *
 manage_window(Window id)
 {
-       Window                  trans;
+       Window                  trans = 0;
        struct workspace        *ws;
-       struct ws_win           *win, *ww;
+       struct ws_win           *win, *ww, *parent;
        int                     format, i, ws_idx, n, border_me = 0;
        unsigned long           nitems, bytes;
        Atom                    ws_idx_atom = 0, type;
@@ -3212,18 +3351,24 @@ 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;
+               parent = find_window(win->transient);
+               if (parent)
+                       parent->child_trans = win;
                DNPRINTF(SWM_D_MISC, "manage_window: win %u transient %u\n",
                    (unsigned)win->id, win->transient);
        }
        /* get supported protocols */
        if (XGetWMProtocols(display, id, &prot, &n)) {
-               for (i = 0, pp = prot; i < n; i++, pp++)
+               for (i = 0, pp = prot; i < n; i++, pp++) {
+                       if (*pp == takefocus)
+                               win->take_focus = 1;
                        if (*pp == adelete)
                                win->can_delete = 1;
+               }
                if (prot)
                        XFree(prot);
        }
@@ -3245,20 +3390,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;
@@ -3283,6 +3427,11 @@ manage_window(Window id)
        if (XGetClassHint(display, win->id, &win->ch)) {
                DNPRINTF(SWM_D_CLASS, "class: %s name: %s\n",
                    win->ch.res_class, win->ch.res_name);
+
+               /* java is retarded so treat it special */
+               if (strstr(win->ch.res_name, "sun-awt"))
+                       win->java = 1;
+
                for (i = 0; i < quirks_length; i++){
                        if (!strcmp(win->ch.res_class, quirks[i].class) &&
                            !strcmp(win->ch.res_name, quirks[i].name)) {
@@ -3295,6 +3444,8 @@ manage_window(Window id)
                }
        }
 
+       if (win->java && win->transient) {
+       }
        /* alter window position if quirky */
        if (win->quirks & SWM_Q_ANYWHERE) {
                win->manual = 1; /* don't center the quirky windows */
@@ -3305,7 +3456,7 @@ 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;
                }
                border_me = 1;
@@ -3343,15 +3494,21 @@ void
 unmanage_window(struct ws_win *win)
 {
        struct workspace        *ws;
+       struct ws_win           *parent;
 
        if (win == NULL)
                return;
 
        DNPRINTF(SWM_D_MISC, "unmanage_window:  %lu\n", win->id);
 
+       if (win->transient) {
+               parent = find_window(win->transient);
+               if (parent)
+                       parent->child_trans = NULL;
+       }
+
        ws = win->ws;
        TAILQ_REMOVE(&win->ws->winlist, win, entry);
-       set_win_state(win, WithdrawnState);
        if (win->ch.res_class)
                XFree(win->ch.res_class);
        if (win->ch.res_name)
@@ -3360,6 +3517,86 @@ unmanage_window(struct ws_win *win)
 }
 
 void
+focus_magic(struct ws_win *win)
+{
+       if (win->child_trans) {
+               /* win = parent & has a transient so focus on that */
+               if (win->java) {
+                       focus_win(win->child_trans);
+                       if (win->child_trans->take_focus)
+                               client_msg(win, takefocus);
+               } else {
+                       focus_win(win->child_trans);
+                       if (win->child_trans->take_focus)
+                               client_msg(win->child_trans, takefocus);
+               }
+       } else {
+               /* regular focus */
+               focus_win(win);
+               if (win->take_focus)
+                       client_msg(win, takefocus);
+       }
+}
+
+void
+expose(XEvent *e)
+{
+       DNPRINTF(SWM_D_EVENT, "expose: window: %lu\n", e->xexpose.window);
+}
+
+void
+keypress(XEvent *e)
+{
+       unsigned int            i;
+       KeySym                  keysym;
+       XKeyEvent               *ev = &e->xkey;
+
+       DNPRINTF(SWM_D_EVENT, "keypress: window: %lu\n", ev->window);
+
+       keysym = XKeycodeToKeysym(display, (KeyCode)ev->keycode, 0);
+       for (i = 0; i < keys_length; i++)
+               if (keysym == keys[i].keysym
+                  && CLEANMASK(keys[i].mod) == CLEANMASK(ev->state)
+                  && keyfuncs[keys[i].funcid].func) {
+                       if (keys[i].funcid == kf_spawn_custom)
+                               spawn_custom(
+                                   root_to_region(ev->root),
+                                   &(keyfuncs[keys[i].funcid].args),
+                                   keys[i].spawn_name
+                                   );
+                       else
+                               keyfuncs[keys[i].funcid].func(
+                                   root_to_region(ev->root),
+                                   &(keyfuncs[keys[i].funcid].args)
+                                   );
+               }
+}
+
+void
+buttonpress(XEvent *e)
+{
+       XButtonPressedEvent     *ev = &e->xbutton;
+
+       struct ws_win           *win;
+       int                     i, action;
+
+       DNPRINTF(SWM_D_EVENT, "buttonpress: window: %lu\n", ev->window);
+
+       action = root_click;
+       if ((win = find_window(ev->window)) == NULL)
+               return;
+
+       focus_magic(win);
+       action = client_click;
+
+       for (i = 0; i < LENGTH(buttons); i++)
+               if (action == buttons[i].action && buttons[i].func &&
+                   buttons[i].button == ev->button &&
+                   CLEANMASK(buttons[i].mask) == CLEANMASK(ev->state))
+                       buttons[i].func(win, &buttons[i].args);
+}
+
+void
 configurerequest(XEvent *e)
 {
        XConfigureRequestEvent  *ev = &e->xconfigurerequest;
@@ -3444,7 +3681,6 @@ destroynotify(XEvent *e)
        struct ws_win           *win, *winfocus = NULL;
        struct workspace        *ws;
        struct ws_win_list      *wl;
-
        XDestroyWindowEvent     *ev = &e->xdestroywindow;
 
        DNPRINTF(SWM_D_EVENT, "destroynotify: window %lu\n", ev->window);
@@ -3453,20 +3689,39 @@ destroynotify(XEvent *e)
                /* find a window to focus */
                ws = win->ws;
                wl = &ws->winlist;
-               if (ws->focus == win) {
-                       if (TAILQ_FIRST(wl) == win)
-                               winfocus = TAILQ_NEXT(win, entry);
-                       else {
-                               winfocus = TAILQ_PREV(ws->focus, ws_win_list, entry);
-                               if (winfocus == NULL)
-                                       winfocus = TAILQ_LAST(wl, ws_win_list);
+
+               /* if we are transient give focus to parent */
+               if (win->transient)
+                       winfocus = find_window(win->transient);
+               else if (ws->focus == win) {
+                       /* if in max_stack try harder */
+                       if (ws->cur_layout->flags & SWM_L_FOCUSPREV) {
+                               if (win != ws->focus_prev)
+                                       winfocus = ws->focus_prev;
+                               else if (win != ws->focus)
+                                       winfocus = ws->focus;
                        }
-               }
 
+                       /* fallback and normal handling */
+                       if (winfocus == NULL) {
+                               if (TAILQ_FIRST(wl) == win)
+                                       winfocus = TAILQ_NEXT(win, entry);
+                               else {
+                                       winfocus = TAILQ_PREV(ws->focus,
+                                           ws_win_list, entry);
+                                       if (winfocus == NULL)
+                                               winfocus = TAILQ_LAST(wl,
+                                                   ws_win_list);
+                               }
+                       }
+               }
                unmanage_window(win);
+
+               ignore_enter = 1;
                stack();
                if (winfocus)
                        focus_win(winfocus);
+               ignore_enter = 0;
        }
 }
 
@@ -3480,7 +3735,7 @@ enternotify(XEvent *e)
 
        if (ignore_enter) {
                /* eat event(r) to prevent autofocus */
-               ignore_enter--;
+               ignore_enter = 0;
                return;
        }
        /*
@@ -3490,8 +3745,10 @@ enternotify(XEvent *e)
        if (QLength(display))
                return;
 
-       if ((win = find_window(ev->window)) != NULL)
-               focus_win(win);
+       if ((win = find_window(ev->window)) == NULL)
+               return;
+
+       focus_magic(win);
 }
 
 void
@@ -3507,12 +3764,23 @@ focusout(XEvent *e)
 }
 
 void
+mapnotify(XEvent *e)
+{
+       struct ws_win           *win;
+       XMapEvent               *ev = &e->xmap;
+
+       DNPRINTF(SWM_D_EVENT, "mapnotify: window: %lu\n", ev->window);
+
+       win = find_window(ev->window);
+       if (win)
+               set_win_state(win, NormalState);
+}
+
+void
 mappingnotify(XEvent *e)
 {
        XMappingEvent           *ev = &e->xmapping;
 
-       DNPRINTF(SWM_D_EVENT, "mappingnotify: window: %lu\n", ev->window);
-
        XRefreshKeyboardMapping(ev);
        if (ev->request == MappingKeyboard)
                grabkeys();
@@ -3535,12 +3803,15 @@ maprequest(XEvent *e)
        if (wa.override_redirect)
                return;
 
-       manage_window(e->xmaprequest.window);
+       win = manage_window(e->xmaprequest.window);
+       if (win == NULL)
+               return; /* can't happen */
 
+       ignore_enter = 1;
        stack();
+       ignore_enter = 0;
 
        /* make new win focused */
-       win = find_window(ev->window);
        r = root_to_region(win->wa.root);
        if (win->ws == r->ws)
                focus_win(win);
@@ -3584,7 +3855,58 @@ propertynotify(XEvent *e)
 void
 unmapnotify(XEvent *e)
 {
+       struct ws_win           *win, *winfocus = NULL;
+       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;
+
+       /* java can not deal with this heuristic */
+       if (win->java)
+               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
+                */
+               ws = win->ws;
+               /* if we are max_stack try harder to focus on something */
+               if (ws->cur_layout->flags & SWM_L_FOCUSPREV) {
+                       if (win->transient)
+                               winfocus = find_window(win->transient);
+                       else if (win != ws->focus_prev)
+                               winfocus = ws->focus_prev;
+                       else if (win != ws->focus)
+                               winfocus = ws->focus;
+               }
+
+               /* normal and fallback if haven't found anything to focus on */
+               if (winfocus == NULL) {
+                       winfocus = TAILQ_PREV(win, ws_win_list, entry);
+                       if (TAILQ_FIRST(&ws->winlist) == win)
+                               winfocus = TAILQ_NEXT(win, entry);
+                       else {
+                               if (ws->focus)
+                                       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);
+               ignore_enter = 1;
+               stack();
+               focus_win(winfocus);
+               ignore_enter = 0;
+       }
 }
 
 void
@@ -3634,25 +3956,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)
 {
@@ -3739,7 +4042,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);
@@ -3786,7 +4089,6 @@ void
 screenchange(XEvent *e) {
        XRRScreenChangeNotifyEvent      *xe = (XRRScreenChangeNotifyEvent *)e;
        struct swm_region               *r;
-       struct ws_win                   *win;
        int                             i;
 
        DNPRINTF(SWM_D_EVENT, "screenchange: %lu\n", xe->root);
@@ -3804,11 +4106,6 @@ screenchange(XEvent *e) {
        /* brute force for now, just re-enumerate the regions */
        scan_xrandr(i);
 
-       /* hide any windows that went away */
-       TAILQ_FOREACH(r, &screens[i].rl, entry)
-               TAILQ_FOREACH(win, &r->ws->winlist, entry)
-                       XUnmapWindow(display, win->id);
-
        /* add bars to all regions */
        for (i = 0; i < ScreenCount(display); i++)
                TAILQ_FOREACH(r, &screens[i].rl, entry)
@@ -3826,7 +4123,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)
@@ -3864,6 +4161,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++)
@@ -3891,8 +4189,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 */
@@ -3900,9 +4199,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) {
@@ -3911,6 +4211,7 @@ setup_screens(void)
                }
        }
 }
+
 void
 setup_globals(void)
 {
@@ -3981,6 +4282,7 @@ main(int argc, char *argv[])
        astate = XInternAtom(display, "WM_STATE", False);
        aprot = XInternAtom(display, "WM_PROTOCOLS", False);
        adelete = XInternAtom(display, "WM_DELETE_WINDOW", False);
+       takefocus = XInternAtom(display, "WM_TAKE_FOCUS", False);
 
        /* look for local and global conf file */
        pwd = getpwuid(getuid());