JasonWoof Got questions, comments, patches, etc.? Contact Jason Woofenden
Merge branch 'master' of opensource.conformal.com:/git/scrotwm
[spectrwm.git] / scrotwm.c
index 0aa4860..55bf753 100644 (file)
--- a/scrotwm.c
+++ b/scrotwm.c
@@ -4,6 +4,7 @@
  * 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) 2010 Tuukka Kataja <stuge@xor.fi>
  * Copyright (c) 2011 Jason L. Wright <jason@thought.net>
  *
  * Permission to use, copy, modify, and distribute this software for any
  * DEALINGS IN THE SOFTWARE.
  */
 
-static const char      *cvstag = "$scrotwm$";
+static const char      *cvstag =
+    "$scrotwm$";
 
-#define        SWM_VERSION     "0.9.30"
+#define        SWM_VERSION     "0.9.34"
 
 #include <stdio.h>
 #include <stdlib.h>
@@ -85,6 +87,7 @@ static const char     *cvstag = "$scrotwm$";
 #include <X11/Xproto.h>
 #include <X11/Xutil.h>
 #include <X11/extensions/Xrandr.h>
+#include <X11/extensions/XTest.h>
 
 #ifdef __OSX__
 #include <osx.h>
@@ -218,6 +221,7 @@ int                 bar_verbose = 1;
 int                    bar_height = 0;
 int                    stack_enabled = 1;
 int                    clock_enabled = 1;
+int                    urgent_enabled = 0;
 char                   *clock_format = NULL;
 int                    title_name_enabled = 0;
 int                    title_class_enabled = 0;
@@ -225,6 +229,7 @@ int                 window_name_enabled = 0;
 int                    focus_mode = SWM_FOCUS_DEFAULT;
 int                    disable_border = 0;
 int                    border_width = 1;
+int                    verbose_layout = 0;
 pid_t                  bar_pid;
 GC                     bar_gc;
 XGCValues              bar_gcv;
@@ -287,9 +292,19 @@ struct ws_win {
        XWindowAttributes       wa;
        XSizeHints              sh;
        XClassHint              ch;
+       XWMHints                *hints;
 };
 TAILQ_HEAD(ws_win_list, ws_win);
 
+/* pid goo */
+struct pid_e {
+       TAILQ_ENTRY(pid_e)      entry;
+       long                    pid;
+       int                     ws;
+};
+TAILQ_HEAD(pid_list, pid_e);
+struct pid_list                        pidlist = TAILQ_HEAD_INITIALIZER(pidlist);
+
 /* layout handlers */
 void   stack(void);
 void   vertical_config(struct workspace *, int);
@@ -297,6 +312,8 @@ void        vertical_stack(struct workspace *, struct swm_geometry *);
 void   horizontal_config(struct workspace *, int);
 void   horizontal_stack(struct workspace *, struct swm_geometry *);
 void   max_stack(struct workspace *, struct swm_geometry *);
+void   plain_stacker(struct workspace *);
+void   fancy_stacker(struct workspace *);
 
 struct ws_win *find_window(Window);
 
@@ -311,18 +328,20 @@ struct layout {
        u_int32_t       flags;
 #define SWM_L_FOCUSPREV                (1<<0)
 #define SWM_L_MAPONFOCUS       (1<<1)
-       char            *name;
+       void            (*l_string)(struct workspace *);
 } layouts[] =  {
        /* stack,               configure */
-       { vertical_stack,       vertical_config,        0,      "[|]" },
-       { horizontal_stack,     horizontal_config,      0,      "[-]" },
+       { vertical_stack,       vertical_config,        0,      plain_stacker },
+       { horizontal_stack,     horizontal_config,      0,      plain_stacker },
        { max_stack,            NULL,
-         SWM_L_MAPONFOCUS | SWM_L_FOCUSPREV,                   "[ ]"},
-       { NULL,                 NULL,                   0,      NULL },
+         SWM_L_MAPONFOCUS | SWM_L_FOCUSPREV,                   plain_stacker },
+       { NULL,                 NULL,                   0,      NULL  },
 };
 
-/* position of max_stack mode in the layouts array */
-#define SWM_MAX_STACK          2
+/* position of max_stack mode in the layouts array, index into layouts! */
+#define SWM_V_STACK            (0)
+#define SWM_H_STACK            (1)
+#define SWM_MAX_STACK          (2)
 
 #define SWM_H_SLICE            (32)
 #define SWM_V_SLICE            (32)
@@ -330,6 +349,7 @@ struct layout {
 /* define work spaces */
 struct workspace {
        int                     idx;            /* workspace index */
+       int                     always_raise;   /* raise windows on focus */
        struct layout           *cur_layout;    /* current layout handlers */
        struct ws_win           *focus;         /* may be NULL */
        struct ws_win           *focus_prev;    /* may be NULL */
@@ -337,6 +357,7 @@ struct workspace {
        struct swm_region       *old_r;         /* may be NULL */
        struct ws_win_list      winlist;        /* list of windows in ws */
        struct ws_win_list      unmanagedlist;  /* list of dead windows in ws */
+       char                    stacker[10];    /* display stacker and layout */
 
        /* stacker state */
        struct {
@@ -399,6 +420,14 @@ union arg {
 #define SWM_ARG_ID_CENTER      (71)
 #define SWM_ARG_ID_KILLWINDOW  (80)
 #define SWM_ARG_ID_DELETEWINDOW        (81)
+#define SWM_ARG_ID_WIDTHGROW   (90)
+#define SWM_ARG_ID_WIDTHSHRINK (91)
+#define SWM_ARG_ID_HEIGHTGROW  (92)
+#define SWM_ARG_ID_HEIGHTSHRINK        (93)
+#define SWM_ARG_ID_MOVEUP      (100)
+#define SWM_ARG_ID_MOVEDOWN    (101)
+#define SWM_ARG_ID_MOVELEFT    (102)
+#define SWM_ARG_ID_MOVERIGHT   (103)
        char                    **argv;
 };
 
@@ -415,6 +444,7 @@ struct quirk {
 #define SWM_Q_ANYWHERE         (1<<2)  /* don't position this window */
 #define SWM_Q_XTERM_FONTADJ    (1<<3)  /* adjust xterm fonts when resizing */
 #define SWM_Q_FULLSCREEN       (1<<4)  /* remove border */
+#define SWM_Q_FOCUSPREV                (1<<5)  /* focus on caller */
 };
 int                            quirks_size = 0, quirks_length = 0;
 struct quirk                   *quirks = NULL;
@@ -554,7 +584,8 @@ setup_ewmh(void)
                /* Report supported atoms */
                XDeleteProperty(display, screens[i].root, sup_list);
                for (j = 0; j < LENGTH(ewmh); j++)
-                       XChangeProperty(display, screens[i].root, sup_list, XA_ATOM, 32,
+                       XChangeProperty(display, screens[i].root,
+                           sup_list, XA_ATOM, 32,
                            PropModeAppend, (unsigned char *)&ewmh[j].atom,1);
        }
 }
@@ -591,12 +622,11 @@ void
 ewmh_autoquirk(struct ws_win *win)
 {
        int                     success, i;
-       unsigned long           *data = NULL;
-       unsigned long           n;
+       unsigned long           *data = NULL, n;
        Atom                    type;
 
        success = get_property(win->id, ewmh[_NET_WM_WINDOW_TYPE].atom, (~0L),
-           XA_ATOM, &n, (unsigned char **)&data);
+           XA_ATOM, &n, (void *)&data);
 
        if (!success) {
                XFree(data);
@@ -740,7 +770,8 @@ ewmh_update_win_state(struct ws_win *win, long state, long action)
                        win->manual = (win->ewmh_flags & SWM_F_MANUAL) != 0;
        if (state == ewmh[_NET_WM_STATE_FULLSCREEN].atom)
                if (changed)
-                       if (!ewmh_set_win_fullscreen(win, win->ewmh_flags & EWMH_F_FULLSCREEN))
+                       if (!ewmh_set_win_fullscreen(win,
+                           win->ewmh_flags & EWMH_F_FULLSCREEN))
                                win->ewmh_flags = orig_flags; /* revert */
 
        XDeleteProperty(display, win->id, ewmh[_NET_WM_STATE].atom);
@@ -783,8 +814,8 @@ ewmh_get_win_state(struct ws_win *win)
        if (win->manual)
                win->ewmh_flags |= SWM_F_MANUAL;
 
-       success = get_property(win->id, ewmh[_NET_WM_STATE].atom, (~0L), XA_ATOM,
-           &n, (unsigned char **)&states);
+       success = get_property(win->id, ewmh[_NET_WM_STATE].atom,
+           (~0L), XA_ATOM, &n, (void *)&states);
 
        if (!success)
                return;
@@ -1042,6 +1073,24 @@ sighdlr(int sig)
        errno = saved_errno;
 }
 
+struct pid_e *
+find_pid(long pid)
+{
+       struct pid_e            *p = NULL;
+
+       DNPRINTF(SWM_D_MISC, "find_pid: %lu\n", pid);
+
+       if (pid == 0)
+               return (NULL);
+
+       TAILQ_FOREACH(p, &pidlist, entry) {
+               if (p->pid == pid)
+                       return (p);
+       }
+
+       return (NULL);
+}
+
 unsigned long
 name_to_color(char *colorname)
 {
@@ -1088,6 +1137,28 @@ setscreencolor(char *val, int i, int c)
 }
 
 void
+fancy_stacker(struct workspace *ws)
+{
+       strcpy(ws->stacker, "[   ]");
+       if (ws->cur_layout->l_stack == vertical_stack)
+               snprintf(ws->stacker, sizeof ws->stacker, "[%d|%d]",
+                   ws->l_state.vertical_mwin, ws->l_state.vertical_stacks);
+       if (ws->cur_layout->l_stack == horizontal_stack)
+               snprintf(ws->stacker, sizeof ws->stacker, "[%d-%d]",
+                   ws->l_state.horizontal_mwin, ws->l_state.horizontal_stacks);
+}
+
+void
+plain_stacker(struct workspace *ws)
+{
+       strcpy(ws->stacker, "[ ]");
+       if (ws->cur_layout->l_stack == vertical_stack)
+               strcpy(ws->stacker, "[|]");
+       if (ws->cur_layout->l_stack == horizontal_stack)
+               strcpy(ws->stacker, "[-]");
+}
+
+void
 custom_region(char *val)
 {
        unsigned int                    sidx, x, y, w, h;
@@ -1103,11 +1174,12 @@ custom_region(char *val)
        if (w < 1 || h < 1)
                errx(1, "region %ux%u+%u+%u too small\n", w, h, x, y);
 
-       if (x  < 0 || x > DisplayWidth(display, sidx) ||
-           y < 0 || y > DisplayHeight(display, sidx) ||
+       if (x > DisplayWidth(display, sidx) ||
+           y > DisplayHeight(display, sidx) ||
            w + x > DisplayWidth(display, sidx) ||
            h + y > DisplayHeight(display, sidx)) {
-               fprintf(stderr, "ignoring region %ux%u+%u+%u - not within screen boundaries "
+               fprintf(stderr, "ignoring region %ux%u+%u+%u "
+                   "- not within screen boundaries "
                    "(%ux%u)\n", w, h, x, y,
                    DisplayWidth(display, sidx), DisplayHeight(display, sidx));
                return;
@@ -1198,6 +1270,44 @@ bar_window_name(char *s, ssize_t sz, struct ws_win *cur_focus)
        }
 }
 
+int            urgent[SWM_WS_MAX];
+void
+bar_urgent(char *s, ssize_t sz)
+{
+       XWMHints                *wmh = NULL;
+       struct ws_win           *win;
+       int                     i, j;
+       char                    b[8];
+
+       if (urgent_enabled == 0)
+               return;
+
+       for (i = 0; i < SWM_WS_MAX; i++)
+               urgent[i] = 0;
+
+       for (i = 0; i < ScreenCount(display); i++)
+               for (j = 0; j < SWM_WS_MAX; j++)
+                       TAILQ_FOREACH(win, &screens[i].ws[j].winlist, entry) {
+                               wmh = XGetWMHints(display, win->id);
+                               if (wmh == NULL)
+                                       continue;
+
+                               if (wmh->flags & XUrgencyHint)
+                                       urgent[j] = 1;
+                               XFree(wmh);
+                       }
+
+       strlcat(s, "* ", sz);
+       for (i = 0; i < SWM_WS_MAX; i++) {
+               if (urgent[i])
+                       snprintf(b, sizeof b, "%d ", i + 1);
+               else
+                       snprintf(b, sizeof b, "- ");
+               strlcat(s, b, sz);
+       }
+       strlcat(s, "*    ", sz);
+}
+
 void
 bar_update(void)
 {
@@ -1210,7 +1320,6 @@ bar_update(void)
        char                    cn[SWM_BAR_MAX];
        char                    loc[SWM_BAR_MAX];
        char                    *b;
-       char                    *stack = "";
 
        if (bar_enabled == 0)
                return;
@@ -1243,15 +1352,13 @@ bar_update(void)
                TAILQ_FOREACH(r, &screens[i].rl, entry) {
                        strlcpy(cn, "", sizeof cn);
                        if (r && r->ws) {
+                               bar_urgent(cn, sizeof cn);
                                bar_class_name(cn, sizeof cn, r->ws->focus);
                                bar_window_name(cn, sizeof cn, r->ws->focus);
                        }
 
-                       if (stack_enabled)
-                               stack = r->ws->cur_layout->name;
-
                        snprintf(loc, sizeof loc, "%d:%d %s   %s%s    %s    %s",
-                           x++, r->ws->idx + 1, stack, s, cn, bar_ext,
+                           x++, r->ws->idx + 1, r->ws->stacker, s, cn, bar_ext,
                            bar_vertext);
                        bar_print(r, loc);
                }
@@ -1360,12 +1467,14 @@ bar_setup(struct swm_region *r)
        if (bar_fs == NULL)
                errx(1, "couldn't create font structure");
 
-       bar_height = bar_fs->ascent + bar_fs->descent + 1 + 2 * bar_border_width;
+       bar_height = bar_fs->ascent + bar_fs->descent + 1 +
+           2 * bar_border_width;
        x = X(r);
        y = bar_at_bottom ? (Y(r) + HEIGHT(r) - bar_height) : Y(r);
 
        r->bar_window = XCreateSimpleWindow(display,
-           r->s->root, x, y, WIDTH(r) - 2 * bar_border_width, bar_height - 2 * bar_border_width,
+           r->s->root, x, y, WIDTH(r) - 2 * bar_border_width,
+           bar_height - 2 * bar_border_width,
            bar_border_width, r->s->c[SWM_S_COLOR_BAR_BORDER].color,
            r->s->c[SWM_S_COLOR_BAR].color);
        bar_gc = XCreateGC(display, r->bar_window, 0, &bar_gcv);
@@ -1460,7 +1569,8 @@ config_win(struct ws_win *win, XConfigureRequestEvent  *ev)
                return;
 
        if (ev == NULL) {
-               DNPRINTF(SWM_D_MISC, "config_win: win %lu x %d y %d w %d h %d\n",
+               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;
@@ -1475,7 +1585,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",
+               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;
@@ -1677,7 +1788,7 @@ find_window(Window id)
 }
 
 void
-spawn(struct swm_region *r, union arg *args, int close_fd)
+spawn(int ws_idx, union arg *args, int close_fd)
 {
        int                     fd;
        char                    *ret = NULL;
@@ -1689,7 +1800,7 @@ spawn(struct swm_region *r, union arg *args, int close_fd)
 
        setenv("LD_PRELOAD", SWM_LIB, 1);
 
-       if (asprintf(&ret, "%d", r->ws->idx) == -1) {
+       if (asprintf(&ret, "%d", ws_idx) == -1) {
                perror("_SWM_WS");
                _exit(1);
        }
@@ -1737,10 +1848,11 @@ spawnterm(struct swm_region *r, union arg *args)
 {
        DNPRINTF(SWM_D_MISC, "spawnterm\n");
 
-       if (term_width)
-               setenv("_SWM_XTERM_FONTADJ", "", 1);
-       if (fork() == 0)
-               spawn(r, args, 1);
+       if (fork() == 0) {
+               if (term_width)
+                       setenv("_SWM_XTERM_FONTADJ", "", 1);
+               spawn(r->ws->idx, args, 1);
+       }
 }
 
 void
@@ -1773,7 +1885,7 @@ validate_win(struct ws_win *testwin)
        int                     i, x, foundit = 0;
 
        if (testwin == NULL)
-               return(0);
+               return (0);
 
        for (i = 0, foundit = 0; i < ScreenCount(display); i++)
                TAILQ_FOREACH(r, &screens[i].rl, entry)
@@ -1901,6 +2013,13 @@ focus_win(struct ws_win *win)
        XGetInputFocus(display, &cur_focus, &rr);
        if ((cfw = find_window(cur_focus)) != NULL)
                unfocus_win(cfw);
+       else {
+               /* use larger hammer since the window was killed somehow */
+               TAILQ_FOREACH(cfw, &win->ws->winlist, entry)
+                       if (cfw->ws && cfw->ws->r && cfw->ws->r->s)
+                               XSetWindowBorder(display, cfw->id,
+                                   cfw->ws->r->s->c[SWM_S_COLOR_UNFOCUS].color);
+       }
 
        win->ws->focus = win;
 
@@ -1912,11 +2031,11 @@ 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);
-               if (win->ws->cur_layout->flags & SWM_L_MAPONFOCUS)
+               if (win->ws->cur_layout->flags & SWM_L_MAPONFOCUS ||
+                   win->ws->always_raise)
                        XMapRaised(display, win->id);
 
                XChangeProperty(display, win->s->root,
@@ -1968,6 +2087,9 @@ switchws(struct swm_region *r, union arg *args)
        this_r->ws = new_ws;
        new_ws->r = this_r;
 
+       /* this is needed so that we can click on a window after a restart */
+       unfocus_all();
+
        stack();
        a.id = SWM_ARG_ID_FOCUSCUR;
        focus(new_ws->r, &a);
@@ -2157,7 +2279,7 @@ swapwin(struct swm_region *r, union arg *args)
                                source = source->ws->focus_prev;
                        else
                                return;
-                }
+               }
                if (target == NULL || source == NULL)
                        return;
                source->ws->focus_prev = target;
@@ -2216,7 +2338,8 @@ focus_prev(struct ws_win *win)
        }
 
        /* if in max_stack try harder */
-       if (ws->cur_layout->flags & SWM_L_FOCUSPREV) {
+       if ((win->quirks & SWM_Q_FOCUSPREV) ||
+           (ws->cur_layout->flags & SWM_L_FOCUSPREV)) {
                if (cur_focus != ws->focus_prev)
                        winfocus = ws->focus_prev;
                else if (cur_focus != ws->focus)
@@ -2341,6 +2464,8 @@ cycle_layout(struct swm_region *r, union arg *args)
                ws->cur_layout = &layouts[0];
 
        stack();
+       if (focus_mode == SWM_FOCUS_DEFAULT)
+               drain_enter_notify();
        a.id = SWM_ARG_ID_FOCUSCUR;
        focus(r, &a);
        bar_update();
@@ -2357,8 +2482,9 @@ stack_config(struct swm_region *r, union arg *args)
        if (ws->cur_layout->l_config != NULL)
                ws->cur_layout->l_config(ws, args->id);
 
-       if (args->id != SWM_ARG_ID_STACKINIT);
+       if (args->id != SWM_ARG_ID_STACKINIT)
                stack();
+       bar_update();
 }
 
 void
@@ -2385,6 +2511,7 @@ stack(void) {
                                g.h -= bar_height;
                        }
                        r->ws->cur_layout->l_stack(r->ws, &g);
+                       r->ws->cur_layout->l_string(r->ws);
                        /* save r so we can track region changes */
                        r->ws->old_r = r;
                }
@@ -2458,8 +2585,10 @@ 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 - wc.border_width;
-               win->g.y = r->g.y + (HEIGHT(r) - win->g.h) / 2 - wc.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 */
@@ -2549,7 +2678,8 @@ 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 && win->iconic == 0)
+               if (win->transient == 0 && win->floating == 0
+                   && win->iconic == 0)
                        break;
 
        if (win == NULL)
@@ -2598,7 +2728,8 @@ stack_master(struct workspace *ws, struct swm_geometry *g, int rot, int flip)
        } else {
                msize = -2;
                colno = split = winno / stacks;
-               win_g.w = ((r_g.w - (stacks * 2 * border_width) + 2 * border_width) / stacks);
+               win_g.w = ((r_g.w - (stacks * 2 * border_width) +
+                   2 * border_width) / stacks);
        }
        hrh = r_g.h / colno;
        extra = r_g.h - (colno * hrh);
@@ -2628,10 +2759,11 @@ stack_master(struct workspace *ws, struct swm_geometry *g, int rot, int flip)
                                win_g.x = r_g.x;
                        else
                                win_g.x += win_g.w + 2 * border_width;
-                       win_g.w = (r_g.w - msize - (stacks * 2 * border_width)) / stacks;
+                       win_g.w = (r_g.w - msize -
+                           (stacks * 2 * border_width)) / stacks;
                        if (s == 1)
-                               win_g.w += (r_g.w - msize - (stacks * 2 * border_width)) %
-                                   stacks;
+                               win_g.w += (r_g.w - msize -
+                                   (stacks * 2 * border_width)) % stacks;
                        s--;
                        j = 0;
                }
@@ -2933,7 +3065,8 @@ send_to_ws(struct swm_region *r, union arg *args)
        /* Try to update the window's workspace property */
        ws_idx_atom = XInternAtom(display, "_SWM_WS", False);
        if (ws_idx_atom &&
-           snprintf(ws_idx_str, SWM_PROPLEN, "%d", nws->idx) < SWM_PROPLEN) {
+           snprintf((char *)ws_idx_str, SWM_PROPLEN, "%d", nws->idx) <
+               SWM_PROPLEN) {
                DNPRINTF(SWM_D_PROP, "setting property _SWM_WS to %s\n",
                    ws_idx_str);
                XChangeProperty(display, win->id, ws_idx_atom, XA_STRING, 8,
@@ -2944,6 +3077,26 @@ send_to_ws(struct swm_region *r, union arg *args)
 }
 
 void
+pressbutton(struct swm_region *r, union arg *args)
+{
+       XTestFakeButtonEvent(display, args->id, True, CurrentTime);
+       XTestFakeButtonEvent(display, args->id, False, CurrentTime);
+}
+
+void
+raise_toggle(struct swm_region *r, union arg *args)
+{
+       if (r && r->ws == NULL)
+               return;
+
+       r->ws->always_raise = !r->ws->always_raise;
+
+       /* bring floaters back to top */
+       if (r->ws->always_raise == 0)
+               stack();
+}
+
+void
 iconify(struct swm_region *r, union arg *args)
 {
        union arg a;
@@ -2953,6 +3106,8 @@ iconify(struct swm_region *r, union arg *args)
        unmap_window(r->ws->focus);
        update_iconic(r->ws->focus, 1);
        stack();
+       if (focus_mode == SWM_FOCUS_DEFAULT)
+               drain_enter_notify();
        r->ws->focus = NULL;
        a.id = SWM_ARG_ID_FOCUSCUR;
        focus(r, &a);
@@ -2992,7 +3147,7 @@ uniconify(struct swm_region *r, union arg *args)
 {
        struct ws_win           *win;
        FILE                    *lfile;
-       char                    *name;
+       unsigned char           *name;
        int                     count = 0;
        unsigned long           len;
 
@@ -3043,7 +3198,8 @@ search_do_resp(void)
 {
        ssize_t                 rbytes;
        struct ws_win           *win;
-       char                    *name, *resp, *s;
+       unsigned char           *name;
+       char                    *resp, *s;
        unsigned long           len;
 
        DNPRINTF(SWM_D_MISC, "search_do_resp:\n");
@@ -3156,6 +3312,8 @@ floating_toggle(struct swm_region *r, union arg *args)
            _NET_WM_STATE_TOGGLE);
 
        stack();
+       if (focus_mode == SWM_FOCUS_DEFAULT)
+               drain_enter_notify();
 
        if (win == win->ws->focus) {
                a.id = SWM_ARG_ID_FOCUSCUR;
@@ -3188,15 +3346,20 @@ resize_window(struct ws_win *win, int center)
        XConfigureWindow(display, win->id, mask, &wc);
 }
 
+#define SWM_RESIZE_STEPS       (50)
+
 void
 resize(struct ws_win *win, union arg *args)
 {
        XEvent                  ev;
        Time                    time = 0;
-       struct swm_region       *r = win->ws->r;
+       struct swm_region       *r = NULL;
        int                     relx, rely;
-       union arg               a;
+       int                     resize_step = 0;
 
+       if (win == NULL)
+               return;
+       r = win->ws->r;
 
        DNPRINTF(SWM_D_MOUSE, "resize: win %lu floating %d trans %lu\n",
            win->id, win->floating, win->transient);
@@ -3211,11 +3374,38 @@ resize(struct ws_win *win, union arg *args)
        win->manual = 1;
        ewmh_update_win_state(win, ewmh[_SWM_WM_STATE_MANUAL].atom,
            _NET_WM_STATE_ADD);
-       /* raise the window = move to last in window list */
-       a.id = SWM_ARG_ID_MOVELAST;
-       swapwin(r, &a);
+
        stack();
 
+       switch (args->id) {
+       case SWM_ARG_ID_WIDTHSHRINK:
+               win->g.w -= SWM_RESIZE_STEPS;
+               resize_step = 1;
+               break;
+       case SWM_ARG_ID_WIDTHGROW:
+               win->g.w += SWM_RESIZE_STEPS;
+               resize_step = 1;
+               break;
+       case SWM_ARG_ID_HEIGHTSHRINK:
+               win->g.h -= SWM_RESIZE_STEPS;
+               resize_step = 1;
+               break;
+       case SWM_ARG_ID_HEIGHTGROW:
+               win->g.h += SWM_RESIZE_STEPS;
+               resize_step = 1;
+               break;
+       default:
+               break;
+       }
+       if (resize_step) {
+               resize_window(win, 0);
+               store_float_geom(win,r);
+               return;
+       }
+
+       if (focus_mode == SWM_FOCUS_DEFAULT)
+               drain_enter_notify();
+
        if (XGrabPointer(display, win->id, False, MOUSEMASK, GrabModeAsync,
            GrabModeAsync, None, None /* cursor */, CurrentTime) != GrabSuccess)
                return;
@@ -3235,7 +3425,7 @@ resize(struct ws_win *win, union arg *args)
        do {
                XMaskEvent(display, MOUSEMASK | ExposureMask |
                    SubstructureRedirectMask, &ev);
-               switch(ev.type) {
+               switch (ev.type) {
                case ConfigureRequest:
                case Expose:
                case MapRequest:
@@ -3280,6 +3470,20 @@ resize(struct ws_win *win, union arg *args)
 }
 
 void
+resize_step(struct swm_region *r, union arg *args)
+{
+       struct ws_win           *win = NULL;
+
+       if (r && r->ws && r->ws->focus)
+               win = r->ws->focus;
+       else
+               return;
+
+       resize(win, args);
+}
+
+
+void
 move_window(struct ws_win *win)
 {
        unsigned int            mask;
@@ -3299,13 +3503,19 @@ move_window(struct ws_win *win)
        XConfigureWindow(display, win->id, mask, &wc);
 }
 
+#define SWM_MOVE_STEPS (50)
+
 void
 move(struct ws_win *win, union arg *args)
 {
        XEvent                  ev;
        Time                    time = 0;
-       struct swm_region       *r = win->ws->r;
-       union arg               a;
+       int                     move_step = 0;
+       struct swm_region       *r = NULL;
+
+       if (win == NULL)
+               return;
+       r = win->ws->r;
 
        DNPRINTF(SWM_D_MOUSE, "move: win %lu floating %d trans %lu\n",
            win->id, win->floating, win->transient);
@@ -3316,18 +3526,42 @@ move(struct ws_win *win, union arg *args)
 
        win->manual = 1;
        if (win->floating == 0 && !win->transient) {
-               win->floating = 1;
                ewmh_update_win_state(win, ewmh[_NET_WM_STATE_ABOVE].atom,
                    _NET_WM_STATE_ADD);
        }
        ewmh_update_win_state(win, ewmh[_SWM_WM_STATE_MANUAL].atom,
            _NET_WM_STATE_ADD);
 
-       /* raise the window = move to last in window list */
-       a.id = SWM_ARG_ID_MOVELAST;
-       swapwin(r, &a);
        stack();
 
+       move_step = 0;
+       switch (args->id) {
+       case SWM_ARG_ID_MOVELEFT:
+               win->g.x -= (SWM_MOVE_STEPS - border_width);
+               move_step = 1;
+               break;
+       case SWM_ARG_ID_MOVERIGHT:
+               win->g.x += (SWM_MOVE_STEPS - border_width);
+               move_step = 1;
+               break;
+       case SWM_ARG_ID_MOVEUP:
+               win->g.y -= (SWM_MOVE_STEPS - border_width);
+               move_step = 1;
+               break;
+       case SWM_ARG_ID_MOVEDOWN:
+               win->g.y += (SWM_MOVE_STEPS - border_width);
+               move_step = 1;
+               break;
+       default:
+               break;
+       }
+       if (move_step) {
+               move_window(win);
+               store_float_geom(win,r);
+               return;
+       }
+
+
        if (XGrabPointer(display, win->id, False, MOUSEMASK, GrabModeAsync,
            GrabModeAsync, None, None /* cursor */, CurrentTime) != GrabSuccess)
                return;
@@ -3335,7 +3569,7 @@ move(struct ws_win *win, union arg *args)
        do {
                XMaskEvent(display, MOUSEMASK | ExposureMask |
                    SubstructureRedirectMask, &ev);
-               switch(ev.type) {
+               switch (ev.type) {
                case ConfigureRequest:
                case Expose:
                case MapRequest:
@@ -3373,6 +3607,23 @@ move(struct ws_win *win, union arg *args)
        drain_enter_notify();
 }
 
+void
+move_step(struct swm_region *r, union arg *args)
+{
+       struct ws_win           *win = NULL;
+
+       if (r && r->ws && r->ws->focus)
+               win = r->ws->focus;
+       else
+               return;
+
+       if (!(win->transient != 0 || win->floating != 0))
+               return;
+
+       move(win, args);
+}
+
+
 /* user/key callable function IDs */
 enum keyfuncid {
        kf_cycle_layout,
@@ -3430,6 +3681,16 @@ enum keyfuncid {
        kf_spawn_custom,
        kf_iconify,
        kf_uniconify,
+       kf_raise_toggle,
+       kf_button2,
+       kf_width_shrink,
+       kf_width_grow,
+       kf_height_shrink,
+       kf_height_grow,
+       kf_move_left,
+       kf_move_right,
+       kf_move_up,
+       kf_move_down,
        kf_dumpwins, /* MUST BE LAST */
        kf_invalid
 };
@@ -3506,6 +3767,16 @@ struct keyfunc {
        { "spawn_custom",       dummykeyfunc,   {0} },
        { "iconify",            iconify,        {0} },
        { "uniconify",          uniconify,      {0} },
+       { "raise_toggle",       raise_toggle,   {0} },
+       { "button2",            pressbutton,    {2} },
+       { "width_shrink",       resize_step,    {.id = SWM_ARG_ID_WIDTHSHRINK} },
+       { "width_grow",         resize_step,    {.id = SWM_ARG_ID_WIDTHGROW} },
+       { "height_shrink",      resize_step,    {.id = SWM_ARG_ID_HEIGHTSHRINK} },
+       { "height_grow",        resize_step,    {.id = SWM_ARG_ID_HEIGHTGROW} },
+       { "move_left",          move_step,      {.id = SWM_ARG_ID_MOVELEFT} },
+       { "move_right",         move_step,      {.id = SWM_ARG_ID_MOVERIGHT} },
+       { "move_up",            move_step,      {.id = SWM_ARG_ID_MOVEUP} },
+       { "move_down",          move_step,      {.id = SWM_ARG_ID_MOVEDOWN} },
        { "dumpwins",           dumpwins,       {0} }, /* MUST BE LAST */
        { "invalid key func",   NULL,           {0} },
 };
@@ -3652,7 +3923,7 @@ spawn_custom(struct swm_region *r, union arg *args, char *spawn_name)
                return;
        a.argv = real_args;
        if (fork() == 0)
-               spawn(r, &a, 1);
+               spawn(r->ws->idx, &a, 1);
 
        for (i = 0; i < spawn_argc; i++)
                free(real_args[i]);
@@ -3688,7 +3959,7 @@ spawn_select(struct swm_region *r, union arg *args, char *spawn_name, int *pid)
                        errx(1, "dup2");
                close(select_list_pipe[1]);
                close(select_resp_pipe[0]);
-               spawn(r, &a, 0);
+               spawn(r->ws->idx, &a, 0);
                break;
        default: /* parent */
                close(select_list_pipe[0]);
@@ -3809,8 +4080,9 @@ setconfspawn(char *selector, char *value, int flags)
                        cp += (long)strspn(cp, " \t");
                if (strlen(word) > 0) {
                        prog->argc++;
-                       prog->argv = realloc(prog->argv,
-                           prog->argc * sizeof(char *));
+                       if ((prog->argv = realloc(prog->argv,
+                           prog->argc * sizeof(char *))) == NULL)
+                               err(1, "setconfspawn: realloc");
                        if ((prog->argv[prog->argc - 1] = strdup(word)) == NULL)
                                err(1, "setconfspawn: strdup");
                }
@@ -3909,7 +4181,8 @@ strdupsafe(char *str)
 }
 
 void
-setkeybinding(unsigned int mod, KeySym ks, enum keyfuncid kfid, char *spawn_name)
+setkeybinding(unsigned int mod, KeySym ks, enum keyfuncid kfid,
+    char *spawn_name)
 {
        int                     i, j;
        DNPRINTF(SWM_D_KEY, "setkeybinding: enter %s [%s]\n",
@@ -3956,7 +4229,7 @@ setkeybinding(unsigned int mod, KeySym ks, enum keyfuncid kfid, char *spawn_name
                keys_size = 4;
                DNPRINTF(SWM_D_KEY, "setkeybinding: init list %d\n", keys_size);
                keys = malloc((size_t)keys_size * sizeof(struct key));
-               if (!keys) {
+               if (keys == NULL) {
                        fprintf(stderr, "malloc failed\n");
                        perror(" failed");
                        quit(NULL, NULL);
@@ -3965,7 +4238,7 @@ setkeybinding(unsigned int mod, KeySym ks, enum keyfuncid kfid, char *spawn_name
                keys_size *= 2;
                DNPRINTF(SWM_D_KEY, "setkeybinding: grow list %d\n", keys_size);
                keys = realloc(keys, (size_t)keys_size * sizeof(struct key));
-               if (!keys) {
+               if (keys == NULL) {
                        fprintf(stderr, "realloc failed\n");
                        perror(" failed");
                        quit(NULL, NULL);
@@ -3981,7 +4254,7 @@ setkeybinding(unsigned int mod, KeySym ks, enum keyfuncid kfid, char *spawn_name
                keys[j].spawn_name = strdupsafe(spawn_name);
        } else {
                fprintf(stderr, "keys array problem?\n");
-               if (!keys) {
+               if (keys == NULL) {
                        fprintf(stderr, "keys array problem\n");
                        quit(NULL, NULL);
                }
@@ -4053,7 +4326,7 @@ setup_keys(void)
        setkeybinding(MODKEY|ShiftMask, XK_j,           kf_swap_next,   NULL);
        setkeybinding(MODKEY|ShiftMask, XK_k,           kf_swap_prev,   NULL);
        setkeybinding(MODKEY|ShiftMask, XK_Return,      kf_spawn_term,  NULL);
-       setkeybinding(MODKEY,           XK_p,           kf_spawn_custom,        "menu");
+       setkeybinding(MODKEY,           XK_p,           kf_spawn_custom,"menu");
        setkeybinding(MODKEY|ShiftMask, XK_q,           kf_quit,        NULL);
        setkeybinding(MODKEY,           XK_q,           kf_restart,     NULL);
        setkeybinding(MODKEY,           XK_m,           kf_focus_main,  NULL);
@@ -4087,14 +4360,24 @@ setup_keys(void)
        setkeybinding(MODKEY|ShiftMask, XK_Tab,         kf_focus_prev,  NULL);
        setkeybinding(MODKEY|ShiftMask, XK_x,           kf_wind_kill,   NULL);
        setkeybinding(MODKEY,           XK_x,           kf_wind_del,    NULL);
-       setkeybinding(MODKEY,           XK_s,           kf_spawn_custom,        "screenshot_all");
-       setkeybinding(MODKEY|ShiftMask, XK_s,           kf_spawn_custom,        "screenshot_wind");
+       setkeybinding(MODKEY,           XK_s,           kf_spawn_custom,"screenshot_all");
+       setkeybinding(MODKEY|ShiftMask, XK_s,           kf_spawn_custom,"screenshot_wind");
        setkeybinding(MODKEY,           XK_t,           kf_float_toggle,NULL);
        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|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);
+       setkeybinding(MODKEY|ShiftMask, XK_r,           kf_raise_toggle,NULL);
+       setkeybinding(MODKEY,           XK_v,           kf_button2,     NULL);
+       setkeybinding(MODKEY,           XK_equal,       kf_width_grow,  NULL);
+       setkeybinding(MODKEY,           XK_minus,       kf_width_shrink,NULL);
+       setkeybinding(MODKEY|ShiftMask, XK_equal,       kf_height_grow, NULL);
+       setkeybinding(MODKEY|ShiftMask, XK_minus,       kf_height_shrink,NULL);
+       setkeybinding(MODKEY,           XK_bracketleft, kf_move_left,   NULL);
+       setkeybinding(MODKEY,           XK_bracketright,kf_move_right,  NULL);
+       setkeybinding(MODKEY|ShiftMask, XK_bracketleft, kf_move_up,     NULL);
+       setkeybinding(MODKEY|ShiftMask, XK_bracketright,kf_move_down,   NULL);
 #ifdef SWM_DEBUG
        setkeybinding(MODKEY|ShiftMask, XK_d,           kf_dumpwins,    NULL);
 #endif
@@ -4112,7 +4395,7 @@ updatenumlockmask(void)
        for (i = 0; i < 8; i++)
                for (j = 0; j < modmap->max_keypermod; j++)
                        if (modmap->modifiermap[i * modmap->max_keypermod + j]
-                         == XKeysymToKeycode(display, XK_Num_Lock))
+                           == XKeysymToKeycode(display, XK_Num_Lock))
                                numlockmask = (1 << i);
 
        XFreeModifiermap(modmap);
@@ -4174,6 +4457,7 @@ const char *quirkname[] = {
        "ANYWHERE",
        "XTERM_FONTADJ",
        "FULLSCREEN",
+       "FOCUSPREV",
 };
 
 /* SWM_Q_WS: retain '|' for back compat for now (2009-08-11) */
@@ -4194,7 +4478,8 @@ parsequirks(char *qstr, unsigned long *quirk)
                        cp += (long)strspn(cp, SWM_Q_WS);
                for (i = 0; i < LENGTH(quirkname); i++) {
                        if (!strncasecmp(name, quirkname[i], SWM_QUIRK_LEN)) {
-                               DNPRINTF(SWM_D_QUIRK, "parsequirks: %s\n", name);
+                               DNPRINTF(SWM_D_QUIRK,
+                                   "parsequirks: %s\n", name);
                                if (i == 0) {
                                        *quirk = 0;
                                        return (0);
@@ -4257,7 +4542,7 @@ setquirk(const char *class, const char *name, const int quirk)
                quirks_size = 4;
                DNPRINTF(SWM_D_QUIRK, "setquirk: init list %d\n", quirks_size);
                quirks = malloc((size_t)quirks_size * sizeof(struct quirk));
-               if (!quirks) {
+               if (quirks == NULL) {
                        fprintf(stderr, "setquirk: malloc failed\n");
                        perror(" failed");
                        quit(NULL, NULL);
@@ -4265,8 +4550,9 @@ setquirk(const char *class, const char *name, const int quirk)
        } else if (quirks_length == quirks_size) {
                quirks_size *= 2;
                DNPRINTF(SWM_D_QUIRK, "setquirk: grow list %d\n", quirks_size);
-               quirks = realloc(quirks, (size_t)quirks_size * sizeof(struct quirk));
-               if (!quirks) {
+               quirks = realloc(quirks,
+                   (size_t)quirks_size * sizeof(struct quirk));
+               if (quirks == NULL) {
                        fprintf(stderr, "setquirk: realloc failed\n");
                        perror(" failed");
                        quit(NULL, NULL);
@@ -4280,7 +4566,7 @@ setquirk(const char *class, const char *name, const int quirk)
                quirks[j].quirk = quirk;
        } else {
                fprintf(stderr, "quirks array problem?\n");
-               if (!quirks) {
+               if (quirks == NULL) {
                        fprintf(stderr, "quirks array problem!\n");
                        quit(NULL, NULL);
                }
@@ -4308,7 +4594,7 @@ setconfquirk(char *selector, char *value, int flags)
 void
 setup_quirks(void)
 {
-       setquirk("MPlayer",             "xv",           SWM_Q_FLOAT | SWM_Q_FULLSCREEN);
+       setquirk("MPlayer",             "xv",           SWM_Q_FLOAT | SWM_Q_FULLSCREEN | SWM_Q_FOCUSPREV);
        setquirk("OpenOffice.org 3.2",  "VCLSalFrame",  SWM_Q_FLOAT);
        setquirk("Firefox-bin",         "firefox-bin",  SWM_Q_TRANSSZ);
        setquirk("Firefox",             "Dialog",       SWM_Q_FLOAT);
@@ -4320,23 +4606,27 @@ 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 */
 #define SWM_CONF_FILE  "scrotwm.conf"
 
-enum   { SWM_S_BAR_DELAY, SWM_S_BAR_ENABLED, SWM_S_BAR_BORDER_WIDTH, SWM_S_STACK_ENABLED,
-         SWM_S_CLOCK_ENABLED, SWM_S_CLOCK_FORMAT, 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_WINDOW_NAME_ENABLED,
-         SWM_S_FOCUS_MODE, SWM_S_DISABLE_BORDER, SWM_S_BORDER_WIDTH, SWM_S_BAR_FONT,
-         SWM_S_BAR_ACTION, SWM_S_SPAWN_TERM, SWM_S_SS_APP, SWM_S_DIALOG_RATIO,
-         SWM_S_BAR_AT_BOTTOM
+enum   { SWM_S_BAR_DELAY, SWM_S_BAR_ENABLED, SWM_S_BAR_BORDER_WIDTH,
+         SWM_S_STACK_ENABLED, SWM_S_CLOCK_ENABLED, SWM_S_CLOCK_FORMAT,
+         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_WINDOW_NAME_ENABLED, SWM_S_URGENT_ENABLED,
+         SWM_S_FOCUS_MODE, SWM_S_DISABLE_BORDER, SWM_S_BORDER_WIDTH,
+         SWM_S_BAR_FONT, SWM_S_BAR_ACTION, SWM_S_SPAWN_TERM,
+         SWM_S_SS_APP, SWM_S_DIALOG_RATIO, SWM_S_BAR_AT_BOTTOM,
+         SWM_S_VERBOSE_LAYOUT
        };
 
 int
 setconfvalue(char *selector, char *value, int flags)
 {
+       int i;
        switch (flags) {
        case SWM_S_BAR_DELAY:
                bar_delay = atoi(value);
@@ -4384,6 +4674,9 @@ setconfvalue(char *selector, char *value, int flags)
        case SWM_S_TITLE_NAME_ENABLED:
                title_name_enabled = atoi(value);
                break;
+       case SWM_S_URGENT_ENABLED:
+               urgent_enabled = atoi(value);
+               break;
        case SWM_S_FOCUS_MODE:
                if (!strcmp(value, "default"))
                        focus_mode = SWM_FOCUS_DEFAULT;
@@ -4422,6 +4715,15 @@ setconfvalue(char *selector, char *value, int flags)
                if (dialog_ratio > 1.0 || dialog_ratio <= .3)
                        dialog_ratio = .6;
                break;
+       case SWM_S_VERBOSE_LAYOUT:
+               verbose_layout = atoi(value);
+               for (i = 0; layouts[i].l_stack != NULL; i++) {
+                       if (verbose_layout)
+                               layouts[i].l_string = fancy_stacker;
+                       else
+                               layouts[i].l_string = plain_stacker;
+               }
+               break;
        default:
                return (1);
        }
@@ -4458,17 +4760,148 @@ setconfregion(char *selector, char *value, int flags)
        return (0);
 }
 
+int
+setautorun(char *selector, char *value, int flags)
+{
+       int                     ws_id;
+       char                    s[1024];
+       char                    *ap, *sp = s;
+       union arg               a;
+       int                     argc = 0;
+       long                    pid;
+       struct pid_e            *p;
+
+       if (getenv("SWM_STARTED"))
+               return (0);
+
+       bzero(s, sizeof s);
+       if (sscanf(value, "ws[%d]:%1023c", &ws_id, s) != 2)
+               errx(1, "invalid autorun entry, should be 'ws[<idx>]:command'\n");
+       ws_id--;
+       if (ws_id < 0 || ws_id >= SWM_WS_MAX)
+               errx(1, "autorun: invalid workspace %d\n", ws_id + 1);
+
+       /*
+        * This is a little intricate
+        *
+        * If the pid already exists we simply reuse it because it means it was
+        * used before AND not claimed by manage_window.  We get away with
+        * altering it in the parent after INSERT because this can not be a race
+        */
+       a.argv = NULL;
+       while ((ap = strsep(&sp, " \t")) != NULL) {
+               if (*ap == '\0')
+                       continue;
+               DNPRINTF(SWM_D_SPAWN, "setautorun: arg [%s]\n", ap);
+               argc++;
+               if ((a.argv = realloc(a.argv, argc * sizeof(char *))) == NULL)
+                       err(1, "setautorun: realloc");
+               a.argv[argc - 1] = ap;
+       }
+
+       if ((a.argv = realloc(a.argv, (argc + 1) * sizeof(char *))) == NULL)
+               err(1, "setautorun: realloc");
+       a.argv[argc] = NULL;
+
+       if ((pid = fork()) == 0) {
+               spawn(ws_id, &a, 1);
+               /* NOTREACHED */
+               _exit(1);
+       }
+       free(a.argv);
+
+       /* parent */
+       p = find_pid(pid);
+       if (p == NULL) {
+               p = calloc(1, sizeof *p);
+               if (p == NULL)
+                       return (1);
+               TAILQ_INSERT_TAIL(&pidlist, p, entry);
+       }
+
+       p->pid = pid;
+       p->ws = ws_id;
+
+       return (0);
+}
+
+int
+setlayout(char *selector, char *value, int flags)
+{
+       int                     ws_id, i, x, mg, ma, si, raise;
+       int                     st = SWM_V_STACK;
+       char                    s[1024];
+       struct workspace        *ws;
+
+       if (getenv("SWM_STARTED"))
+               return (0);
+
+       bzero(s, sizeof s);
+       if (sscanf(value, "ws[%d]:%d:%d:%d:%d:%1023c",
+           &ws_id, &mg, &ma, &si, &raise, s) != 6)
+               errx(1, "invalid layout entry, should be 'ws[<idx>]:"
+                   "<master_grow>:<master_add>:<stack_inc>:<always_raise>:"
+                   "<type>'\n");
+       ws_id--;
+       if (ws_id < 0 || ws_id >= SWM_WS_MAX)
+               errx(1, "layout: invalid workspace %d\n", ws_id + 1);
+
+       if (!strcasecmp(s, "vertical"))
+               st = SWM_V_STACK;
+       else if (!strcasecmp(s, "horizontal"))
+               st = SWM_H_STACK;
+       else if (!strcasecmp(s, "fullscreen"))
+               st = SWM_MAX_STACK;
+       else
+               errx(1, "invalid layout entry, should be 'ws[<idx>]:"
+                   "<master_grow>:<master_add>:<stack_inc>:<always_raise>:"
+                   "<type>'\n");
+
+       for (i = 0; i < ScreenCount(display); i++) {
+               ws = (struct workspace *)&screens[i].ws;
+               ws[ws_id].cur_layout = &layouts[st];
+
+               ws[ws_id].always_raise = raise;
+               if (st == SWM_MAX_STACK)
+                       continue;
+
+               /* master grow */
+               for (x = 0; x < abs(mg); x++) {
+                       ws[ws_id].cur_layout->l_config(&ws[ws_id],
+                           mg >= 0 ?  SWM_ARG_ID_MASTERGROW :
+                           SWM_ARG_ID_MASTERSHRINK);
+                       stack();
+               }
+               /* master add */
+               for (x = 0; x < abs(ma); x++) {
+                       ws[ws_id].cur_layout->l_config(&ws[ws_id],
+                           ma >= 0 ?  SWM_ARG_ID_MASTERADD :
+                           SWM_ARG_ID_MASTERDEL);
+                       stack();
+               }
+               /* stack inc */
+               for (x = 0; x < abs(si); x++) {
+                       ws[ws_id].cur_layout->l_config(&ws[ws_id],
+                           si >= 0 ?  SWM_ARG_ID_STACKINC :
+                           SWM_ARG_ID_STACKDEC);
+                       stack();
+               }
+       }
+
+       return (0);
+}
+
 /* config options */
 struct config_option {
        char                    *optname;
-       int (*func)(char*, char*, int);
-       int funcflags;
+       int                     (*func)(char*, char*, int);
+       int                     funcflags;
 };
 struct config_option configopt[] = {
        { "bar_enabled",                setconfvalue,   SWM_S_BAR_ENABLED },
        { "bar_at_bottom",              setconfvalue,   SWM_S_BAR_AT_BOTTOM },
        { "bar_border",                 setconfcolor,   SWM_S_COLOR_BAR_BORDER },
-       { "bar_border_width",                   setconfvalue,   SWM_S_BAR_BORDER_WIDTH },
+       { "bar_border_width",           setconfvalue,   SWM_S_BAR_BORDER_WIDTH },
        { "bar_color",                  setconfcolor,   SWM_S_COLOR_BAR },
        { "bar_font_color",             setconfcolor,   SWM_S_COLOR_BAR_FONT },
        { "bar_font",                   setconfvalue,   SWM_S_BAR_FONT },
@@ -4483,6 +4916,7 @@ struct config_option configopt[] = {
        { "cycle_empty",                setconfvalue,   SWM_S_CYCLE_EMPTY },
        { "cycle_visible",              setconfvalue,   SWM_S_CYCLE_VISIBLE },
        { "dialog_ratio",               setconfvalue,   SWM_S_DIALOG_RATIO },
+       { "verbose_layout",             setconfvalue,   SWM_S_VERBOSE_LAYOUT },
        { "modkey",                     setconfmodkey,  0 },
        { "program",                    setconfspawn,   0 },
        { "quirk",                      setconfquirk,   0 },
@@ -4491,12 +4925,15 @@ struct config_option configopt[] = {
        { "screenshot_enabled",         setconfvalue,   SWM_S_SS_ENABLED },
        { "screenshot_app",             setconfvalue,   SWM_S_SS_APP },
        { "window_name_enabled",        setconfvalue,   SWM_S_WINDOW_NAME_ENABLED },
+       { "urgent_enabled",             setconfvalue,   SWM_S_URGENT_ENABLED },
        { "term_width",                 setconfvalue,   SWM_S_TERM_WIDTH },
        { "title_class_enabled",        setconfvalue,   SWM_S_TITLE_CLASS_ENABLED },
        { "title_name_enabled",         setconfvalue,   SWM_S_TITLE_NAME_ENABLED },
-       { "focus_mode",                 setconfvalue,   SWM_S_FOCUS_MODE },
-       { "disable_border",             setconfvalue,   SWM_S_DISABLE_BORDER },
-       { "border_width",               setconfvalue,   SWM_S_BORDER_WIDTH },
+       { "focus_mode",                 setconfvalue,   SWM_S_FOCUS_MODE },
+       { "disable_border",             setconfvalue,   SWM_S_DISABLE_BORDER },
+       { "border_width",               setconfvalue,   SWM_S_BORDER_WIDTH },
+       { "autorun",                    setautorun,     0 },
+       { "layout",                     setlayout,      0 },
 };
 
 
@@ -4597,13 +5034,98 @@ 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);
+}
+
+long
+window_get_pid(Window win)
+{
+       Atom                    actual_type_return;
+       int                     actual_format_return = 0;
+       unsigned long           nitems_return = 0;
+       unsigned long           bytes_after_return = 0;
+       long                    *pid = NULL;
+       long                    ret = 0;
+       const char              *errstr;
+       unsigned char           *prop = NULL;
+
+       if (XGetWindowProperty(display, win,
+           XInternAtom(display, "_NET_WM_PID", False), 0, 1, False,
+           XA_CARDINAL, &actual_type_return, &actual_format_return,
+           &nitems_return, &bytes_after_return,
+           (unsigned char**)(void*)&pid) != Success)
+               goto tryharder;
+       if (actual_type_return != XA_CARDINAL)
+               goto tryharder;
+       if (pid == NULL)
+               goto tryharder;
+
+       ret = *pid;
+       XFree(pid);
+
+       return (ret);
+
+tryharder:
+       if (XGetWindowProperty(display, win,
+           XInternAtom(display, "_SWM_PID", False), 0, SWM_PROPLEN, False,
+           XA_STRING, &actual_type_return, &actual_format_return,
+           &nitems_return, &bytes_after_return, &prop) != Success)
+               return (0);
+       if (actual_type_return != XA_STRING)
+               return (0);
+       if (prop == NULL)
+               return (0);
+
+       ret = strtonum((const char *)prop, 0, UINT_MAX, &errstr);
+       /* ignore error because strtonum returns 0 anyway */
+       XFree(prop);
+
+       return (ret);
 }
 
 struct ws_win *
@@ -4621,6 +5143,7 @@ manage_window(Window id)
        long                    mask;
        const char              *errstr;
        XWindowChanges          wc;
+       struct pid_e            *p;
 
        if ((win = find_window(id)) != NULL)
                return (win);   /* already being managed */
@@ -4630,9 +5153,9 @@ manage_window(Window id)
                DNPRINTF(SWM_D_MISC, "manage previously unmanaged window "
                    "%lu\n", win->id);
                TAILQ_REMOVE(&win->ws->unmanagedlist, win, entry);
-               if (win->transient)
-                       set_child_transient(win);
-               if (trans && (ww = find_window(trans)))
+               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);
@@ -4645,17 +5168,22 @@ manage_window(Window id)
 
        win->id = id;
 
+       /* see if we need to override the workspace */
+       p = find_pid(window_get_pid(id));
+
        /* Get all the window data in one shot */
        ws_idx_atom = XInternAtom(display, "_SWM_WS", False);
-       if (ws_idx_atom)
+       if (ws_idx_atom) {
                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);
+       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);
        }
@@ -4680,9 +5208,14 @@ manage_window(Window id)
         * transient, * put it in the same workspace
         */
        r = root_to_region(win->wa.root);
-       if (prop && win->transient == 0) {
+       if (p) {
+               ws = &r->s->ws[p->ws];
+               TAILQ_REMOVE(&pidlist, p, entry);
+               free(p);
+               p = NULL;
+       } else if (prop && win->transient == 0) {
                DNPRINTF(SWM_D_PROP, "got property _SWM_WS=%s\n", prop);
-               ws_idx = strtonum(prop, 0, 9, &errstr);
+               ws_idx = strtonum((const char *)prop, 0, 9, &errstr);
                if (errstr) {
                        DNPRINTF(SWM_D_EVENT, "window idx is %s: %s",
                            errstr, prop);
@@ -4723,7 +5256,8 @@ manage_window(Window id)
 
        /* Set window properties so we can remember this after reincarnation */
        if (ws_idx_atom && prop == NULL &&
-           snprintf(ws_idx_str, SWM_PROPLEN, "%d", ws->idx) < SWM_PROPLEN) {
+           snprintf((char *)ws_idx_str, SWM_PROPLEN, "%d", ws->idx) <
+               SWM_PROPLEN) {
                DNPRINTF(SWM_D_PROP, "setting property _SWM_WS to %s\n",
                    ws_idx_str);
                XChangeProperty(display, win->id, ws_idx_atom, XA_STRING, 8,
@@ -4910,8 +5444,8 @@ keypress(XEvent *e)
        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) {
+                   && 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),
@@ -4935,7 +5469,6 @@ buttonpress(XEvent *e)
 
        DNPRINTF(SWM_D_EVENT, "buttonpress: window: %lu\n", ev->window);
 
-       action = root_click;
        if ((win = find_window(ev->window)) == NULL)
                return;
 
@@ -4995,6 +5528,8 @@ configurenotify(XEvent *e)
                adjust_font(win);
                if (font_adjusted)
                        stack();
+               if (focus_mode == SWM_FOCUS_DEFAULT)
+                       drain_enter_notify();
        }
 }
 
@@ -5018,6 +5553,8 @@ destroynotify(XEvent *e)
 
        unmanage_window(win);
        stack();
+       if (focus_mode == SWM_FOCUS_DEFAULT)
+               drain_enter_notify();
        free_window(win);
 }
 
@@ -5304,6 +5841,9 @@ unmapnotify(XEvent *e)
                /* resend unmap because we ated it */
                XUnmapWindow(display, e->xunmap.window);
        }
+
+       if (focus_mode == SWM_FOCUS_DEFAULT)
+               drain_enter_notify();
 }
 
 void
@@ -5348,7 +5888,8 @@ clientmessage(XEvent *e)
                        XKillClient(display, win->id);
        }
        if (ev->message_type == ewmh[_NET_MOVERESIZE_WINDOW].atom) {
-               DNPRINTF(SWM_D_EVENT, "clientmessage: _NET_MOVERESIZE_WINDOW \n");
+               DNPRINTF(SWM_D_EVENT,
+                   "clientmessage: _NET_MOVERESIZE_WINDOW \n");
                if (win->floating) {
                        if (ev->data.l[0] & (1<<8)) /* x */
                                win->g.x = ev->data.l[1];
@@ -5368,7 +5909,8 @@ clientmessage(XEvent *e)
                DNPRINTF(SWM_D_EVENT, "clientmessage: _NET_WM_STATE \n");
                ewmh_update_win_state(win, ev->data.l[1], ev->data.l[0]);
                if (ev->data.l[2])
-                       ewmh_update_win_state(win, ev->data.l[2], ev->data.l[0]);
+                       ewmh_update_win_state(win, ev->data.l[2],
+                           ev->data.l[0]);
 
                stack();
        }
@@ -5565,6 +6107,8 @@ screenchange(XEvent *e) {
                TAILQ_FOREACH(r, &screens[i].rl, entry)
                        bar_setup(r);
        stack();
+       if (focus_mode == SWM_FOCUS_DEFAULT)
+               drain_enter_notify();
 }
 
 void
@@ -5618,14 +6162,11 @@ setup_screens(void)
        int                     i, j, k;
        int                     errorbase, major, minor;
        struct workspace        *ws;
-       int                     ws_idx_atom;
 
        if ((screens = calloc(ScreenCount(display),
             sizeof(struct swm_screen))) == NULL)
                errx(1, "calloc: screens");
 
-       ws_idx_atom = XInternAtom(display, "_SWM_WS", False);
-
        /* initial Xrandr setup */
        xrandr_support = XRRQueryExtension(display,
            &xrandr_eventbase, &errorbase);
@@ -5668,6 +6209,7 @@ setup_screens(void)
                                        layouts[k].l_config(ws,
                                            SWM_ARG_ID_STACKINIT);
                        ws->cur_layout = &layouts[0];
+                       ws->cur_layout->l_string(ws);
                }
 
                scan_xrandr(i);
@@ -5801,10 +6343,12 @@ main(int argc, char *argv[])
        setup_ewmh();
        /* set some values to work around bad programs */
        workaround();
-
        /* grab existing windows (before we build the bars) */
        grab_windows();
 
+       if (getenv("SWM_STARTED") == NULL)
+               setenv("SWM_STARTED", "YES", 1);
+
        /* setup all bars */
        for (i = 0; i < ScreenCount(display); i++)
                TAILQ_FOREACH(r, &screens[i].rl, entry) {
@@ -5817,6 +6361,8 @@ main(int argc, char *argv[])
 
        grabkeys();
        stack();
+       if (focus_mode == SWM_FOCUS_DEFAULT)
+               drain_enter_notify();
 
        xfd = ConnectionNumber(display);
        while (running) {