JasonWoof Got questions, comments, patches, etc.? Contact Jason Woofenden
replace large enternotify hammer with pliers instead.
[spectrwm.git] / scrotwm.c
index b464494..0aa4860 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
@@ -52,7 +53,7 @@
 
 static const char      *cvstag = "$scrotwm$";
 
-#define        SWM_VERSION     "0.9.29"
+#define        SWM_VERSION     "0.9.30"
 
 #include <stdio.h>
 #include <stdlib.h>
@@ -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)
@@ -168,6 +169,10 @@ Atom                       astate;
 Atom                   aprot;
 Atom                   adelete;
 Atom                   takefocus;
+Atom                   a_wmname;
+Atom                   a_utf8_string;
+Atom                   a_string;
+Atom                   a_swm_iconic;
 volatile sig_atomic_t   running = 1;
 volatile sig_atomic_t   restart_wm = 0;
 int                    outputs = 0;
@@ -186,6 +191,13 @@ int                        term_width = 0;
 int                    font_adjusted = 0;
 unsigned int           mod_key = MODKEY;
 
+/* dmenu search */
+struct swm_region      *search_r;
+int                    select_list_pipe[2];
+int                    select_resp_pipe[2];
+pid_t                  searchpid;
+volatile sig_atomic_t  search_resp;
+
 /* dialog windows */
 double                 dialog_ratio = .6;
 /* status bar */
@@ -261,6 +273,7 @@ struct ws_win {
        int                     floatmaxed;     /* flag: floater was maxed in max_stack */
        int                     floating;
        int                     manual;
+       int                     iconic;
        unsigned int            ewmh_flags;
        int                     font_size_boundary[SWM_MAX_FONT_STEPS];
        int                     font_steps;
@@ -390,9 +403,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;
@@ -456,6 +468,7 @@ struct ewmh_hint {
 
 void   store_float_geom(struct ws_win *win, struct swm_region *r);
 int    floating_toggle_win(struct ws_win *win);
+void   spawn_select(struct swm_region *, union arg *, char *, int *);
 
 int
 get_property(Window id, Atom atom, long count, Atom type,
@@ -479,6 +492,52 @@ get_property(Window id, Atom atom, long count, Atom type,
 }
 
 void
+update_iconic(struct ws_win *win, int newv)
+{
+       int32_t v = newv;
+       Atom iprop;
+
+       win->iconic = newv;
+
+       iprop = XInternAtom(display, "_SWM_ICONIC", False);
+       if (!iprop)
+               return;
+       if (newv)
+               XChangeProperty(display, win->id, iprop, XA_INTEGER, 32,
+                   PropModeReplace, (unsigned char *)&v, 1);
+       else
+               XDeleteProperty(display, win->id, iprop);
+}
+
+int
+get_iconic(struct ws_win *win)
+{
+       int32_t v = 0;
+       int retfmt, status;
+       Atom iprop, rettype;
+       unsigned long nitems, extra;
+       unsigned char *prop = NULL;
+
+       iprop = XInternAtom(display, "_SWM_ICONIC", False);
+       if (!iprop)
+               goto out;
+       status = XGetWindowProperty(display, win->id, iprop, 0L, 1L,
+           False, XA_INTEGER, &rettype, &retfmt, &nitems, &extra, &prop);
+       if (status != Success)
+               goto out;
+       if (rettype != XA_INTEGER || retfmt != 32)
+               goto out;
+       if (nitems != 1)
+               goto out;
+       v = *((int32_t *)prop);
+
+out:
+       if (prop != NULL)
+               XFree(prop);
+       return (v);
+}
+
+void
 setup_ewmh(void)
 {
        int                     i,j;
@@ -597,7 +656,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;
@@ -874,8 +933,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");
@@ -884,8 +944,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");
@@ -953,6 +1014,8 @@ sighdlr(int sig)
 #endif /* SWM_DEBUG */
                                break;
                        }
+                       if (pid == searchpid)
+                               search_resp = 1;
 
 #ifdef SWM_DEBUG
                        if (WIFEXITED(status)) {
@@ -1318,6 +1381,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};
@@ -1381,15 +1456,13 @@ config_win(struct ws_win *win, XConfigureRequestEvent  *ev)
 {
        XConfigureEvent         ce;
 
-       fprintf(stderr, "config_win: win %lu x %d y %d w %d h %d\n",
-           win->id, win->g.x, win->g.y, win->g.w, win->g.h);
-       DNPRINTF(SWM_D_MISC, "config_win: win %lu x %d y %d w %d h %d\n",
-           win->id, win->g.x, win->g.y, win->g.w, win->g.h);
-
        if (win == NULL)
                return;
 
        if (ev == NULL) {
+               DNPRINTF(SWM_D_MISC, "config_win: win %lu x %d y %d w %d h %d\n",
+                   win->id, win->g.x, win->g.y, win->g.w, win->g.h);
+
                ce.type = ConfigureNotify;
                ce.display = display;
                ce.event = win->id;
@@ -1402,6 +1475,8 @@ config_win(struct ws_win *win, XConfigureRequestEvent  *ev)
                ce.above = None;
                ce.override_redirect = False;
        } else {
+               DNPRINTF(SWM_D_MISC, "config_win: ev win %lu x %d y %d w %d h %d\n",
+                   ev->window, ev->x, ev->y, ev->width, ev->height);
                ce.type = ConfigureNotify;
                ce.display = ev->display;
                ce.event = ev->window;
@@ -1429,6 +1504,8 @@ count_win(struct workspace *ws, int count_transient)
                        continue;
                if (count_transient == 0 && win->transient)
                        continue;
+               if (win->iconic)
+                       continue;
                count++;
        }
        DNPRINTF(SWM_D_MISC, "count_win: %d\n", count);
@@ -1600,40 +1677,40 @@ find_window(Window id)
 }
 
 void
-spawn(struct swm_region *r, union arg *args)
+spawn(struct swm_region *r, union arg *args, int close_fd)
 {
        int                     fd;
        char                    *ret = NULL;
 
        DNPRINTF(SWM_D_MISC, "spawn: %s\n", args->argv[0]);
 
-       if (fork() == 0) {
-               if (display)
-                       close(ConnectionNumber(display));
+       if (display)
+               close(ConnectionNumber(display));
 
-               setenv("LD_PRELOAD", SWM_LIB, 1);
+       setenv("LD_PRELOAD", SWM_LIB, 1);
 
-               if (asprintf(&ret, "%d", r->ws->idx) == -1) {
-                       perror("_SWM_WS");
-                       _exit(1);
-               }
-               setenv("_SWM_WS", ret, 1);
-               free(ret);
-               ret = NULL;
+       if (asprintf(&ret, "%d", r->ws->idx) == -1) {
+               perror("_SWM_WS");
+               _exit(1);
+       }
+       setenv("_SWM_WS", ret, 1);
+       free(ret);
+       ret = NULL;
 
-               if (asprintf(&ret, "%d", getpid()) == -1) {
-                       perror("_SWM_PID");
-                       _exit(1);
-               }
-               setenv("_SWM_PID", ret, 1);
-               free(ret);
-               ret = NULL;
+       if (asprintf(&ret, "%d", getpid()) == -1) {
+               perror("_SWM_PID");
+               _exit(1);
+       }
+       setenv("_SWM_PID", ret, 1);
+       free(ret);
+       ret = NULL;
 
-               if (setsid() == -1) {
-                       perror("setsid");
-                       _exit(1);
-               }
+       if (setsid() == -1) {
+               perror("setsid");
+               _exit(1);
+       }
 
+       if (close_fd) {
                /*
                 * close stdin and stdout to prevent interaction between apps
                 * and the baraction script
@@ -1647,12 +1724,12 @@ spawn(struct swm_region *r, union arg *args)
                dup2(fd, STDOUT_FILENO);
                if (fd > 2)
                        close(fd);
+       }
 
-               execvp(args->argv[0], args->argv);
+       execvp(args->argv[0], args->argv);
 
-               perror("execvp");
-               _exit(1);
-       }
+       perror("execvp");
+       _exit(1);
 }
 
 void
@@ -1662,7 +1739,8 @@ spawnterm(struct swm_region *r, union arg *args)
 
        if (term_width)
                setenv("_SWM_XTERM_FONTADJ", "", 1);
-       spawn(r, args);
+       if (fork() == 0)
+               spawn(r, args, 1);
 }
 
 void
@@ -1834,6 +1912,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);
@@ -1892,12 +1971,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
@@ -2000,6 +2083,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;
@@ -2020,8 +2126,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
@@ -2029,6 +2139,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);
@@ -2062,6 +2175,8 @@ swapwin(struct swm_region *r, union arg *args)
                return;
        }
 
+       sort_windows(wl);
+
        stack();
 }
 
@@ -2122,13 +2237,13 @@ done:
        if (winfocus == winlostfocus || winfocus == NULL)
                return;
 
-       focus_magic(winfocus, SWM_F_GENERIC);
+       focus_magic(winfocus);
 }
 
 void
 focus(struct swm_region *r, union arg *args)
 {
-       struct ws_win           *winfocus = NULL, *winlostfocus = NULL;
+       struct ws_win           *winfocus = NULL, *winlostfocus = NULL, *head;
        struct ws_win           *cur_focus = NULL;
        struct ws_win_list      *wl = NULL;
        struct workspace        *ws = NULL;
@@ -2140,14 +2255,16 @@ focus(struct swm_region *r, union arg *args)
 
        /* treat FOCUS_CUR special */
        if (args->id == SWM_ARG_ID_FOCUSCUR) {
-               if (r->ws->focus)
+               if (r->ws->focus && r->ws->focus->iconic == 0)
                        winfocus = r->ws->focus;
-               else if (r->ws->focus_prev)
+               else if (r->ws->focus_prev && r->ws->focus_prev->iconic == 0)
                        winfocus = r->ws->focus_prev;
                else
-                       winfocus = TAILQ_FIRST(&r->ws->winlist);
+                       TAILQ_FOREACH(winfocus, &r->ws->winlist, entry)
+                               if (winfocus->iconic == 0)
+                                       break;
 
-               focus_magic(winfocus, SWM_F_GENERIC);
+               focus_magic(winfocus);
                return;
        }
 
@@ -2160,15 +2277,37 @@ focus(struct swm_region *r, union arg *args)
 
        switch (args->id) {
        case SWM_ARG_ID_FOCUSPREV:
-               winfocus = TAILQ_PREV(cur_focus, ws_win_list, entry);
-               if (winfocus == NULL)
-                       winfocus = TAILQ_LAST(wl, ws_win_list);
+               head = TAILQ_PREV(cur_focus, ws_win_list, entry);
+               if (head == NULL)
+                       head = TAILQ_LAST(wl, ws_win_list);
+               winfocus = head;
+               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;
 
        case SWM_ARG_ID_FOCUSNEXT:
-               winfocus = TAILQ_NEXT(cur_focus, entry);
-               if (winfocus == NULL)
-                       winfocus = TAILQ_FIRST(wl);
+               head = TAILQ_NEXT(cur_focus, entry);
+               if (head == NULL)
+                       head = TAILQ_FIRST(wl);
+               winfocus = head;
+
+               /* skip iconics */
+               if (winfocus && winfocus->iconic) {
+                       TAILQ_FOREACH(winfocus, wl, entry)
+                               if (winfocus->iconic == 0)
+                                       break;
+               }
                break;
 
        case SWM_ARG_ID_FOCUSMAIN:
@@ -2180,11 +2319,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
@@ -2253,6 +2391,9 @@ stack(void) {
        }
        if (font_adjusted)
                font_adjusted--;
+
+       if (focus_mode == SWM_FOCUS_DEFAULT)
+               drain_enter_notify();
 }
 
 void
@@ -2317,22 +2458,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;
@@ -2408,7 +2549,7 @@ stack_master(struct workspace *ws, struct swm_geometry *g, int rot, int flip)
                return;
 
        TAILQ_FOREACH(win, &ws->winlist, entry)
-               if (win->transient == 0 && win->floating == 0)
+               if (win->transient == 0 && win->floating == 0 && win->iconic == 0)
                        break;
 
        if (win == NULL)
@@ -2468,6 +2609,8 @@ stack_master(struct workspace *ws, struct swm_geometry *g, int rot, int flip)
        TAILQ_FOREACH(win, &ws->winlist, entry) {
                if (win->transient != 0 || win->floating != 0)
                        continue;
+               if (win->iconic != 0)
+                       continue;
 
                if (win->ewmh_flags & EWMH_F_FULLSCREEN) {
                        fs_win = win;
@@ -2563,12 +2706,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 == 1)
+                       continue;
                if (win->ewmh_flags & EWMH_F_FULLSCREEN) {
                        fs_win = win;
                        continue;
@@ -2742,7 +2886,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);
        }
 }
 
@@ -2750,7 +2894,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];
@@ -2772,6 +2916,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);
@@ -2791,6 +2944,151 @@ send_to_ws(struct swm_region *r, union arg *args)
 }
 
 void
+iconify(struct swm_region *r, union arg *args)
+{
+       union arg a;
+
+       if (r->ws->focus == NULL)
+               return;
+       unmap_window(r->ws->focus);
+       update_iconic(r->ws->focus, 1);
+       stack();
+       r->ws->focus = NULL;
+       a.id = SWM_ARG_ID_FOCUSCUR;
+       focus(r, &a);
+}
+
+unsigned char *
+get_win_name(Display *dpy, Window win, Atom wname, Atom stype,
+    unsigned long *slen)
+{
+       int                     status, retfmt;
+       unsigned long           nitems, nbytes, nextra;
+       unsigned char           *prop = NULL;
+       Atom                    rettype;
+
+       status = XGetWindowProperty(dpy, win, wname, 0L, 0L, False, stype,
+           &rettype, &retfmt,  &nitems, &nbytes, &prop);
+       if (status != Success)
+               return (NULL);
+       XFree(prop);
+
+       status = XGetWindowProperty(dpy, win, wname, 0L, nbytes, False,
+           stype, &rettype, &retfmt, &nitems, &nextra, &prop);
+       if (status != Success) {
+               XFree(prop);
+               return (NULL);
+       }
+       if (rettype != stype) {
+               XFree(prop);
+               return (NULL);
+       }
+       *slen = nitems;
+       return (prop);
+}
+
+void
+uniconify(struct swm_region *r, union arg *args)
+{
+       struct ws_win           *win;
+       FILE                    *lfile;
+       char                    *name;
+       int                     count = 0;
+       unsigned long           len;
+
+       DNPRINTF(SWM_D_MISC, "uniconify\n");
+
+       if (r && r->ws == NULL)
+               return;
+
+       /* make sure we have anything to uniconify */
+       TAILQ_FOREACH(win, &r->ws->winlist, entry) {
+               if (win->ws == NULL)
+                       continue; /* should never happen */
+               if (win->iconic == 0)
+                       continue;
+               count++;
+       }
+       if (count == 0)
+               return;
+
+       search_r = r;
+
+       spawn_select(r, args, "uniconify", &searchpid);
+
+       if ((lfile = fdopen(select_list_pipe[1], "w")) == NULL)
+               return;
+
+       TAILQ_FOREACH(win, &r->ws->winlist, entry) {
+               if (win->ws == NULL)
+                       continue; /* should never happen */
+               if (win->iconic == 0)
+                       continue;
+
+               name = get_win_name(display, win->id, a_wmname, a_string,
+                   &len);
+               if (name == NULL)
+                       continue;
+               fprintf(lfile, "%s.%lu\n", name, win->id);
+               XFree(name);
+       }
+
+       fclose(lfile);
+}
+
+#define MAX_RESP_LEN   1024
+
+void
+search_do_resp(void)
+{
+       ssize_t                 rbytes;
+       struct ws_win           *win;
+       char                    *name, *resp, *s;
+       unsigned long           len;
+
+       DNPRINTF(SWM_D_MISC, "search_do_resp:\n");
+
+       search_resp = 0;
+       searchpid = 0;
+
+       if ((resp = calloc(1, MAX_RESP_LEN + 1)) == NULL) {
+               fprintf(stderr, "search: calloc\n");
+               return;
+       }
+
+       rbytes = read(select_resp_pipe[0], resp, MAX_RESP_LEN);
+       if (rbytes <= 0) {
+               fprintf(stderr, "search: read error: %s\n", strerror(errno));
+               goto done;
+       }
+       resp[rbytes] = '\0';
+       len = strlen(resp);
+
+       DNPRINTF(SWM_D_MISC, "search_do_resp: resp %s\n", resp);
+       TAILQ_FOREACH(win, &search_r->ws->winlist, entry) {
+               if (win->iconic == 0)
+                       continue;
+               name = get_win_name(display, win->id, a_wmname, a_string, &len);
+               if (name == NULL)
+                       continue;
+               if (asprintf(&s, "%s.%lu", name, win->id) == -1) {
+                       XFree(name);
+                       continue;
+               }
+               XFree(name);
+               if (strncmp(s, resp, len) == 0) {
+                       /* XXX this should be a callback to generalize */
+                       update_iconic(win, 0);
+                       free(s);
+                       break;
+               }
+               free(s);
+       }
+done:
+       free(resp);
+}
+
+void
 wkill(struct swm_region *r, union arg *args)
 {
        DNPRINTF(SWM_D_MISC, "wkill %d\n", args->id);
@@ -2978,7 +3276,7 @@ resize(struct ws_win *win, union arg *args)
        XUngrabPointer(display, CurrentTime);
 
        /* drain events */
-       while (XCheckMaskEvent(display, EnterWindowMask, &ev));
+       drain_enter_notify();
 }
 
 void
@@ -2993,6 +3291,7 @@ move_window(struct ws_win *win)
        mask = CWX | CWY;
        wc.x = win->g.x;
        wc.y = win->g.y;
+       wc.border_width = border_width;
 
        DNPRINTF(SWM_D_STACK, "move_window: win %lu x %d y %d w %d h %d\n",
            win->id, wc.x, wc.y, wc.width, wc.height);
@@ -3071,7 +3370,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 */
@@ -3129,7 +3428,9 @@ enum keyfuncid {
        kf_spawn_lock,
        kf_spawn_initscr,
        kf_spawn_custom,
-       kf_dumpwins,
+       kf_iconify,
+       kf_uniconify,
+       kf_dumpwins, /* MUST BE LAST */
        kf_invalid
 };
 
@@ -3203,7 +3504,9 @@ struct keyfunc {
        { "spawn_lock",         legacyfunc,     {0} },
        { "spawn_initscr",      legacyfunc,     {0} },
        { "spawn_custom",       dummykeyfunc,   {0} },
-       { "dumpwins",           dumpwins,       {0} },
+       { "iconify",            iconify,        {0} },
+       { "uniconify",          uniconify,      {0} },
+       { "dumpwins",           dumpwins,       {0} }, /* MUST BE LAST */
        { "invalid key func",   NULL,           {0} },
 };
 struct key {
@@ -3259,15 +3562,15 @@ struct spawn_prog {
 int                            spawns_size = 0, spawns_length = 0;
 struct spawn_prog              *spawns = NULL;
 
-void
-spawn_custom(struct swm_region *r, union arg *args, char *spawn_name)
+int
+spawn_expand(struct swm_region *r, union arg *args, char *spawn_name,
+    char ***ret_args)
 {
-       union arg               a;
        struct spawn_prog       *prog = NULL;
        int                     i;
        char                    *ap, **real_args;
 
-       DNPRINTF(SWM_D_SPAWN, "spawn_custom %s\n", spawn_name);
+       DNPRINTF(SWM_D_SPAWN, "spawn_expand %s\n", spawn_name);
 
        /* find program */
        for (i = 0; i < spawns_length; i++) {
@@ -3277,7 +3580,7 @@ spawn_custom(struct swm_region *r, union arg *args, char *spawn_name)
        if (prog == NULL) {
                fprintf(stderr, "spawn_custom: program %s not found\n",
                    spawn_name);
-               return;
+               return (-1);
        }
 
        /* make room for expanded args */
@@ -3334,10 +3637,66 @@ spawn_custom(struct swm_region *r, union arg *args, char *spawn_name)
                fprintf(stderr, "\n");
        }
 #endif
+       *ret_args = real_args;
+       return (prog->argc);
+}
+
+void
+spawn_custom(struct swm_region *r, union arg *args, char *spawn_name)
+{
+       union arg               a;
+       char                    **real_args;
+       int                     spawn_argc, i;
+
+       if ((spawn_argc = spawn_expand(r, args, spawn_name, &real_args)) < 0)
+               return;
+       a.argv = real_args;
+       if (fork() == 0)
+               spawn(r, &a, 1);
+
+       for (i = 0; i < spawn_argc; i++)
+               free(real_args[i]);
+       free(real_args);
+}
+
+void
+spawn_select(struct swm_region *r, union arg *args, char *spawn_name, int *pid)
+{
+       union arg               a;
+       char                    **real_args;
+       int                     i, spawn_argc;
 
+       if ((spawn_argc = spawn_expand(r, args, spawn_name, &real_args)) < 0)
+               return;
        a.argv = real_args;
-       spawn(r, &a);
-       for (i = 0; i < prog->argc; i++)
+
+       if (pipe(select_list_pipe) == -1)
+               err(1, "pipe error");
+       if (pipe(select_resp_pipe) == -1)
+               err(1, "pipe error");
+
+       if (signal(SIGPIPE, SIG_IGN) == SIG_ERR)
+               err(1, "could not disable SIGPIPE");
+       switch (*pid = fork()) {
+       case -1:
+               err(1, "cannot fork");
+               break;
+       case 0: /* child */
+               if (dup2(select_list_pipe[0], 0) == -1)
+                       errx(1, "dup2");
+               if (dup2(select_resp_pipe[1], 1) == -1)
+                       errx(1, "dup2");
+               close(select_list_pipe[1]);
+               close(select_resp_pipe[0]);
+               spawn(r, &a, 0);
+               break;
+       default: /* parent */
+               close(select_list_pipe[0]);
+               close(select_resp_pipe[1]);
+               break;
+       }
+
+       for (i = 0; i < spawn_argc; i++)
                free(real_args[i]);
        free(real_args);
 }
@@ -3478,6 +3837,13 @@ setup_spawn(void)
                                        " -nf $bar_font_color"
                                        " -sb $bar_border"
                                        " -sf $bar_color",      0);
+       setconfspawn("uniconify",       "dmenu"
+                                       " -i"
+                                       " -fn $bar_font"
+                                       " -nb $bar_color"
+                                       " -nf $bar_font_color"
+                                       " -sb $bar_border"
+                                       " -sf $bar_color",      0);
 }
 
 /* key bindings */
@@ -3727,6 +4093,8 @@ setup_keys(void)
        setkeybinding(MODKEY|ShiftMask, XK_v,           kf_version,     NULL);
        setkeybinding(MODKEY|ShiftMask, XK_Delete,      kf_spawn_custom,        "lock");
        setkeybinding(MODKEY|ShiftMask, XK_i,           kf_spawn_custom,        "initscr");
+       setkeybinding(MODKEY,           XK_w,           kf_iconify,     NULL);
+       setkeybinding(MODKEY|ShiftMask, XK_w,           kf_uniconify,   NULL);
 #ifdef SWM_DEBUG
        setkeybinding(MODKEY|ShiftMask, XK_d,           kf_dumpwins,    NULL);
 #endif
@@ -4262,9 +4630,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 (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);
        }
@@ -4272,6 +4643,8 @@ manage_window(Window id)
        if ((win = calloc(1, sizeof(struct ws_win))) == NULL)
                errx(1, "calloc: failed to allocate memory for new window");
 
+       win->id = id;
+
        /* Get all the window data in one shot */
        ws_idx_atom = XInternAtom(display, "_SWM_WS", False);
        if (ws_idx_atom)
@@ -4299,6 +4672,8 @@ manage_window(Window id)
                        XFree(prot);
        }
 
+       win->iconic = get_iconic(win);
+
        /*
         * Figure out where to put the window. If it was previously assigned to
         * a workspace (either by spawn() or manually moving), and isn't
@@ -4333,7 +4708,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;
@@ -4419,8 +4797,6 @@ manage_window(Window id)
        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 || win->transient) && (ws->idx == r->ws->idx))
                XMapRaised(display, win->id);
@@ -4473,8 +4849,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);
@@ -4483,14 +4858,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);
@@ -4564,7 +4939,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++)
@@ -4601,18 +4976,6 @@ configurerequest(XEvent *e)
        } else {
                DNPRINTF(SWM_D_EVENT, "configurerequest: change window: %lu\n",
                    ev->window);
-#if 0
-               if (win->floating) {
-                       if (ev->value_mask & CWX)
-                               win->g.x = ev->x;
-                       if (ev->value_mask & CWY)
-                               win->g.y = ev->y;
-                       if (ev->value_mask & CWWidth)
-                               win->g.w = ev->width;
-                       if (ev->value_mask & CWHeight)
-                               win->g.h = ev->height;
-               }
-#endif
                config_win(win, ev);
        }
 }
@@ -4676,11 +5039,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;
@@ -4771,7 +5129,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 */
@@ -4869,7 +5227,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
@@ -4881,12 +5239,18 @@ propertynotify(XEvent *e)
        DNPRINTF(SWM_D_EVENT, "propertynotify: window: %lu\n",
            ev->window);
 
-       if (ev->state == PropertyDelete)
-               return; /* ignore */
        win = find_window(ev->window);
        if (win == NULL)
                return;
 
+       if (ev->state == PropertyDelete && ev->atom == a_swm_iconic) {
+               update_iconic(win, 0);
+               XMapRaised(display, win->id);
+               stack();
+               focus_win(win);
+               return;
+       }
+
        switch (ev->atom) {
        case XA_WM_NORMAL_HINTS:
 #if 0
@@ -4924,6 +5288,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);
        }
 }
 
@@ -5388,6 +5767,10 @@ main(int argc, char *argv[])
        aprot = XInternAtom(display, "WM_PROTOCOLS", False);
        adelete = XInternAtom(display, "WM_DELETE_WINDOW", False);
        takefocus = XInternAtom(display, "WM_TAKE_FOCUS", False);
+       a_wmname = XInternAtom(display, "WM_NAME", False);
+       a_utf8_string = XInternAtom(display, "UTF8_STRING", False);
+       a_string = XInternAtom(display, "STRING", False);
+       a_swm_iconic = XInternAtom(display, "_SWM_ICONIC", False);
 
        /* look for local and global conf file */
        pwd = getpwuid(getuid());
@@ -5492,6 +5875,8 @@ main(int argc, char *argv[])
                                DNPRINTF(SWM_D_MISC, "select failed");
                if (restart_wm == 1)
                        restart(NULL, NULL);
+               if (search_resp == 1)
+                       search_do_resp();
                if (running == 0)
                        goto done;
                if (bar_alarm) {