]> code.delx.au - spectrwm/blobdiff - scrotwm.c
Add clock format from merdely
[spectrwm] / scrotwm.c
index 3f1cbddd1f2026414be4b7d404b4eb20c60409e0..29029359553aa69b1f02a2ea70a3a90807626df5 100644 (file)
--- a/scrotwm.c
+++ b/scrotwm.c
@@ -50,7 +50,7 @@
 
 static const char      *cvstag = "$scrotwm$";
 
-#define        SWM_VERSION     "0.6"
+#define        SWM_VERSION     "0.9"
 
 #include <stdio.h>
 #include <stdlib.h>
@@ -128,10 +128,11 @@ u_int32_t         swm_debug = 0
 #define BUTTONMASK             (ButtonPressMask|ButtonReleaseMask)
 #define MOUSEMASK              (BUTTONMASK|PointerMotionMask)
 #define SWM_PROPLEN            (16)
-#define X(r)                   (r)->g.x        
+#define X(r)                   (r)->g.x
 #define Y(r)                   (r)->g.y
-#define WIDTH(r)               (r)->g.w        
+#define WIDTH(r)               (r)->g.w
 #define HEIGHT(r)              (r)->g.h
+#define SWM_MAX_FONT_STEPS     (3)
 
 #ifndef SWM_LIB
 #define SWM_LIB                        "/usr/X11R6/lib/swmhack.so"
@@ -139,10 +140,13 @@ u_int32_t         swm_debug = 0
 
 char                   **start_argv;
 Atom                   astate;
+Atom                   aprot;
+Atom                   adelete;
 int                    (*xerrorxlib)(Display *, XErrorEvent *);
 int                    other_wm;
 int                    running = 1;
 int                    ss_enabled = 0;
+int                    xrandr_support;
 int                    xrandr_eventbase;
 int                    ignore_enter = 0;
 unsigned int           numlockmask = 0;
@@ -150,11 +154,13 @@ Display                   *display;
 
 int                    cycle_empty = 0;
 int                    cycle_visible = 0;
+int                    term_width = 0;
+int                    font_adjusted = 0;
 
 /* dialog windows */
 double                 dialog_ratio = .6;
 /* status bar */
-#define SWM_BAR_MAX    (128)
+#define SWM_BAR_MAX    (256)
 char                   *bar_argv[] = { NULL, NULL };
 int                    bar_pipe[2];
 char                   bar_ext[SWM_BAR_MAX];
@@ -167,13 +173,14 @@ int                       bar_extra = 1;
 int                    bar_extra_running = 0;
 int                    bar_verbose = 1;
 int                    bar_height = 0;
+int                    clock_enabled = 1;
 pid_t                  bar_pid;
 GC                     bar_gc;
 XGCValues              bar_gcv;
 int                    bar_fidx = 0;
 XFontStruct            *bar_fs;
 char                   *bar_fonts[] = {
-                           "-*-terminus-*-*-*-*-*-*-*-*-*-*-*-*",
+                           "-*-terminus-medium-*-*-*-*-*-*-*-*-*-*-*",
                            "-*-times-medium-r-*-*-*-*-*-*-*-*-*-*",
                            NULL
 };
@@ -181,6 +188,8 @@ char                        *bar_fonts[] = {
 /* terminal + args */
 char                   *spawn_term[] = { "xterm", NULL };
 char                   *spawn_screenshot[] = { "screenshot.sh", NULL, NULL };
+char                   *spawn_lock[] = { "xlock", NULL };
+char                   *spawn_initscr[] = { "initscreen.sh", NULL };
 char                   *spawn_menu[] = { "dmenu_run", "-fn", NULL, "-nb", NULL,
                            "-nf", NULL, "-sb", NULL, "-sf", NULL, NULL };
 
@@ -205,9 +214,9 @@ struct workspace;
 struct swm_region {
        TAILQ_ENTRY(swm_region) entry;
        struct swm_geometry     g;
-       Window                  bar_window;
        struct workspace        *ws;    /* current workspace on this region */
        struct swm_screen       *s;     /* screen idx */
+       Window                  bar_window;
 }; 
 TAILQ_HEAD(swm_region_list, swm_region);
 
@@ -219,6 +228,11 @@ struct ws_win {
        int                     floating;
        int                     transient;
        int                     manual;
+       int                     font_size_boundary[SWM_MAX_FONT_STEPS];
+       int                     font_steps;
+       int                     last_inc;
+       int                     can_delete;
+       unsigned long           quirks;
        struct workspace        *ws;    /* always valid */
        struct swm_screen       *s;     /* always valid, never changes */
        XWindowAttributes       wa;
@@ -236,6 +250,8 @@ void        horizontal_stack(struct workspace *, struct swm_geometry *);
 void   max_stack(struct workspace *, struct swm_geometry *);
 
 void   grabbuttons(struct ws_win *, int);
+void   new_region(struct swm_screen *, int, int, int, int);
+void   update_modkey(unsigned int);
 
 struct layout {
        void            (*l_stack)(struct workspace *, struct swm_geometry *);
@@ -257,6 +273,7 @@ struct workspace {
        int                     restack;        /* restack on switch */
        struct layout           *cur_layout;    /* current layout handlers */
        struct ws_win           *focus;         /* may be NULL */
+       struct ws_win           *focus_prev;    /* may be NULL */
        struct swm_region       *r;             /* may be NULL */
        struct ws_win_list      winlist;        /* list of windows in ws */
 
@@ -264,8 +281,10 @@ struct workspace {
        struct {
                                int horizontal_msize;
                                int horizontal_mwin;
+                               int horizontal_stacks;
                                int vertical_msize;
                                int vertical_mwin;
+                               int vertical_stacks;
        } l_state;
 };
 
@@ -277,8 +296,8 @@ enum        { SWM_S_COLOR_BAR, SWM_S_COLOR_BAR_BORDER, SWM_S_COLOR_BAR_FONT,
 struct swm_screen {
        int                     idx;            /* screen index */
        struct swm_region_list  rl;     /* list of regions on this screen */
+       struct swm_region_list  orl;    /* list of old regions */
        Window                  root;
-       int                     xrandr_support;
        struct workspace        ws[SWM_WS_MAX];
 
        /* colors */
@@ -311,10 +330,14 @@ union arg {
 #define SWM_ARG_ID_CYCLEWS_DOWN        (13)
 #define SWM_ARG_ID_CYCLESC_UP  (14)
 #define SWM_ARG_ID_CYCLESC_DOWN        (15)
+#define SWM_ARG_ID_STACKINC    (16)
+#define SWM_ARG_ID_STACKDEC    (17)
 #define SWM_ARG_ID_SS_ALL      (0)
 #define SWM_ARG_ID_SS_WINDOW   (1)
 #define SWM_ARG_ID_DONTCENTER  (0)
 #define SWM_ARG_ID_CENTER      (1)
+#define SWM_ARG_ID_KILLWINDOW  (0)
+#define SWM_ARG_ID_DELETEWINDOW        (1)
        char                    **argv;
 };
 
@@ -323,11 +346,26 @@ struct quirk {
        char                    *class;
        char                    *name;
        unsigned long           quirk;
-#define SWM_Q_FLOAT            (1<<0)
+#define SWM_Q_FLOAT            (1<<0)  /* float this window */
+#define SWM_Q_TRANSSZ          (1<<1)  /* transiend window size too small */
+#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 */
 } quirks[] = {
-       { "MPlayer",            "xv",           SWM_Q_FLOAT },
+       { "MPlayer",            "xv",           SWM_Q_FLOAT | SWM_Q_FULLSCREEN },
        { "OpenOffice.org 2.4", "VCLSalFrame",  SWM_Q_FLOAT },
-       { NULL,         NULL,           0},
+       { "OpenOffice.org 3.0", "VCLSalFrame",  SWM_Q_FLOAT },
+       { "Firefox-bin",        "firefox-bin",  SWM_Q_TRANSSZ },
+       { "Firefox",            "Dialog",       SWM_Q_FLOAT },
+       { "Gimp",               "gimp",         SWM_Q_FLOAT | SWM_Q_ANYWHERE },
+       { "XTerm",              "xterm",        SWM_Q_XTERM_FONTADJ },
+       { "xine",               "Xine Window",  SWM_Q_FLOAT | SWM_Q_ANYWHERE },
+       { "Xitk",               "Xitk Combo",   SWM_Q_FLOAT | SWM_Q_ANYWHERE },
+       { "xine",               "xine Panel",   SWM_Q_FLOAT | SWM_Q_ANYWHERE },
+       { "Xitk",               "Xine Window",  SWM_Q_FLOAT | SWM_Q_ANYWHERE },
+       { "xine",               "xine Video Fullscreen Window", SWM_Q_FULLSCREEN | SWM_Q_FLOAT },
+       { "pcb",                "pcb",          SWM_Q_FLOAT },
+       { NULL,                 NULL,           0},
 };
 
 /* events */
@@ -403,6 +441,33 @@ setscreencolor(char *val, int i, int c)
                    i, ScreenCount(display));
 }
 
+void
+custom_region(char *val)
+{
+       unsigned int                    sidx, x, y, w, h;
+
+       if (sscanf(val, "screen[%u]:%ux%u+%u+%u", &sidx, &w, &h, &x, &y) != 5)
+               errx(1, "invalid custom region, "
+                   "should be 'screen[<n>]:<n>x<n>+<n>+<n>\n");
+       if (sidx < 1 || sidx > ScreenCount(display))
+               errx(1, "invalid screen index: %d out of bounds (maximum %d)\n",
+                   sidx, ScreenCount(display));
+       sidx--;
+
+       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) ||
+           w + x > DisplayWidth(display, sidx) ||
+           h + y > DisplayHeight(display, sidx))
+               errx(1, "region %ux%u+%u+%u not within screen boundaries "
+                   "(%ux%u)\n", w, h, x, y,
+                   DisplayWidth(display, sidx), DisplayHeight(display, sidx));
+           
+       new_region(&screens[sidx], x, y, w, h);
+}
+
 int
 varmatch(char *var, char *name, int *index)
 {
@@ -439,6 +504,7 @@ conf_load(char *filename)
        char                    *line, *cp, *var, *val;
        size_t                  len, lineno = 0;
        int                     i, sc;
+       unsigned int            modkey;
 
        DNPRINTF(SWM_D_MISC, "conf_load: filename %s\n", filename);
 
@@ -487,7 +553,9 @@ conf_load(char *filename)
                        break;
 
                case 'c':
-                       if (!varmatch(var, "color_focus", &i))
+                       if (!strncmp(var, "clock_enabled", strlen("clock_enabled")))
+                               clock_enabled = atoi(val);
+                       else if (!varmatch(var, "color_focus", &i))
                                setscreencolor(val, i, SWM_S_COLOR_FOCUS);
                        else if (!varmatch(var, "color_unfocus", &i))
                                setscreencolor(val, i, SWM_S_COLOR_UNFOCUS);
@@ -509,16 +577,49 @@ conf_load(char *filename)
                                goto bad;
                        break;
 
+               case 'm':
+                       if (!strncmp(var, "modkey", strlen("modkey"))) {
+                               modkey = MODKEY;
+                               if (!strncmp(val, "Mod2", strlen("Mod2")))
+                                       modkey = Mod2Mask;
+                               else if (!strncmp(val, "Mod3", strlen("Mod3")))
+                                       modkey = Mod3Mask;
+                               else if (!strncmp(val, "Mod4", strlen("Mod4")))
+                                       modkey = Mod4Mask;
+                               else
+                                       modkey = Mod1Mask;
+                               update_modkey(modkey);
+                       } else
+                               goto bad;
+                       break;
+
+               case 'r':
+                       if (!strncmp(var, "region", strlen("region")))
+                               custom_region(val);
+                       else
+                               goto bad;
+                       break;
+
                case 's':
                        if (!strncmp(var, "spawn_term", strlen("spawn_term")))
                                asprintf(&spawn_term[0], "%s", val);
-                       if (!strncmp(var, "screenshot_enabled",
+                       else if (!strncmp(var, "screenshot_enabled",
                            strlen("screenshot_enabled")))
                                ss_enabled = atoi(val);
-                       if (!strncmp(var, "screenshot_app",
+                       else if (!strncmp(var, "screenshot_app",
                            strlen("screenshot_app")))
                                asprintf(&spawn_screenshot[0], "%s", val);
+                       else
+                               goto bad;
                        break;
+
+               case 't':
+                       if (!strncmp(var, "term_width", strlen("term_width")))
+                               term_width = atoi(val);
+                       else
+                               goto bad;
+                       break;
+
                default:
                        goto bad;
                }
@@ -582,7 +683,6 @@ bar_update(void)
 
        if (bar_enabled == 0)
                return;
-
        if (bar_extra && bar_extra_running) {
                /* ignore short reads; it'll correct itself */
                while ((b = fgetln(stdin, &len)) != NULL)
@@ -598,14 +698,18 @@ bar_update(void)
        } else
                strlcpy(bar_ext, "", sizeof bar_ext);
 
-       time(&tmt);
-       localtime_r(&tmt, &tm);
-       strftime(s, sizeof s, "%a %b %d %R %Z %Y", &tm);
+       if (clock_enabled == 0)
+               strlcpy(s, "", sizeof s);
+       else {
+               time(&tmt);
+               localtime_r(&tmt, &tm);
+               strftime(s, sizeof s, "%a %b %d %R %Z %Y    ", &tm);
+       }
        for (i = 0; i < ScreenCount(display); i++) {
                x = 1;
                TAILQ_FOREACH(r, &screens[i].rl, entry) {
-                       snprintf(loc, sizeof loc, "%s     %d:%d    %s    %s",
-                           s, x++, r->ws->idx + 1, bar_ext, bar_vertext);
+                       snprintf(loc, sizeof loc, "%d:%d    %s%s    %s",
+                           x++, r->ws->idx + 1, s, bar_ext, bar_vertext);
                        bar_print(r, loc);
                }
        }
@@ -627,26 +731,23 @@ bar_toggle(struct swm_region *r, union arg *args)
 
        DNPRINTF(SWM_D_MISC, "bar_toggle\n");
 
-       if (bar_enabled) {
+       if (bar_enabled)
                for (i = 0; i < sc; i++)
                        TAILQ_FOREACH(tmpr, &screens[i].rl, entry)
                                XUnmapWindow(display, tmpr->bar_window);
-       } else {
+       else
                for (i = 0; i < sc; i++)
                        TAILQ_FOREACH(tmpr, &screens[i].rl, entry)
                                XMapRaised(display, tmpr->bar_window);
-       }
+
        bar_enabled = !bar_enabled;
-       XSync(display, False);
        for (i = 0; i < sc; i++)
                for (j = 0; j < SWM_WS_MAX; j++)
                        screens[i].ws[j].restack = 1;
 
        stack();
        /* must be after stack */
-       for (i = 0; i < sc; i++)
-               TAILQ_FOREACH(tmpr, &screens[i].rl, entry)
-                       bar_update();
+       bar_update();
 }
 
 void
@@ -741,6 +842,21 @@ version(struct swm_region *r, union arg *args)
        bar_update();
 }
 
+void
+client_msg(struct ws_win *win, Atom a)
+{
+       XClientMessageEvent     cm;
+
+       bzero(&cm, sizeof cm);
+       cm.type = ClientMessage;
+       cm.window = win->id;
+       cm.message_type = aprot;
+       cm.format = 32;
+       cm.data.l[0] = a;
+       cm.data.l[1] = CurrentTime;
+       XSendEvent(display, win->id, False, 0L, (XEvent *)&cm);
+}
+
 void
 config_win(struct ws_win *win)
 {
@@ -799,6 +915,34 @@ unmap_all(void)
                                XUnmapWindow(display, win->id);
 }
 
+void
+fake_keypress(struct ws_win *win, int keysym, int modifiers)
+{
+       XKeyEvent event;
+
+       event.display = display;        /* Ignored, but what the hell */
+       event.window = win->id;
+       event.root = win->s->root;
+       event.subwindow = None;
+       event.time = CurrentTime;
+       event.x = win->g.x;
+       event.y = win->g.y;
+       event.x_root = 1;
+       event.y_root = 1;
+       event.same_screen = True;
+       event.keycode = XKeysymToKeycode(display, keysym);
+       event.state = modifiers;
+
+       event.type = KeyPress;
+       XSendEvent(event.display, event.window, True,
+           KeyPressMask, (XEvent *)&event);
+
+       event.type = KeyRelease;
+       XSendEvent(event.display, event.window, True,
+           KeyPressMask, (XEvent *)&event);
+
+}
+
 void
 restart(struct swm_region *r, union arg *args)
 {
@@ -822,7 +966,7 @@ restart(struct swm_region *r, union arg *args)
 struct swm_region *
 root_to_region(Window root)
 {
-       struct swm_region       *r;
+       struct swm_region       *r = NULL;
        Window                  rr, cr;
        int                     i, x, y, wx, wy;
        unsigned int            mask;
@@ -832,20 +976,17 @@ root_to_region(Window root)
                        break;
 
        if (XQueryPointer(display, screens[i].root, 
-           &rr, &cr, &x, &y, &wx, &wy, &mask) == False) {
-               /* if we can't query the pointer, grab the first region */
-               r = TAILQ_FIRST(&screens[i].rl);
-       } else {
-               /* otherwise, choose a region based on pointer location */
-               TAILQ_FOREACH(r, &screens[i].rl, entry) {
+           &rr, &cr, &x, &y, &wx, &wy, &mask) != False) {
+               /* choose a region based on pointer location */
+               TAILQ_FOREACH(r, &screens[i].rl, entry)
                        if (x >= X(r) && x <= X(r) + WIDTH(r) &&
                            y >= Y(r) && y <= Y(r) + HEIGHT(r))
                                break;
-               }
-
-               if (r == NULL)
-                       r = TAILQ_FIRST(&screens[i].rl);
        }
+
+       if (r == NULL)
+               r = TAILQ_FIRST(&screens[i].rl);
+
        return (r);
 }
 
@@ -903,6 +1044,16 @@ spawn(struct swm_region *r, union arg *args)
        wait(0);
 }
 
+void
+spawnterm(struct swm_region *r, union arg *args)
+{
+       DNPRINTF(SWM_D_MISC, "spawnterm\n");
+
+       if (term_width)
+               setenv("_SWM_XTERM_FONTADJ", "", 1);
+       spawn(r, args);
+}
+
 void
 spawnmenu(struct swm_region *r, union arg *args)
 {
@@ -947,6 +1098,9 @@ focus_win(struct ws_win *win)
        if (win == NULL)
                return;
 
+       if (win->ws->focus != win && win->ws->focus != NULL)
+               win->ws->focus_prev = win->ws->focus;
+
        unfocus_all();
        win->ws->focus = win;
        if (win->ws->r != NULL) {
@@ -974,7 +1128,7 @@ switchws(struct swm_region *r, union arg *args)
        old_ws = this_r->ws;
        new_ws = &this_r->s->ws[wsid];
 
-       DNPRINTF(SWM_D_WS, "switchws screen %d region %dx%d+%d+%d: "
+       DNPRINTF(SWM_D_WS, "switchws screen[%d]:%dx%d+%d+%d: "
            "%d -> %d\n", r->s->idx, WIDTH(r), HEIGHT(r), X(r), Y(r),
            old_ws->idx, wsid);
 
@@ -985,13 +1139,14 @@ switchws(struct swm_region *r, union arg *args)
        if (!other_r) {
                /* if the other workspace is hidden, switch windows */
                /* map new window first to prevent ugly blinking */
+               old_ws->r = NULL;
+               old_ws->restack = 1;
+
                TAILQ_FOREACH(win, &new_ws->winlist, entry)
                        XMapRaised(display, win->id);
 
                TAILQ_FOREACH(win, &old_ws->winlist, entry)
                        XUnmapWindow(display, win->id);
-               old_ws->r = NULL;
-               old_ws->restack = 1;
        } else {
                other_r->ws = old_ws;
                old_ws->r = other_r;
@@ -1014,7 +1169,7 @@ cyclews(struct swm_region *r, union arg *args)
        struct swm_screen       *s = r->s;
 
        DNPRINTF(SWM_D_WS, "cyclews id %d "
-           "in screen %d region %dx%d+%d+%d ws %d\n", args->id,
+           "in screen[%d]:%dx%d+%d+%d ws %d\n", args->id,
            r->s->idx, WIDTH(r), HEIGHT(r), X(r), Y(r), r->ws->idx);
 
        a.id = r->ws->idx;
@@ -1075,7 +1230,7 @@ cyclescr(struct swm_region *r, union arg *args)
 void
 swapwin(struct swm_region *r, union arg *args)
 {
-       struct ws_win           *target;
+       struct ws_win           *target, *source;
        struct ws_win_list      *wl;
 
 
@@ -1085,40 +1240,48 @@ swapwin(struct swm_region *r, union arg *args)
        if (cur_focus == NULL)
                return;
 
-       wl = &cur_focus->ws->winlist;
+       source = cur_focus;
+       wl = &source->ws->winlist;
 
        switch (args->id) {
        case SWM_ARG_ID_SWAPPREV:
-               target = TAILQ_PREV(cur_focus, ws_win_list, entry);
+               target = TAILQ_PREV(source, ws_win_list, entry);
                TAILQ_REMOVE(wl, cur_focus, entry);
                if (target == NULL)
-                       TAILQ_INSERT_TAIL(wl, cur_focus, entry);
+                       TAILQ_INSERT_TAIL(wl, source, entry);
                else
-                       TAILQ_INSERT_BEFORE(target, cur_focus, entry);
+                       TAILQ_INSERT_BEFORE(target, source, entry);
                break;
        case SWM_ARG_ID_SWAPNEXT: 
-               target = TAILQ_NEXT(cur_focus, entry);
-               TAILQ_REMOVE(wl, cur_focus, entry);
+               target = TAILQ_NEXT(source, entry);
+               TAILQ_REMOVE(wl, source, entry);
                if (target == NULL)
-                       TAILQ_INSERT_HEAD(wl, cur_focus, entry);
+                       TAILQ_INSERT_HEAD(wl, source, entry);
                else
-                       TAILQ_INSERT_AFTER(wl, target, cur_focus, entry);
+                       TAILQ_INSERT_AFTER(wl, target, source, entry);
                break;
        case SWM_ARG_ID_SWAPMAIN:
                target = TAILQ_FIRST(wl);
-               if (target == cur_focus)
-                       return;
+               if (target == source) {
+                       if (source->ws->focus_prev != NULL &&
+                           source->ws->focus_prev != target)  
+                                
+                               source = source->ws->focus_prev;
+                       else
+                               return;
+                }
+               source->ws->focus_prev = target;
                TAILQ_REMOVE(wl, target, entry);
-               TAILQ_INSERT_BEFORE(cur_focus, target, entry);
-               TAILQ_REMOVE(wl, cur_focus, entry);
-               TAILQ_INSERT_HEAD(wl, cur_focus, entry);
+               TAILQ_INSERT_BEFORE(source, target, entry);
+               TAILQ_REMOVE(wl, source, entry);
+               TAILQ_INSERT_HEAD(wl, source, entry);
                break;
        default:
                DNPRINTF(SWM_D_MOVE, "invalid id: %d\n", args->id);
                return;
        }
 
-       ignore_enter = 2;
+       ignore_enter = 1;
        stack();
 }
 
@@ -1151,6 +1314,10 @@ focus(struct swm_region *r, union arg *args)
 
        case SWM_ARG_ID_FOCUSMAIN:
                winfocus = TAILQ_FIRST(wl);
+               if (winfocus == cur_focus)
+                       winfocus = cur_focus->ws->focus_prev;
+               if (winfocus == NULL)
+                       return;
                break;
 
        default:
@@ -1221,6 +1388,8 @@ stack(void) {
                        r->ws->cur_layout->l_stack(r->ws, &g);
                }
        }
+       if (font_adjusted)
+               font_adjusted--;
        XSync(display, False);
 }
 
@@ -1232,8 +1401,12 @@ stack_floater(struct ws_win *win, struct swm_region *r)
 
        bzero(&wc, sizeof wc);
        mask = CWX | CWY | CWBorderWidth | CWWidth | CWHeight;
-       wc.border_width = 1;
-       if (win->transient) {
+       if ((win->quirks & SWM_Q_FULLSCREEN) && (win->g.w == WIDTH(r)) &&
+           (win->g.h == HEIGHT(r)))
+               wc.border_width = 0;
+       else
+               wc.border_width = 1;
+       if (win->transient && (win->quirks & SWM_Q_TRANSSZ)) {
                win->g.w = (double)WIDTH(r) * dialog_ratio;
                win->g.h = (double)HEIGHT(r) * dialog_ratio;
        }
@@ -1253,6 +1426,35 @@ stack_floater(struct ws_win *win, struct swm_region *r)
        XConfigureWindow(display, win->id, mask, &wc);
 }
 
+/*
+ * Send keystrokes to terminal to decrease/increase the font size as the
+ * window size changes.
+ */
+void
+adjust_font(struct ws_win *win)
+{
+       if (!(win->quirks & SWM_Q_XTERM_FONTADJ) ||
+           win->floating || win->transient)
+               return;
+       
+       if (win->sh.width_inc && win->last_inc != win->sh.width_inc &&
+           win->g.w / win->sh.width_inc < term_width &&
+           win->font_steps < SWM_MAX_FONT_STEPS) {
+               win->font_size_boundary[win->font_steps] =
+                   (win->sh.width_inc * term_width) + win->sh.base_width;
+               win->font_steps++;
+               font_adjusted++;
+               win->last_inc = win->sh.width_inc;
+               fake_keypress(win, XK_KP_Subtract, ShiftMask);
+       } else if (win->font_steps && win->last_inc != win->sh.width_inc &&
+           win->g.w > win->font_size_boundary[win->font_steps - 1]) {
+               win->font_steps--;
+               font_adjusted++;
+               win->last_inc = win->sh.width_inc;
+               fake_keypress(win, XK_KP_Add, ShiftMask);
+       }
+}
+
 #define SWAPXY(g)      do {                            \
        int tmp;                                        \
        tmp = (g)->y; (g)->y = (g)->x; (g)->x = tmp;    \
@@ -1264,8 +1466,9 @@ stack_master(struct workspace *ws, struct swm_geometry *g, int rot, int flip)
        XWindowChanges          wc;
        struct swm_geometry     win_g, r_g = *g;
        struct ws_win           *win, *winfocus;
-       int                     i, j, w_inc, h_inc, w_base, h_base;
-       int                     hrh, extra, h_slice, last_h = 0;
+       int                     i, j, s, stacks; 
+       int                     w_inc = 1, h_inc, w_base = 1, h_base;
+       int                     hrh, extra = 0, h_slice, last_h = 0;
        int                     split, colno, winno, mwin, msize, mscale;
        int                     remain, missing, v_slice;;
        unsigned int            mask;
@@ -1280,69 +1483,84 @@ stack_master(struct workspace *ws, struct swm_geometry *g, int rot, int flip)
                ws->focus = TAILQ_FIRST(&ws->winlist);
        winfocus = cur_focus ? cur_focus : ws->focus;
 
-       win = TAILQ_FIRST(&ws->winlist);
+       TAILQ_FOREACH(win, &ws->winlist, entry)
+               if (win->transient == 0 && win->floating == 0)
+                       break;
+
+       if (win == NULL)
+               goto notiles;
+
        if (rot) {
                w_inc = win->sh.width_inc;
                w_base = win->sh.base_width;
                mwin = ws->l_state.horizontal_mwin;
                mscale = ws->l_state.horizontal_msize;
+               stacks = ws->l_state.horizontal_stacks;
                SWAPXY(&r_g);
        } else {
                w_inc = win->sh.height_inc;
                w_base = win->sh.base_height;
                mwin = ws->l_state.vertical_mwin;
                mscale = ws->l_state.vertical_msize;
+               stacks = ws->l_state.vertical_stacks;
        }
        win_g = r_g;
 
+       if (stacks > winno - mwin)
+               stacks = winno - mwin;
+       if (stacks < 1)
+               stacks = 1;
+
        h_slice = r_g.h / SWM_H_SLICE;
        if (mwin && winno > mwin) {
                v_slice = r_g.w / SWM_V_SLICE;
 
                split = mwin;
                colno = split;
-               msize = v_slice * mscale;
+               win_g.w = v_slice * mscale;
 
                if (w_inc > 1 && w_inc < v_slice) {
                        /* adjust for window's requested size increment */
                        remain = (win_g.w - w_base) % w_inc;
                        missing = w_inc - remain;
-
-                       if (missing <= extra || j == 0) {
-                               extra -= missing;
-                               win_g.w += missing;
-                       } else {
-                               win_g.w -= remain;
-                               extra += remain;
-                       }
+                       win_g.w -= remain;
+                       extra += remain;
                }
 
-               win_g.w = msize;
+               msize = win_g.w;
                if (flip) 
                        win_g.x += r_g.w - msize;
        } else {
-               colno = winno;
-               split = 0;
+               msize = -2;
+               colno = split = winno / stacks;
+               win_g.w = ((r_g.w - (stacks * 2) + 2) / stacks);
        }
        hrh = r_g.h / colno;
        extra = r_g.h - (colno * hrh);
        win_g.h = hrh - 2;
 
        /*  stack all the tiled windows */
-       i = j = 0;
+       i = j = 0, s = stacks;
        TAILQ_FOREACH(win, &ws->winlist, entry) {
                if (win->transient != 0 || win->floating != 0)
                        continue;
 
                if (split && i == split) {
-                       colno = winno - split;
+                       colno = (winno - mwin) / stacks;
+                       if (s <= (winno - mwin) % stacks)
+                               colno++;
+                       split = split + colno;
                        hrh = (r_g.h / colno);
                        extra = r_g.h - (colno * hrh);
                        if (flip)
                                win_g.x = r_g.x;
                        else
-                               win_g.x += msize + 2;
-                       win_g.w = r_g.w - (msize + 2);
+                               win_g.x += win_g.w + 2;
+                       win_g.w = (r_g.w - msize - (stacks * 2)) / stacks;
+                       if (s == 1)
+                               win_g.w += (r_g.w - msize - (stacks * 2)) %
+                                   stacks;
+                       s--;
                        j = 0;
                }
                win_g.h = hrh - 2;
@@ -1350,7 +1568,7 @@ stack_master(struct workspace *ws, struct swm_geometry *g, int rot, int flip)
                        h_inc = win->sh.width_inc;
                        h_base = win->sh.base_width;
                } else {
-                       h_inc = win->sh.height_inc;     
+                       h_inc = win->sh.height_inc;
                        h_base = win->sh.base_height;
                }
                if (j == colno - 1) {
@@ -1387,18 +1605,17 @@ stack_master(struct workspace *ws, struct swm_geometry *g, int rot, int flip)
                        win->g.w = wc.width = win_g.w;
                        win->g.h = wc.height = win_g.h;
                }
+               adjust_font(win);
                mask = CWX | CWY | CWWidth | CWHeight | CWBorderWidth;
                XConfigureWindow(display, win->id, mask, &wc);
                XMapRaised(display, win->id);
-               /*
-               fprintf(stderr, "vertical_stack: win %d x %d y %d w %d h %d bw %d\n", win->id, win->g.x, win->g.y, win->g.w , win->g.h, wc.border_width);
-               */
 
                last_h = win_g.h;
                i++;
                j++;
        }
 
+ notiles:
        /* now, stack all the floaters and transients */
        TAILQ_FOREACH(win, &ws->winlist, entry) {
                if (win->transient == 0 && win->floating == 0)
@@ -1422,6 +1639,7 @@ vertical_config(struct workspace *ws, int id)
        case SWM_ARG_ID_STACKINIT:
                ws->l_state.vertical_msize = SWM_V_SLICE / 2;
                ws->l_state.vertical_mwin = 1;
+               ws->l_state.vertical_stacks = 1;
                break;
        case SWM_ARG_ID_MASTERSHRINK:
                if (ws->l_state.vertical_msize > 1)
@@ -1438,6 +1656,13 @@ vertical_config(struct workspace *ws, int id)
                if (ws->l_state.vertical_mwin > 0)
                        ws->l_state.vertical_mwin--;
                break;
+       case SWM_ARG_ID_STACKINC:
+               ws->l_state.vertical_stacks++;
+               break;
+       case SWM_ARG_ID_STACKDEC:
+               if (ws->l_state.vertical_stacks > 1)
+                       ws->l_state.vertical_stacks--;
+               break;
        default:
                return;
        }
@@ -1461,6 +1686,7 @@ horizontal_config(struct workspace *ws, int id)
        case SWM_ARG_ID_STACKINIT:
                ws->l_state.horizontal_mwin = 1;
                ws->l_state.horizontal_msize = SWM_H_SLICE / 2;
+               ws->l_state.horizontal_stacks = 1;
                break;
        case SWM_ARG_ID_MASTERSHRINK:
                if (ws->l_state.horizontal_msize > 1)
@@ -1477,6 +1703,13 @@ horizontal_config(struct workspace *ws, int id)
                if (ws->l_state.horizontal_mwin > 0)
                        ws->l_state.horizontal_mwin--;
                break;
+       case SWM_ARG_ID_STACKINC:
+               ws->l_state.horizontal_stacks++;
+               break;
+       case SWM_ARG_ID_STACKDEC:
+               if (ws->l_state.horizontal_stacks > 1)
+                       ws->l_state.horizontal_stacks--;
+               break;
        default:
                return;
        }
@@ -1588,9 +1821,16 @@ send_to_ws(struct swm_region *r, union arg *args)
 void
 wkill(struct swm_region *r, union arg *args)
 {
-       DNPRINTF(SWM_D_MISC, "wkill\n");
-       if(r->ws->focus != NULL)
+       DNPRINTF(SWM_D_MISC, "wkill %d\n", args->id);
+
+       if(r->ws->focus == NULL)
+               return;
+
+       if (args->id == SWM_ARG_ID_KILLWINDOW)
                XKillClient(display, r->ws->focus->id);
+       else
+               if (r->ws->focus->can_delete)
+                       client_msg(r->ws->focus, adelete);
 }
 
 void
@@ -1645,12 +1885,14 @@ struct key {
        { MODKEY,               XK_l,           stack_config,   {.id = SWM_ARG_ID_MASTERGROW} },
        { MODKEY,               XK_comma,       stack_config,   {.id = SWM_ARG_ID_MASTERADD} },
        { MODKEY,               XK_period,      stack_config,   {.id = SWM_ARG_ID_MASTERDEL} },
+       { MODKEY | ShiftMask,   XK_comma,       stack_config,   {.id = SWM_ARG_ID_STACKINC} },
+       { MODKEY | ShiftMask,   XK_period,      stack_config,   {.id = SWM_ARG_ID_STACKDEC} },
        { MODKEY,               XK_Return,      swapwin,        {.id = SWM_ARG_ID_SWAPMAIN} },
        { MODKEY,               XK_j,           focus,          {.id = SWM_ARG_ID_FOCUSNEXT} },
        { MODKEY,               XK_k,           focus,          {.id = SWM_ARG_ID_FOCUSPREV} },
        { MODKEY | ShiftMask,   XK_j,           swapwin,        {.id = SWM_ARG_ID_SWAPNEXT} },
        { MODKEY | ShiftMask,   XK_k,           swapwin,        {.id = SWM_ARG_ID_SWAPPREV} },
-       { MODKEY | ShiftMask,   XK_Return,      spawn,          {.argv = spawn_term} },
+       { MODKEY | ShiftMask,   XK_Return,      spawnterm,      {.argv = spawn_term} },
        { MODKEY,               XK_p,           spawnmenu,      {.argv = spawn_menu} },
        { MODKEY | ShiftMask,   XK_q,           quit,           {0} },
        { MODKEY,               XK_q,           restart,        {0} },
@@ -1682,11 +1924,14 @@ struct key {
        { MODKEY,               XK_b,           bar_toggle,     {0} },
        { MODKEY,               XK_Tab,         focus,          {.id = SWM_ARG_ID_FOCUSNEXT} },
        { MODKEY | ShiftMask,   XK_Tab,         focus,          {.id = SWM_ARG_ID_FOCUSPREV} },
-       { MODKEY | ShiftMask,   XK_x,           wkill,          {0} },
+       { MODKEY | ShiftMask,   XK_x,           wkill,          {.id = SWM_ARG_ID_KILLWINDOW} },
+       { MODKEY,               XK_x,           wkill,          {.id = SWM_ARG_ID_DELETEWINDOW} },
        { MODKEY,               XK_s,           screenshot,     {.id = SWM_ARG_ID_SS_ALL} },
        { MODKEY | ShiftMask,   XK_s,           screenshot,     {.id = SWM_ARG_ID_SS_WINDOW} },
        { MODKEY,               XK_t,           floating_toggle,{0} },
        { MODKEY | ShiftMask,   XK_v,           version,        {0} },
+       { MODKEY | ShiftMask,   XK_Delete,      spawn,          {.argv = spawn_lock} },
+       { MODKEY | ShiftMask,   XK_i,           spawn,          {.argv = spawn_initscr} },
 };
 
 void
@@ -1721,7 +1966,7 @@ resize(struct ws_win *win, union arg *args)
        XEvent                  ev;
        Time                    time = 0;
 
-       DNPRINTF(SWM_D_MOUSE, "resize: win %d floating %d trans %d\n",
+       DNPRINTF(SWM_D_MOUSE, "resize: win %lu floating %d trans %d\n",
            win->id, win->floating, win->transient);
 
        if (!(win->transient != 0 || win->floating != 0))
@@ -1741,10 +1986,10 @@ resize(struct ws_win *win, union arg *args)
                        handler[ev.type](&ev);
                        break;
                case MotionNotify:
-                       if (ev.xmotion.x < 0)
-                               ev.xmotion.x = 0;
-                       if (ev.xmotion.y < 0)
-                               ev.xmotion.y = 0;
+                       if (ev.xmotion.x <= 1)
+                               ev.xmotion.x = 1;
+                       if (ev.xmotion.y <= 1)
+                               ev.xmotion.y = 1;
                        win->g.w = ev.xmotion.x;
                        win->g.h = ev.xmotion.y;
 
@@ -1796,7 +2041,7 @@ move(struct ws_win *win, union arg *args)
        Time                    time = 0;
        int                     restack = 0;
 
-       DNPRINTF(SWM_D_MOUSE, "move: win %d floating %d trans %d\n",
+       DNPRINTF(SWM_D_MOUSE, "move: win %lu floating %d trans %d\n",
            win->id, win->floating, win->transient);
 
        if (win->floating == 0) {
@@ -1859,6 +2104,24 @@ struct button {
        { client_click,         MODKEY,         Button1,        move,           {0} },
 };
 
+void
+update_modkey(unsigned int mod)
+{
+       int                     i;
+
+       for (i = 0; i < LENGTH(keys); i++)
+               if (keys[i].mod & ShiftMask)
+                       keys[i].mod = mod | ShiftMask;
+               else
+                       keys[i].mod = mod;
+
+       for (i = 0; i < LENGTH(buttons); i++)
+               if (buttons[i].mask & ShiftMask)
+                       buttons[i].mask = mod | ShiftMask;
+               else
+                       buttons[i].mask = mod;
+}
+
 void
 updatenumlockmask(void)
 {
@@ -1992,13 +2255,15 @@ manage_window(Window id)
        Window                  trans;
        struct workspace        *ws;
        struct ws_win           *win;
-       int                     format, i, ws_idx;
+       int                     format, i, ws_idx, n;
        unsigned long           nitems, bytes;
        Atom                    ws_idx_atom = 0, type;
+       Atom                    *prot = NULL, *pp;
        unsigned char           ws_idx_str[SWM_PROPLEN], *prop = NULL;
        struct swm_region       *r;
        long                    mask;
        const char              *errstr;
+       XWindowChanges          wc;
 
        if ((win = find_window(id)) != NULL)
                        return (win);   /* already being managed */
@@ -2006,42 +2271,57 @@ manage_window(Window id)
        if ((win = calloc(1, sizeof(struct ws_win))) == NULL)
                errx(1, "calloc: failed to allocate memory for new window");
 
+       /* Get all the window data in one shot */
        ws_idx_atom = XInternAtom(display, "_SWM_WS", False);
        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);
+       XGetTransientForHint(display, id, &trans);
+       XGetWMNormalHints(display, id, &win->sh, &mask); /* XXX function? */
+       if (trans) {
+               win->transient = trans;
+               DNPRINTF(SWM_D_MISC, "manage_window: win %u transient %u\n",
+                   (unsigned)win->id, win->transient);
+       }
+       /* get supported protocols */
+       if (XGetWMProtocols(display, id, &prot, &n)) {
+               for (i = 0, pp = prot; i < n; i++, pp++)
+                       if (*pp == adelete)
+                               win->can_delete = 1;
+               if (prot)
+                       XFree(prot);
+       }
+
+       /*
+        * Figure out where to put the window. If it was previously assigned to
+        * a workspace (either by spawn() or manually moving), and isn't
+        * transient, * put it in the same workspace
+        */
        r = root_to_region(win->wa.root);
-       /* If the window was managed before, put it in the same workspace */
-       if (prop) {
+       if (prop && win->transient == 0) {
                DNPRINTF(SWM_D_PROP, "got property _SWM_WS=%s\n", prop);
                ws_idx = strtonum(prop, 0, 9, &errstr);
-               if (errstr)
+               if (errstr) {
                        DNPRINTF(SWM_D_EVENT, "window idx is %s: %s",
                            errstr, prop);
+               }
                ws = &r->s->ws[ws_idx];
        } else
                ws = r->ws;
 
+       /* set up the window layout */
        win->id = id;
        win->ws = ws;
        win->s = r->s;  /* this never changes */
        TAILQ_INSERT_TAIL(&ws->winlist, win, entry);
 
-       XGetTransientForHint(display, win->id, &trans);
-       if (trans) {
-               win->transient = trans;
-               DNPRINTF(SWM_D_MISC, "manage_window: win %u transient %u\n",
-                   (unsigned)win->id, win->transient);
-       }
        win->g.w = win->wa.width;
        win->g.h = win->wa.height;
        win->g.x = win->wa.x;
        win->g.y = win->wa.y;
 
-       XGetWMNormalHints(display, win->id, &win->sh, &mask); /* XXX function? */
-
+       /* 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) {
                DNPRINTF(SWM_D_PROP, "setting property _SWM_WS to %s\n",
@@ -2051,10 +2331,6 @@ manage_window(Window id)
        }
        XFree(prop);
 
-       /*
-       fprintf(stderr, "manage window: %d x %d y %d w %d h %d\n", win->id, win->g.x, win->g.y, win->g.w, win->g.h);
-       */
-
        if (XGetClassHint(display, win->id, &win->ch)) {
                DNPRINTF(SWM_D_CLASS, "class: %s name: %s\n",
                    win->ch.res_class, win->ch.res_name);
@@ -2066,21 +2342,89 @@ manage_window(Window id)
                                    win->ch.res_class, win->ch.res_name);
                                if (quirks[i].quirk & SWM_Q_FLOAT)
                                        win->floating = 1;
+                               win->quirks = quirks[i].quirk;
                        }
                }
        }
 
+       /* alter window position if quirky */
+       if (win->quirks & SWM_Q_ANYWHERE) {
+               win->manual = 1; /* don't center the quirky windows */
+               bzero(&wc, sizeof wc);
+               mask = 0;
+               if (win->g.y < bar_height) {
+                       win->g.y = wc.y = bar_height;
+                       mask |= CWY;
+               }
+               if (win->g.w + win->g.x > WIDTH(r)) {
+                       win->g.x = wc.x = WIDTH(win->ws->r) - win->g.w - 2;
+                       mask |= CWX;
+               }
+               wc.border_width = 1;
+               mask |= CWBorderWidth;
+               XConfigureWindow(display, win->id, mask, &wc);
+       }
+
+       /* Reset font sizes (the bruteforce way; no default keybinding). */
+       if (win->quirks & SWM_Q_XTERM_FONTADJ) {
+               for (i = 0; i < SWM_MAX_FONT_STEPS; i++)
+                       fake_keypress(win, XK_KP_Subtract, ShiftMask);
+               for (i = 0; i < SWM_MAX_FONT_STEPS; i++)
+                       fake_keypress(win, XK_KP_Add, ShiftMask);
+       }
+
        XSelectInput(display, id, EnterWindowMask | FocusChangeMask |
            PropertyChangeMask | StructureNotifyMask);
 
        set_win_state(win, NormalState);
 
+       /* floaters need to be mapped if they are in the current workspace */
+       if (win->floating && (ws->idx == r->ws->idx))
+               XMapRaised(display, win->id);
+
        /* make new win focused */
        focus_win(win);
 
        return (win);
 }
 
+void
+unmanage_window(struct ws_win *win)
+{
+       struct workspace        *ws;
+
+       if (win == NULL)
+               return;
+
+       DNPRINTF(SWM_D_MISC, "unmanage_window:  %lu\n", win->id);
+
+       /* don't unmanage if we are switching workspaces */
+       ws = win->ws;
+       if (ws->restack)
+               return;
+
+       /* find a window to focus */
+       if (ws->focus == win)
+               ws->focus = TAILQ_PREV(win, ws_win_list, entry);
+       if (ws->focus == NULL)
+               ws->focus = TAILQ_FIRST(&ws->winlist);
+       if (ws->focus == NULL || ws->focus == win) {
+               ws->focus = NULL;
+               unfocus_all();
+       } else
+               focus_win(ws->focus);
+       if (ws->focus_prev == win)
+               ws->focus_prev = NULL;
+
+       TAILQ_REMOVE(&win->ws->winlist, win, entry);
+       set_win_state(win, WithdrawnState);
+       if (win->ch.res_class)
+               XFree(win->ch.res_class);
+       if (win->ch.res_name)
+               XFree(win->ch.res_name);
+       free(win);
+}
+
 void
 configurerequest(XEvent *e)
 {
@@ -2095,10 +2439,6 @@ configurerequest(XEvent *e)
        if (new) {
                DNPRINTF(SWM_D_EVENT, "configurerequest: new window: %lu\n",
                    ev->window);
-               /*
-               fprintf(stderr, "configurerequest: new window: %lu x %d y %d w %d h %d bw %d s %d sm %d\n",
-                   ev->window, ev->x, ev->y, ev->width, ev->height, ev->border_width, ev->above, ev->detail);
-               */
                bzero(&wc, sizeof wc);
                wc.x = ev->x;
                wc.y = ev->y;
@@ -2109,10 +2449,6 @@ configurerequest(XEvent *e)
                wc.stack_mode = ev->detail;
                XConfigureWindow(display, ev->window, ev->value_mask, &wc);
        } else {
-               /*
-               fprintf(stderr, "configurerequest: change window: %lu\n",
-                   ev->window);
-               */
                DNPRINTF(SWM_D_EVENT, "configurerequest: change window: %lu\n",
                    ev->window);
                if (win->floating) {
@@ -2127,19 +2463,19 @@ configurerequest(XEvent *e)
                        if (win->ws->r != NULL) {
                                /* this seems to be full screen */
                                if (win->g.w >= WIDTH(win->ws->r)) {
-                                       win->g.x = -1;
+                                       win->g.x = 0;
                                        win->g.w = WIDTH(win->ws->r);
                                        ev->value_mask |= CWX | CWWidth;
                                }
                                if (win->g.h >= HEIGHT(win->ws->r)) {
                                        /* kill border */
-                                       win->g.y = -1;
+                                       win->g.y = 0;
                                        win->g.h = HEIGHT(win->ws->r);
                                        ev->value_mask |= CWY | CWHeight;
                                }
                        }
-                       if ((ev->value_mask & (CWX|CWY)) &&
-                           !(ev->value_mask & (CWWidth|CWHeight)))
+                       if ((ev->value_mask & (CWX | CWY)) &&
+                           !(ev->value_mask & (CWWidth | CWHeight)))
                                config_win(win);
                        XMoveResizeWindow(display, win->id,
                            win->g.x, win->g.y, win->g.w, win->g.h);
@@ -2151,8 +2487,21 @@ configurerequest(XEvent *e)
 void
 configurenotify(XEvent *e)
 {
+       struct ws_win           *win;
+       long                    mask;
+
        DNPRINTF(SWM_D_EVENT, "configurenotify: window: %lu\n",
            e->xconfigure.window);
+
+       XMapWindow(display, e->xconfigure.window);
+       win = find_window(e->xconfigure.window);
+       if (win) {
+               XGetWMNormalHints(display, win->id, &win->sh, &mask);
+               adjust_font(win);
+               XMapWindow(display, win->id);
+               if (font_adjusted)
+                       stack();
+       }
 }
 
 void
@@ -2160,29 +2509,11 @@ destroynotify(XEvent *e)
 {
        struct ws_win           *win;
        XDestroyWindowEvent     *ev = &e->xdestroywindow;
-       struct workspace        *ws;
 
        DNPRINTF(SWM_D_EVENT, "destroynotify: window %lu\n", ev->window);
 
        if ((win = find_window(ev->window)) != NULL) {
-               ws = win->ws;
-               /* find a window to focus */
-               if (ws->focus == win)
-                       ws->focus = TAILQ_PREV(win, ws_win_list, entry);
-               if (ws->focus == NULL)
-                       ws->focus = TAILQ_FIRST(&ws->winlist);
-               if (ws->focus == NULL || ws->focus == win) {
-                       ws->focus = NULL;
-                       unfocus_all();
-               } else
-                       focus_win(ws->focus);
-               TAILQ_REMOVE(&ws->winlist, win, entry);
-               set_win_state(win, WithdrawnState);
-               if (win->ch.res_class)
-                       XFree(win->ch.res_class);
-               if (win->ch.res_name)
-                       XFree(win->ch.res_name);
-               free(win);
+               unmanage_window(win);
                stack();
        }
 }
@@ -2237,6 +2568,7 @@ maprequest(XEvent *e)
        if (wa.override_redirect)
                return;
        manage_window(e->xmaprequest.window);
+
        stack();
 }
 
@@ -2278,7 +2610,14 @@ propertynotify(XEvent *e)
 void
 unmapnotify(XEvent *e)
 {
+       XDestroyWindowEvent     *ev = &e->xdestroywindow;
+       struct ws_win           *win;
+
        DNPRINTF(SWM_D_EVENT, "unmapnotify: window: %lu\n", e->xunmap.window);
+
+       if ((win = find_window(ev->window)) != NULL)
+               if (win->transient)
+                       unmanage_window(win);
 }
 
 void
@@ -2337,28 +2676,74 @@ getstate(Window w)
        unsigned long           n, extra;
        Atom                    real;
 
-       astate = XInternAtom(display, "WM_STATE", False);
        status = XGetWindowProperty(display, w, astate, 0L, 2L, False, astate,
            &real, &format, &n, &extra, (unsigned char **)&p);
        if (status != Success)
                return (-1);
        if (n != 0)
-               result = *p;
+               result = *((long *)p);
        XFree(p);
        return (result);
 }
 
 void
-new_region(struct swm_screen *s, struct workspace *ws,
-    int x, int y, int w, int h)
+new_region(struct swm_screen *s, int x, int y, int w, int h)
 {
-       struct swm_region       *r;
+       struct swm_region       *r, *n;
+       struct workspace        *ws = NULL;
+       int                     i;
 
-       DNPRINTF(SWM_D_MISC, "new region on screen %d: %dx%d (%d, %d)\n",
-            s->idx, x, y, w, h);
+       DNPRINTF(SWM_D_MISC, "new region: screen[%d]:%dx%d+%d+%d\n",
+            s->idx, w, h, x, y);
+
+       /* remove any conflicting regions */
+       n = TAILQ_FIRST(&s->rl);
+       while (n) {
+               r = n;
+               n = TAILQ_NEXT(r, entry);
+               if (X(r) < (x + w) &&
+                   (X(r) + WIDTH(r)) > x &&
+                   Y(r) < (y + h) &&
+                   (Y(r) + HEIGHT(r)) > y) {
+                       XDestroyWindow(display, r->bar_window);
+                       TAILQ_REMOVE(&s->rl, r, entry);
+                       TAILQ_INSERT_TAIL(&s->orl, r, entry);
+               }
+       }
+
+       /* search old regions for one to reuse */
+
+       /* size + location match */
+       TAILQ_FOREACH(r, &s->orl, entry)
+               if (X(r) == x && Y(r) == y &&
+                   HEIGHT(r) == h && WIDTH(r) == w)
+                       break;
 
-       if ((r = calloc(1, sizeof(struct swm_region))) == NULL)
-               errx(1, "calloc: failed to allocate memory for screen");
+       /* size match */
+       TAILQ_FOREACH(r, &s->orl, entry)
+               if (HEIGHT(r) == h && WIDTH(r) == w)
+                       break;
+
+       if (r != NULL) {
+               TAILQ_REMOVE(&s->orl, r, entry);
+               /* try to use old region's workspace */
+               if (r->ws->r == NULL)
+                       ws = r->ws;
+       } else
+               if ((r = calloc(1, sizeof(struct swm_region))) == NULL)
+                       errx(1, "calloc: failed to allocate memory for screen");
+
+       /* if we don't have a workspace already, find one */
+       if (ws == NULL) {
+               for (i = 0; i < SWM_WS_MAX; i++)
+                       if (s->ws[i].r == NULL) {
+                               ws = &s->ws[i];
+                               break;
+                       }
+       }
+
+       if (ws == NULL)
+               errx(1, "no free workspaces\n");
 
        X(r) = x;
        Y(r) = y;
@@ -2368,24 +2753,108 @@ new_region(struct swm_screen *s, struct workspace *ws,
        r->ws = ws;
        ws->r = r;
        TAILQ_INSERT_TAIL(&s->rl, r, entry);
-       bar_setup(r);
 }
 
 void
-setup_screens(void)
+scan_xrandr(int i)
 {
 #ifdef SWM_XRR_HAS_CRTC
        XRRCrtcInfo             *ci;
        XRRScreenResources      *sr;
        int                     c;
+       int                     ncrtc = 0;
 #endif /* SWM_XRR_HAS_CRTC */
+       struct swm_region       *r;
+
+
+       if (i >= ScreenCount(display))
+               errx(1, "invalid screen");
+
+       /* remove any old regions */
+       while ((r = TAILQ_FIRST(&screens[i].rl)) != NULL) {
+               r->ws->r = NULL;
+               XDestroyWindow(display, r->bar_window);
+               TAILQ_REMOVE(&screens[i].rl, r, entry);
+               TAILQ_INSERT_TAIL(&screens[i].orl, r, entry);
+       }
+
+       /* map virtual screens onto physical screens */
+#ifdef SWM_XRR_HAS_CRTC
+       if (xrandr_support) {
+               sr = XRRGetScreenResources(display, screens[i].root);
+               if (sr == NULL)
+                       new_region(&screens[i], 0, 0,
+                           DisplayWidth(display, i),
+                           DisplayHeight(display, i)); 
+               else 
+                       ncrtc = sr->ncrtc;
+
+               for (c = 0, ci = NULL; c < ncrtc; c++) {
+                       ci = XRRGetCrtcInfo(display, sr, sr->crtcs[c]);
+                       if (ci->noutput == 0)
+                               continue;
+
+                       if (ci != NULL && ci->mode == None)
+                               new_region(&screens[i], 0, 0,
+                                   DisplayWidth(display, i),
+                                   DisplayHeight(display, i)); 
+                       else
+                               new_region(&screens[i],
+                                   ci->x, ci->y, ci->width, ci->height);
+               }
+               if (ci)
+                       XRRFreeCrtcInfo(ci);
+               XRRFreeScreenResources(sr);
+       } else
+#endif /* SWM_XRR_HAS_CRTC */
+       {
+               new_region(&screens[i], 0, 0, DisplayWidth(display, i),
+                   DisplayHeight(display, i)); 
+       }
+}
+
+void
+screenchange(XEvent *e) {
+       XRRScreenChangeNotifyEvent      *xe = (XRRScreenChangeNotifyEvent *)e;
+       struct swm_region               *r;
+       struct ws_win                   *win;
+       int                             i;
+
+       DNPRINTF(SWM_D_EVENT, "screenchange: %lu\n", xe->root);
+
+       if (!XRRUpdateConfiguration(e))
+               return;
+
+       /* silly event doesn't include the screen index */
+       for (i = 0; i < ScreenCount(display); i++)
+               if (screens[i].root == xe->root)
+                       break;
+       if (i >= ScreenCount(display))
+               errx(1, "screenchange: screen not found\n");
+
+       /* brute force for now, just re-enumerate the regions */
+       scan_xrandr(i);
+
+       /* hide any windows that went away */
+       TAILQ_FOREACH(r, &screens[i].rl, entry)
+               TAILQ_FOREACH(win, &r->ws->winlist, entry)
+                       XUnmapWindow(display, win->id);
+
+       /* add bars to all regions */
+       for (i = 0; i < ScreenCount(display); i++)
+               TAILQ_FOREACH(r, &screens[i].rl, entry)
+                       bar_setup(r);
+       stack();
+}
+
+void
+setup_screens(void)
+{
        Window                  d1, d2, *wins = NULL;
        XWindowAttributes       wa;
-       struct swm_region       *r;
        unsigned int            no;
-       int                     errorbase, major, minor;
-       int                     ncrtc = 0, w = 0;
         int                    i, j, k;
+       int                     errorbase, major, minor;
        struct workspace        *ws;
        int                     ws_idx_atom;
 
@@ -2395,13 +2864,20 @@ setup_screens(void)
                errx(1, "calloc: screens");
 
        ws_idx_atom = XInternAtom(display, "_SWM_WS", False);
-       
+
+       /* initial Xrandr setup */
+       xrandr_support = XRRQueryExtension(display,
+           &xrandr_eventbase, &errorbase);
+       if (xrandr_support)
+               if (XRRQueryVersion(display, &major, &minor) && major < 1)
+                               xrandr_support = 0;
 
        /* map physical screens */
        for (i = 0; i < ScreenCount(display); i++) {
                DNPRINTF(SWM_D_WS, "setup_screens: init screen %d\n", i);
                screens[i].idx = i;
                TAILQ_INIT(&screens[i].rl);
+               TAILQ_INIT(&screens[i].orl);
                screens[i].root = RootWindow(display, i);
 
                /* set default colors */
@@ -2412,6 +2888,7 @@ setup_screens(void)
                setscreencolor("rgb:a0/a0/a0", i + 1, SWM_S_COLOR_BAR_FONT);
 
                /* init all workspaces */
+               /* XXX these should be dynamically allocated too */
                for (j = 0; j < SWM_WS_MAX; j++) {
                        ws = &screens[i].ws[j];
                        ws->idx = j;
@@ -2426,83 +2903,38 @@ setup_screens(void)
                                            SWM_ARG_ID_STACKINIT);
                        ws->cur_layout = &layouts[0];
                }
-
-               /* map virtual screens onto physical screens */
-               screens[i].xrandr_support = XRRQueryExtension(display,
-                   &xrandr_eventbase, &errorbase);
-               if (screens[i].xrandr_support)
-                       if (XRRQueryVersion(display, &major, &minor) &&
-                           major < 1)
-                               screens[i].xrandr_support = 0;
-
-#if 0  /* not ready for dynamic screen changes */
-               if (screens[i].xrandr_support)
-                       XRRSelectInput(display,
-                           screens[r->s].root,
-                           RRScreenChangeNotifyMask);
-#endif
-
                /* grab existing windows (before we build the bars)*/
                if (!XQueryTree(display, screens[i].root, &d1, &d2, &wins, &no))
                        continue;
 
-#ifdef SWM_XRR_HAS_CRTC
-               sr = XRRGetScreenResources(display, screens[i].root);
-               if (sr == NULL)
-                       new_region(&screens[i], &screens[i].ws[w],
-                           0, 0, DisplayWidth(display, i),
-                           DisplayHeight(display, i)); 
-               else 
-                       ncrtc = sr->ncrtc;
-
-               for (c = 0, ci = NULL; c < ncrtc; c++) {
-                       ci = XRRGetCrtcInfo(display, sr, sr->crtcs[c]);
-                       if (ci->noutput == 0)
-                               continue;
+               scan_xrandr(i);
 
-                       if (ci != NULL && ci->mode == None)
-                               new_region(&screens[i], &screens[i].ws[w], 0, 0,
-                                   DisplayWidth(display, i),
-                                   DisplayHeight(display, i)); 
-                       else
-                               new_region(&screens[i], &screens[i].ws[w],
-                                   ci->x, ci->y, ci->width, ci->height);
-                       w++;
-               }
-               if (ci)
-                       XRRFreeCrtcInfo(ci);
-               XRRFreeScreenResources(sr);
-#else
-               new_region(&screens[i], &screens[i].ws[w], 0, 0,
-                   DisplayWidth(display, i),
-                   DisplayHeight(display, i)); 
-#endif /* SWM_XRR_HAS_CRTC */
+               if (xrandr_support)
+                       XRRSelectInput(display, screens[i].root,
+                           RRScreenChangeNotifyMask);
 
                /* attach windows to a region */
                /* normal windows */
-               if ((r = TAILQ_FIRST(&screens[i].rl)) == NULL)
-                       errx(1, "no regions on screen %d", i);
-
-               for (i = 0; i < no; i++) {
-                        XGetWindowAttributes(display, wins[i], &wa);
-                       if (!XGetWindowAttributes(display, wins[i], &wa) ||
+               for (j = 0; j < no; j++) {
+                        XGetWindowAttributes(display, wins[j], &wa);
+                       if (!XGetWindowAttributes(display, wins[j], &wa) ||
                            wa.override_redirect ||
-                           XGetTransientForHint(display, wins[i], &d1))
+                           XGetTransientForHint(display, wins[j], &d1))
                                continue;
 
                        if (wa.map_state == IsViewable ||
-                           getstate(wins[i]) == NormalState)
-                               manage_window(wins[i]);
+                           getstate(wins[j]) == NormalState)
+                               manage_window(wins[j]);
                }
                /* transient windows */
-               for (i = 0; i < no; i++) {
-                       if (!XGetWindowAttributes(display, wins[i], &wa))
+               for (j = 0; j < no; j++) {
+                       if (!XGetWindowAttributes(display, wins[j], &wa))
                                continue;
 
-                       if (XGetTransientForHint(display, wins[i], &d1) &&
-                           (wa.map_state == IsViewable || getstate(wins[i]) ==
+                       if (XGetTransientForHint(display, wins[j], &d1) &&
+                           (wa.map_state == IsViewable || getstate(wins[j]) ==
                            NormalState))
-                               manage_window(wins[i]);
+                               manage_window(wins[j]);
                 }
                 if (wins) {
                         XFree(wins);
@@ -2511,14 +2943,35 @@ setup_screens(void)
        }
 }
 
+void
+workaround(void)
+{
+       int                     i;
+       Atom                    netwmcheck, netwmname, utf8_string;
+       Window                  root;
+
+       /* work around sun jdk bugs, code from wmname */
+       netwmcheck = XInternAtom(display, "_NET_SUPPORTING_WM_CHECK", False);
+       netwmname = XInternAtom(display, "_NET_WM_NAME", False);
+       utf8_string = XInternAtom(display, "UTF8_STRING", False);
+       for (i = 0; i < ScreenCount(display); i++) {
+               root = screens[i].root;
+               XChangeProperty(display, root, netwmcheck, XA_WINDOW, 32,
+                   PropModeReplace, (unsigned char *)&root, 1);
+               XChangeProperty(display, root, netwmname, utf8_string, 8,
+                   PropModeReplace, "LG3D", strlen("LG3D"));
+       }
+}
+
 int
 main(int argc, char *argv[])
 {
        struct passwd           *pwd;
+       struct swm_region       *r;
        char                    conf[PATH_MAX], *cfile = NULL;
        struct stat             sb;
        XEvent                  e;
-       int                     xfd;
+       int                     xfd, i;
        fd_set                  rd;
 
        start_argv = argv;
@@ -2534,6 +2987,8 @@ main(int argc, char *argv[])
                errx(1, "other wm running");
 
        astate = XInternAtom(display, "WM_STATE", False);
+       aprot = XInternAtom(display, "WM_PROTOCOLS", False);
+       adelete = XInternAtom(display, "WM_DELETE_WINDOW", False);
 
        /* look for local and global conf file */
        pwd = getpwuid(getuid());
@@ -2555,9 +3010,14 @@ main(int argc, char *argv[])
        }
        if (cfile)
                conf_load(cfile);
-       bar_refresh();
 
-       /* ws[0].focus = TAILQ_FIRST(&ws[0].winlist); */
+       /* setup all bars */
+       for (i = 0; i < ScreenCount(display); i++)
+               TAILQ_FOREACH(r, &screens[i].rl, entry)
+                       bar_setup(r);
+
+       /* set some values to work around bad programs */
+       workaround();
 
        grabkeys();
        stack();
@@ -2573,10 +3033,27 @@ main(int argc, char *argv[])
                        bar_alarm = 0;
                        bar_update();
                }
-               while(XPending(display)) {
+               while (XPending(display)) {
                        XNextEvent(display, &e);
-                       if (handler[e.type])
-                               handler[e.type](&e);
+                       if (e.type < LASTEvent) {
+                               if (handler[e.type])
+                                       handler[e.type](&e);
+                               else
+                                       DNPRINTF(SWM_D_EVENT,
+                                           "win: %lu unknown event: %d\n",
+                                           e.xany.window, e.type);
+                       } else {
+                               switch (e.type - xrandr_eventbase) {
+                               case RRScreenChangeNotify:
+                                       screenchange(&e);
+                                       break;
+                               default:
+                                       DNPRINTF(SWM_D_EVENT,
+                                           "win: %lu unknown xrandr event: "
+                                           "%d\n", e.xany.window, e.type);
+                                       break;
+                               }
+                       }
                }
        }