JasonWoof Got questions, comments, patches, etc.? Contact Jason Woofenden
fix keys pressed while kbspawn key is still down
[dwm.git] / dwm.c
diff --git a/dwm.c b/dwm.c
index 10b0d0d..898b917 100644 (file)
--- a/dwm.c
+++ b/dwm.c
@@ -32,6 +32,7 @@
 #include <sys/wait.h>
 #include <X11/cursorfont.h>
 #include <X11/keysym.h>
+#include <X11/XF86keysym.h>
 #include <X11/Xatom.h>
 #include <X11/Xlib.h>
 #include <X11/Xproto.h>
@@ -40,6 +41,9 @@
 #include <X11/extensions/Xinerama.h>
 #endif /* XINERAMA */
 
+#include "drw.h"
+#include "util.h"
+
 /* macros */
 #define BUTTONMASK              (ButtonPressMask|ButtonReleaseMask)
 #define CLEANMASK(mask)         (mask & ~(numlockmask|LockMask) & (ShiftMask|ControlMask|Mod1Mask|Mod2Mask|Mod3Mask|Mod4Mask|Mod5Mask))
                                * MAX(0, MIN((y)+(h),(m)->wy+(m)->wh) - MAX((y),(m)->wy)))
 #define ISVISIBLE(C)            ((C->tags & C->mon->tagset[C->mon->seltags]))
 #define LENGTH(X)               (sizeof X / sizeof X[0])
-#define MAX(A, B)               ((A) > (B) ? (A) : (B))
-#define MIN(A, B)               ((A) < (B) ? (A) : (B))
 #define MOUSEMASK               (BUTTONMASK|PointerMotionMask)
 #define WIDTH(X)                ((X)->w + 2 * (X)->bw)
 #define HEIGHT(X)               ((X)->h + 2 * (X)->bw)
 #define TAGMASK                 ((1 << LENGTH(tags)) - 1)
-#define TEXTW(X)                (textnw(X, strlen(X)) + dc.font.height)
+#define TEXTW_NOPAD(X)          drw_font_getexts_width(drw->font, X, strlen(X))
+#define TEXTW(X)                (TEXTW_NOPAD(X) + drw->font->h)
 
 /* enums */
-enum { CurNormal, CurResize, CurMove, CurLast };        /* cursor */
-enum { ColBorder, ColFG, ColBG, ColLast };              /* color */
+enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */
+enum { SchemeNorm, SchemeSel, SchemeLast }; /* color schemes */
 enum { NetSupported, NetWMName, NetWMState,
-       NetWMFullscreen, NetActiveWindow, NetWMWindowType,
-       NetWMWindowTypeDialog, NetLast };     /* EWMH atoms */
+       NetWMFullscreen, NetWMWindowOpacity, NetActiveWindow, NetWMWindowType,
+       NetWMWindowTypeDialog, NetClientList, NetSupportingWMCheck, NetLast }; /* EWMH atoms */
 enum { WMProtocols, WMDelete, WMState, WMTakeFocus, WMLast }; /* default atoms */
 enum { ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle,
-       ClkClientWin, ClkRootWin, ClkLast };             /* clicks */
+       ClkClientWin, ClkRootWin, ClkAnywhere, ClkLast }; /* clicks */
 
 typedef union {
        int i;
@@ -89,8 +92,9 @@ struct Client {
        int oldx, oldy, oldw, oldh;
        int basew, baseh, incw, inch, maxw, maxh, minw, minh;
        int bw, oldbw;
+       int opacity;
        unsigned int tags;
-       Bool isfixed, isfloating, isurgent, neverfocus, oldstate, isfullscreen;
+       Bool isfixed, isfloating, isurgent, neverfocus, oldstate, isfullscreen, screen_hog;
        Client *next;
        Client *snext;
        Monitor *mon;
@@ -98,21 +102,6 @@ struct Client {
 };
 
 typedef struct {
-       int x, y, w, h;
-       unsigned long norm[ColLast];
-       unsigned long sel[ColLast];
-       Drawable drawable;
-       GC gc;
-       struct {
-               int ascent;
-               int descent;
-               int height;
-               XFontSet set;
-               XFontStruct *xfont;
-       } font;
-} DC; /* draw context */
-
-typedef struct {
        unsigned int mod;
        KeySym keysym;
        void (*func)(const Arg *);
@@ -151,6 +140,7 @@ typedef struct {
        const char *title;
        unsigned int tags;
        Bool isfloating;
+       Bool screen_hog;
        int monitor;
 } Rule;
 
@@ -159,6 +149,7 @@ static void applyrules(Client *c);
 static Bool applysizehints(Client *c, int *x, int *y, int *w, int *h, Bool interact);
 static void arrange(Monitor *m);
 static void arrangemon(Monitor *m);
+static void attach_as_master(Client *c);
 static void attach(Client *c);
 static void attachstack(Client *c);
 static void buttonpress(XEvent *e);
@@ -174,26 +165,21 @@ static Monitor *createmon(void);
 static void destroynotify(XEvent *e);
 static void detach(Client *c);
 static void detachstack(Client *c);
-static void die(const char *errstr, ...);
 static Monitor *dirtomon(int dir);
 static void drawbar(Monitor *m);
 static void drawbars(void);
-static void drawsquare(Bool filled, Bool empty, Bool invert, unsigned long col[ColLast]);
-static void drawtext(const char *text, unsigned long col[ColLast], Bool invert);
 static void enternotify(XEvent *e);
 static void expose(XEvent *e);
 static void focus(Client *c);
 static void focusin(XEvent *e);
 static void focusmon(const Arg *arg);
 static void focusstack(const Arg *arg);
-static unsigned long getcolor(const char *colstr);
 static Bool getrootptr(int *x, int *y);
 static long getstate(Window w);
 static Bool gettextprop(Window w, Atom atom, char *text, unsigned int size);
 static void grabbuttons(Client *c, Bool focused);
 static void grabkeys(void);
 static void incnmaster(const Arg *arg);
-static void initfont(const char *fontstr);
 static void keypress(XEvent *e);
 static void killclient(const Arg *arg);
 static void manage(Window w, XWindowAttributes *wa);
@@ -203,12 +189,14 @@ static void monocle(Monitor *m);
 static void motionnotify(XEvent *e);
 static void movemouse(const Arg *arg);
 static Client *nexttiled(Client *c);
+static Client *snexttiled(Client *c);
+static Client *nextvisible(Client *c);
 static void pop(Client *);
 static void propertynotify(XEvent *e);
 static void quit(const Arg *arg);
 static Monitor *recttomon(int x, int y, int w, int h);
-static void resize(Client *c, int x, int y, int w, int h, Bool interact);
-static void resizeclient(Client *c, int x, int y, int w, int h);
+static void resize(Client *c, int x, int y, int w, int h, Bool interact, Client *base);
+static void resizeclient(Client *c, int x, int y, int w, int h, Client *base);
 static void resizemouse(const Arg *arg);
 static void restack(Monitor *m);
 static void run(void);
@@ -223,10 +211,11 @@ static void setmfact(const Arg *arg);
 static void setup(void);
 static void showhide(Client *c);
 static void sigchld(int unused);
+static void kbspawn(const Arg *arg);
 static void spawn(const Arg *arg);
 static void tag(const Arg *arg);
 static void tagmon(const Arg *arg);
-static int textnw(const char *text, unsigned int len);
+static void jason_layout(Monitor *);
 static void tile(Monitor *);
 static void togglebar(const Arg *arg);
 static void togglefloating(const Arg *arg);
@@ -238,6 +227,7 @@ static void unmapnotify(XEvent *e);
 static Bool updategeom(void);
 static void updatebarpos(Monitor *m);
 static void updatebars(void);
+static void updateclientlist(void);
 static void updatenumlockmask(void);
 static void updatesizehints(Client *c);
 static void updatestatus(void);
@@ -252,12 +242,14 @@ static int xerrordummy(Display *dpy, XErrorEvent *ee);
 static int xerrorstart(Display *dpy, XErrorEvent *ee);
 static void zoom(const Arg *arg);
 
-/* variables */
+// variables
+enum { KeyBufferingOff, KeyBufferingAwaitingWindow, KeyBufferingAwaitingFocus };
+static unsigned int key_buffering = 0;
 static const char broken[] = "broken";
 static char stext[256];
 static int screen;
-static int sw, sh;           /* X display screen geometry width, height */
-static int bh, blw = 0;      /* bar geometry */
+static int sw, sh;  // X display screen geometry width, height
+static int bh;      // bar height
 static int (*xerrorxlib)(Display *, XErrorEvent *);
 static unsigned int numlockmask = 0;
 static void (*handler[LASTEvent]) (XEvent *) = {
@@ -278,12 +270,70 @@ static void (*handler[LASTEvent]) (XEvent *) = {
 };
 static Atom wmatom[WMLast], netatom[NetLast];
 static Bool running = True;
-static Cursor cursor[CurLast];
+static Cur *cursor[CurLast];
+static ClrScheme scheme[SchemeLast];
 static Display *dpy;
-static DC dc;
-static Monitor *mons = NULL, *selmon = NULL;
+static Drw *drw;
+static Fnt *fnt;
+static Monitor *mons, *selmon;
 static Window root;
 
+// unfocused windows get transparent (feature)
+static const unsigned long opacities[] = { 0, 0xbfffffff, 0x00000000 }; // first unused
+static void window_set_opaque(Client *c);
+static void window_set_translucent(Client *c);
+static void window_set_invisible(Client *c);
+static void window_set_opacity(Client *c, int opacity_index);
+static Client* choose_slave (Client *master, Client *focused);
+static void update_window_opacities(Monitor *m);
+void
+window_set_opacity(Client *c, int opacity_index) {
+       if (c->opacity == opacity_index) {
+               return;
+       }
+       c->opacity = opacity_index;
+       if (opacity_index == 0) {
+               XDeleteProperty(dpy, c->win, netatom[NetWMWindowOpacity]);
+       } else {
+               XChangeProperty(dpy, c->win, netatom[NetWMWindowOpacity], XA_CARDINAL, 32, PropModeReplace, (unsigned char *)(&opacities[opacity_index]), 1);
+       }
+}
+void
+window_set_opaque(Client *c) {
+       window_set_opacity(c, 0);
+}
+void
+window_set_translucent(Client *c) {
+       window_set_opacity(c, 1);
+}
+void
+window_set_invisible(Client *c) {
+       window_set_opacity(c, 2);
+}
+void
+update_window_opacities(Monitor *m) {
+       Client *master, *slave, *focused, *c;
+       Bool focused_is_floating = False;
+       master = nexttiled(m->clients);
+       focused = (m->sel && ISVISIBLE(m->sel)) ? m->sel : NULL;
+       slave = choose_slave(master, focused);
+       // detect if the focused window is floating (and visible)
+       if (master && focused && focused != slave && focused != master) {
+               if (nexttiled(focused) != focused) {
+                       focused_is_floating = True;
+               }
+       }
+       // set opacity of visible floaters, master and slave
+       for (c = nextvisible(m->clients); c; c = nextvisible(c->next)) {
+               if (c->isfloating || c == focused || (focused_is_floating && (c == master || c == slave))) {
+                       window_set_opaque(c);
+               } else if (c == master || c == slave) {
+                       window_set_translucent(c);
+               }
+       }
+}
+
+
 /* configuration, allows nested code to access above variables */
 #include "config.h"
 
@@ -300,19 +350,23 @@ applyrules(Client *c) {
        XClassHint ch = { NULL, NULL };
 
        /* rule matching */
-       c->isfloating = c->tags = 0;
+       c->isfloating = c->tags = c->screen_hog = 0;
        XGetClassHint(dpy, c->win, &ch);
        class    = ch.res_class ? ch.res_class : broken;
        instance = ch.res_name  ? ch.res_name  : broken;
 
        for(i = 0; i < LENGTH(rules); i++) {
                r = &rules[i];
-               if((!r->title || strstr(c->name, r->title))
+               if((!r->title || c->name == strstr(c->name, r->title))
                && (!r->class || strstr(class, r->class))
                && (!r->instance || strstr(instance, r->instance)))
                {
                        c->isfloating = r->isfloating;
+                       if(r->isfloating) {
+                               c->x = -1; c->y = -2; // secret code for centered
+                       }
                        c->tags |= r->tags;
+                       c->screen_hog = r->screen_hog;
                        for(m = mons; m && m->num != r->monitor; m = m->next);
                        if(m)
                                c->mon = m;
@@ -328,7 +382,7 @@ applyrules(Client *c) {
 Bool
 applysizehints(Client *c, int *x, int *y, int *w, int *h, Bool interact) {
        Bool baseismin;
-       Monitor *m = c->mon;
+       // Monitor *m = c->mon;
 
        /* set minimum possible */
        *w = MAX(1, *w);
@@ -343,16 +397,17 @@ applysizehints(Client *c, int *x, int *y, int *w, int *h, Bool interact) {
                if(*y + *h + 2 * c->bw < 0)
                        *y = 0;
        }
-       else {
-               if(*x >= m->wx + m->ww)
-                       *x = m->wx + m->ww - WIDTH(c);
-               if(*y >= m->wy + m->wh)
-                       *y = m->wy + m->wh - HEIGHT(c);
-               if(*x + *w + 2 * c->bw <= m->wx)
-                       *x = m->wx;
-               if(*y + *h + 2 * c->bw <= m->wy)
-                       *y = m->wy;
-       }
+       // jason: let windows be offscreen
+       //else {
+       //      if(*x >= m->wx + m->ww)
+       //              *x = m->wx + m->ww - WIDTH(c);
+       //      if(*y >= m->wy + m->wh)
+       //              *y = m->wy + m->wh - HEIGHT(c);
+       //      if(*x + *w + 2 * c->bw <= m->wx)
+       //              *x = m->wx;
+       //      if(*y + *h + 2 * c->bw <= m->wy)
+       //              *y = m->wy;
+       //}
        if(*h < bh)
                *h = bh;
        if(*w < bh)
@@ -412,10 +467,19 @@ arrangemon(Monitor *m) {
 }
 
 void
-attach(Client *c) {
+attach_as_master(Client *c) {
        c->next = c->mon->clients;
        c->mon->clients = c;
 }
+void
+attach(Client *c) {
+       if (c->mon->sel) {
+               c->next = c->mon->sel->next;
+               c->mon->sel->next = c;
+       } else {
+               attach_as_master(c);
+       }
+}
 
 void
 attachstack(Client *c) {
@@ -425,11 +489,23 @@ attachstack(Client *c) {
 
 void
 buttonpress(XEvent *e) {
-       unsigned int i, x, click;
+       unsigned int i, click;
        Arg arg = {0};
        Client *c;
        Monitor *m;
        XButtonPressedEvent *ev = &e->xbutton;
+       Bool called = False;
+
+       for(i = 0; i < LENGTH(buttons); i++) {
+               if(buttons[i].click == ClkAnywhere && buttons[i].button == ev->button
+               && CLEANMASK(buttons[i].mask) == CLEANMASK(ev->state)) {
+                       buttons[i].func(&buttons[i].arg);
+                       called = True;
+               }
+       }
+       if (called) {
+               return;
+       }
 
        click = ClkRootWin;
        /* focus monitor if necessary */
@@ -439,29 +515,21 @@ buttonpress(XEvent *e) {
                focus(NULL);
        }
        if(ev->window == selmon->barwin) {
-               i = x = 0;
-               do
-                       x += TEXTW(tags[i]);
-               while(ev->x >= x && ++i < LENGTH(tags));
-               if(i < LENGTH(tags)) {
-                       click = ClkTagBar;
-                       arg.ui = 1 << i;
-               }
-               else if(ev->x < x + blw)
-                       click = ClkLtSymbol;
-               else if(ev->x > selmon->ww - TEXTW(stext))
-                       click = ClkStatusText;
-               else
-                       click = ClkWinTitle;
-       }
-       else if((c = wintoclient(ev->window))) {
+               return;
+       } else if((c = wintoclient(ev->window))) {
                focus(c);
                click = ClkClientWin;
        }
-       for(i = 0; i < LENGTH(buttons); i++)
+       for(i = 0; i < LENGTH(buttons); i++) {
                if(click == buttons[i].click && buttons[i].func && buttons[i].button == ev->button
-               && CLEANMASK(buttons[i].mask) == CLEANMASK(ev->state))
-                       buttons[i].func(click == ClkTagBar && buttons[i].arg.i == 0 ? &arg : &buttons[i].arg);
+               && CLEANMASK(buttons[i].mask) == CLEANMASK(ev->state)) {
+                       if (click == ClkTagBar && buttons[i].arg.i == 0) {
+                               buttons[i].func(&arg);
+                       } else {
+                               buttons[i].func(&buttons[i].arg);
+                       }
+               }
+       }
 }
 
 void
@@ -485,20 +553,23 @@ cleanup(void) {
        for(m = mons; m; m = m->next)
                while(m->stack)
                        unmanage(m->stack, False);
-       if(dc.font.set)
-               XFreeFontSet(dpy, dc.font.set);
-       else
-               XFreeFont(dpy, dc.font.xfont);
        XUngrabKey(dpy, AnyKey, AnyModifier, root);
-       XFreePixmap(dpy, dc.drawable);
-       XFreeGC(dpy, dc.gc);
-       XFreeCursor(dpy, cursor[CurNormal]);
-       XFreeCursor(dpy, cursor[CurResize]);
-       XFreeCursor(dpy, cursor[CurMove]);
        while(mons)
                cleanupmon(mons);
+       drw_cur_free(drw, cursor[CurNormal]);
+       drw_cur_free(drw, cursor[CurResize]);
+       drw_cur_free(drw, cursor[CurMove]);
+       drw_font_free(dpy, fnt);
+       drw_clr_free(scheme[SchemeNorm].border);
+       drw_clr_free(scheme[SchemeNorm].bg);
+       drw_clr_free(scheme[SchemeNorm].fg);
+       drw_clr_free(scheme[SchemeSel].border);
+       drw_clr_free(scheme[SchemeSel].bg);
+       drw_clr_free(scheme[SchemeSel].fg);
+       drw_free(drw);
        XSync(dpy, False);
        XSetInputFocus(dpy, PointerRoot, RevertToPointerRoot, CurrentTime);
+       XDeleteProperty(dpy, root, netatom[NetActiveWindow]);
 }
 
 void
@@ -541,6 +612,8 @@ clientmessage(XEvent *e) {
                                      || (cme->data.l[0] == 2 /* _NET_WM_STATE_TOGGLE */ && !c->isfullscreen)));
        }
        else if(cme->message_type == netatom[NetActiveWindow]) {
+               // Jason added this so apps can't steal focus:
+               return;
                if(!ISVISIBLE(c)) {
                        c->mon->seltags ^= 1;
                        c->mon->tagset[c->mon->seltags] = c->tags;
@@ -573,14 +646,13 @@ configurenotify(XEvent *e) {
        XConfigureEvent *ev = &e->xconfigure;
        Bool dirty;
 
+       // TODO: updategeom handling sucks, needs to be simplified
        if(ev->window == root) {
-               dirty = (sw != ev->width);
+               dirty = (sw != ev->width || sh != ev->height);
                sw = ev->width;
                sh = ev->height;
                if(updategeom() || dirty) {
-                       if(dc.drawable != 0)
-                               XFreePixmap(dpy, dc.drawable);
-                       dc.drawable = XCreatePixmap(dpy, root, sw, bh, DefaultDepth(dpy, screen));
+                       drw_resize(drw, sw, bh);
                        updatebars();
                        for(m = mons; m; m = m->next)
                                XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, m->ww, bh);
@@ -677,27 +749,32 @@ detach(Client *c) {
        *tc = c->next;
 }
 
+// NOTE: the stack is for z-order and most-recently-focused
+// only mon->clients determines position in visible layout
 void
 detachstack(Client *c) {
-       Client **tc, *t;
-
-       for(tc = &c->mon->stack; *tc && *tc != c; tc = &(*tc)->snext);
-       *tc = c->snext;
-
+       Client *prev = NULL, *next_sel = NULL, *i;
+       for(i = c->mon->stack; i && i != c; i = i->snext) {
+               prev = i;
+       }
        if(c == c->mon->sel) {
-               for(t = c->mon->stack; t && !ISVISIBLE(t); t = t->snext);
-               c->mon->sel = t;
+               // find last visible window before c
+               // WARNING if you detach() before detachstack() this will select last visible window
+               for(i = nextvisible(c->mon->clients); i && i != c; i = nextvisible(i->next))
+                       next_sel = i;
+               // failing that, find first visible window (besides c)
+               if (!next_sel) {
+                       for(i = nextvisible(c->mon->clients); i && i == c; i = nextvisible(i->next));
+                       if (i != c)
+                               next_sel = i;
+               }
+               c->mon->sel = next_sel;
+       }
+       if (prev) {
+               prev->snext = c->snext;
+       } else {
+               c->mon->stack = c->snext;
        }
-}
-
-void
-die(const char *errstr, ...) {
-       va_list ap;
-
-       va_start(ap, errstr);
-       vfprintf(stderr, errstr, ap);
-       va_end(ap);
-       exit(EXIT_FAILURE);
 }
 
 Monitor *
@@ -717,9 +794,8 @@ dirtomon(int dir) {
 
 void
 drawbar(Monitor *m) {
-       int x;
+       int x, xx, w;
        unsigned int i, occ = 0, urg = 0;
-       unsigned long *col;
        Client *c;
 
        for(c = m->clients; c; c = c->next) {
@@ -727,42 +803,38 @@ drawbar(Monitor *m) {
                if(c->isurgent)
                        urg |= c->tags;
        }
-       dc.x = 0;
+       x = 0;
        for(i = 0; i < LENGTH(tags); i++) {
-               dc.w = TEXTW(tags[i]);
-               col = m->tagset[m->seltags] & 1 << i ? dc.sel : dc.norm;
-               drawtext(tags[i], col, urg & 1 << i);
-               drawsquare(m == selmon && selmon->sel && selmon->sel->tags & 1 << i,
-                          occ & 1 << i, urg & 1 << i, col);
-               dc.x += dc.w;
-       }
-       dc.w = blw = TEXTW(m->ltsymbol);
-       drawtext(m->ltsymbol, dc.norm, False);
-       dc.x += dc.w;
-       x = dc.x;
+               w = TEXTW_NOPAD(tags[i]);
+               drw_setscheme(drw, m->tagset[m->seltags] & 1 << i ? &scheme[SchemeSel] : &scheme[SchemeNorm]);
+               drw_text_nopad(drw, x, 0, w, bh, tags[i], urg & 1 << i);
+               x += w;
+       }
+       xx = x;
        if(m == selmon) { /* status is only drawn on selected monitor */
-               dc.w = TEXTW(stext);
-               dc.x = m->ww - dc.w;
-               if(dc.x < x) {
-                       dc.x = x;
-                       dc.w = m->ww - x;
+               w = TEXTW(stext);
+               x = m->ww - w;
+               if(x < xx) {
+                       x = xx;
+                       w = m->ww - xx;
                }
-               drawtext(stext, dc.norm, False);
+               drw_text(drw, x, 0, w, bh, stext, 0);
        }
        else
-               dc.x = m->ww;
-       if((dc.w = dc.x - x) > bh) {
-               dc.x = x;
+               x = m->ww;
+       if((w = x - xx) > bh) {
+               x = xx;
                if(m->sel) {
-                       col = m == selmon ? dc.sel : dc.norm;
-                       drawtext(m->sel->name, col, False);
-                       drawsquare(m->sel->isfixed, m->sel->isfloating, False, col);
+                       drw_setscheme(drw, m == selmon ? &scheme[SchemeSel] : &scheme[SchemeNorm]);
+                       drw_text(drw, x, 0, w, bh, m->sel->name, 0);
+                       drw_rect(drw, x, 0, w, bh, m->sel->isfixed, m->sel->isfloating, 0);
+               }
+               else {
+                       drw_setscheme(drw, &scheme[SchemeNorm]);
+                       drw_text(drw, x, 0, w, bh, NULL, 0);
                }
-               else
-                       drawtext(NULL, dc.norm, False);
        }
-       XCopyArea(dpy, dc.drawable, m->barwin, dc.gc, 0, 0, m->ww, bh, 0, 0);
-       XSync(dpy, False);
+       drw_map(drw, m->barwin, 0, 0, m->ww, bh);
 }
 
 void
@@ -774,50 +846,13 @@ drawbars(void) {
 }
 
 void
-drawsquare(Bool filled, Bool empty, Bool invert, unsigned long col[ColLast]) {
-       int x;
-
-       XSetForeground(dpy, dc.gc, col[invert ? ColBG : ColFG]);
-       x = (dc.font.ascent + dc.font.descent + 2) / 4;
-       if(filled)
-               XFillRectangle(dpy, dc.drawable, dc.gc, dc.x+1, dc.y+1, x+1, x+1);
-       else if(empty)
-               XDrawRectangle(dpy, dc.drawable, dc.gc, dc.x+1, dc.y+1, x, x);
-}
-
-void
-drawtext(const char *text, unsigned long col[ColLast], Bool invert) {
-       char buf[256];
-       int i, x, y, h, len, olen;
-
-       XSetForeground(dpy, dc.gc, col[invert ? ColFG : ColBG]);
-       XFillRectangle(dpy, dc.drawable, dc.gc, dc.x, dc.y, dc.w, dc.h);
-       if(!text)
-               return;
-       olen = strlen(text);
-       h = dc.font.ascent + dc.font.descent;
-       y = dc.y + (dc.h / 2) - (h / 2) + dc.font.ascent;
-       x = dc.x + (h / 2);
-       /* shorten text if necessary */
-       for(len = MIN(olen, sizeof buf); len && textnw(text, len) > dc.w - h; len--);
-       if(!len)
-               return;
-       memcpy(buf, text, len);
-       if(len < olen)
-               for(i = len; i && i > len - 3; buf[--i] = '.');
-       XSetForeground(dpy, dc.gc, col[invert ? ColBG : ColFG]);
-       if(dc.font.set)
-               XmbDrawString(dpy, dc.drawable, dc.font.set, dc.gc, x, y, buf, len);
-       else
-               XDrawString(dpy, dc.drawable, dc.gc, x, y, buf, len);
-}
-
-void
 enternotify(XEvent *e) {
        Client *c;
        Monitor *m;
        XCrossingEvent *ev = &e->xcrossing;
 
+       return; // jason: added to stop mouse focus
+
        if((ev->mode != NotifyNormal || ev->detail == NotifyInferior) && ev->window != root)
                return;
        c = wintoclient(ev->window);
@@ -855,13 +890,18 @@ focus(Client *c) {
                detachstack(c);
                attachstack(c);
                grabbuttons(c, True);
-               XSetWindowBorder(dpy, c->win, dc.sel[ColBorder]);
+               XSetWindowBorder(dpy, c->win, scheme[SchemeSel].border->rgb);
                setfocus(c);
-       }
-       else
+       } else {
                XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime);
+               XDeleteProperty(dpy, root, netatom[NetActiveWindow]);
+       }
        selmon->sel = c;
+       arrange(selmon);
+       update_window_opacities(selmon);
        drawbars();
+       if(c && (!root || (c->win!=root)) )
+               window_set_opaque(c);
 }
 
 void
@@ -870,6 +910,19 @@ focusin(XEvent *e) { /* there are some broken focus acquiring clients */
 
        if(selmon->sel && ev->window != selmon->sel->win)
                setfocus(selmon->sel);
+
+       if(key_buffering == KeyBufferingAwaitingFocus) {
+               key_buffering = KeyBufferingOff;
+               // there's a few ways to release the grab, with different effects (below)
+               // In this example F2 is mapped to kbspawn
+               //     XAllowEvents(dpy, AsyncKeyboard, CurrentTime);
+               //         swallows key presses while F2 is still down.
+               //     XAllowEvents(dpy, ReplayKeyboard, CurrentTime);
+               //         sends the F2 keydown and all the queued events
+               //     XUngrabKeyboard(dpy, CurrentTime);
+               //         sends everything queued and the F2 key_UP_
+               XUngrabKeyboard(dpy, CurrentTime);
+       }
 }
 
 void
@@ -880,7 +933,8 @@ focusmon(const Arg *arg) {
                return;
        if((m = dirtomon(arg->i)) == selmon)
                return;
-       unfocus(selmon->sel, True);
+       unfocus(selmon->sel, False); /* s/True/False/ fixes input focus issues
+                                       in gedit and anjuta */
        selmon = m;
        focus(NULL);
 }
@@ -891,7 +945,14 @@ focusstack(const Arg *arg) {
 
        if(!selmon->sel)
                return;
-       if(arg->i > 0) {
+       if(arg->i == 0) {
+               for(i = selmon->clients; i != selmon->sel; i = i->next) {
+                       if(ISVISIBLE(i)) {
+                               c = i;
+                               break;
+                       }
+               }
+       } else if(arg->i > 0) {
                for(c = selmon->sel->next; c && !ISVISIBLE(c); c = c->next);
                if(!c)
                        for(c = selmon->clients; c && !ISVISIBLE(c); c = c->next);
@@ -926,16 +987,6 @@ getatomprop(Client *c, Atom prop) {
        return atom;
 }
 
-unsigned long
-getcolor(const char *colstr) {
-       Colormap cmap = DefaultColormap(dpy, screen);
-       XColor color;
-
-       if(!XAllocNamedColor(dpy, cmap, colstr, &color, &color))
-               die("error, cannot allocate color '%s'\n", colstr);
-       return color.pixel;
-}
-
 Bool
 getrootptr(int *x, int *y) {
        int di;
@@ -996,22 +1047,23 @@ grabbuttons(Client *c, Bool focused) {
                XUngrabButton(dpy, AnyButton, AnyModifier, c->win);
                if(focused) {
                        for(i = 0; i < LENGTH(buttons); i++)
-                               if(buttons[i].click == ClkClientWin)
+                               if(buttons[i].click == ClkClientWin || buttons[i].click == ClkAnywhere)
                                        for(j = 0; j < LENGTH(modifiers); j++)
                                                XGrabButton(dpy, buttons[i].button,
                                                            buttons[i].mask | modifiers[j],
                                                            c->win, False, BUTTONMASK,
                                                            GrabModeAsync, GrabModeSync, None, None);
-               }
-               else
+               } else {
                        XGrabButton(dpy, AnyButton, AnyModifier, c->win, False,
                                    BUTTONMASK, GrabModeAsync, GrabModeSync, None, None);
+               }
        }
 }
 
 void
 grabkeys(void) {
        updatenumlockmask();
+       //XUngrabKey(dpy, AnyKey, AnyModifier, root);
        {
                unsigned int i, j;
                unsigned int modifiers[] = { 0, LockMask, numlockmask, numlockmask|LockMask };
@@ -1022,7 +1074,9 @@ grabkeys(void) {
                        if((code = XKeysymToKeycode(dpy, keys[i].keysym)))
                                for(j = 0; j < LENGTH(modifiers); j++)
                                        XGrabKey(dpy, code, keys[i].mod | modifiers[j], root,
-                                                True, GrabModeAsync, GrabModeAsync);
+                                                False, // report keys to focused window too?
+                                                GrabModeAsync, // mouse grab mode
+                                                keys[i].func == kbspawn ? GrabModeSync : GrabModeAsync);
        }
 }
 
@@ -1032,40 +1086,6 @@ incnmaster(const Arg *arg) {
        arrange(selmon);
 }
 
-void
-initfont(const char *fontstr) {
-       char *def, **missing;
-       int n;
-
-       dc.font.set = XCreateFontSet(dpy, fontstr, &missing, &n, &def);
-       if(missing) {
-               while(n--)
-                       fprintf(stderr, "dwm: missing fontset: %s\n", missing[n]);
-               XFreeStringList(missing);
-       }
-       if(dc.font.set) {
-               XFontStruct **xfonts;
-               char **font_names;
-
-               dc.font.ascent = dc.font.descent = 0;
-               XExtentsOfFontSet(dc.font.set);
-               n = XFontsOfFontSet(dc.font.set, &xfonts, &font_names);
-               while(n--) {
-                       dc.font.ascent = MAX(dc.font.ascent, (*xfonts)->ascent);
-                       dc.font.descent = MAX(dc.font.descent,(*xfonts)->descent);
-                       xfonts++;
-               }
-       }
-       else {
-               if(!(dc.font.xfont = XLoadQueryFont(dpy, fontstr))
-               && !(dc.font.xfont = XLoadQueryFont(dpy, "fixed")))
-                       die("error, cannot load font: '%s'\n", fontstr);
-               dc.font.ascent = dc.font.xfont->ascent;
-               dc.font.descent = dc.font.xfont->descent;
-       }
-       dc.font.height = dc.font.ascent + dc.font.descent;
-}
-
 #ifdef XINERAMA
 static Bool
 isuniquegeom(XineramaScreenInfo *unique, size_t n, XineramaScreenInfo *info) {
@@ -1085,11 +1105,13 @@ keypress(XEvent *e) {
 
        ev = &e->xkey;
        keysym = XKeycodeToKeysym(dpy, (KeyCode)ev->keycode, 0);
-       for(i = 0; i < LENGTH(keys); i++)
+       for(i = 0; i < LENGTH(keys); i++) {
                if(keysym == keys[i].keysym
                && CLEANMASK(keys[i].mod) == CLEANMASK(ev->state)
-               && keys[i].func)
+               && keys[i].func) {
                        keys[i].func(&(keys[i].arg));
+               }
+       }
 }
 
 void
@@ -1115,6 +1137,7 @@ manage(Window w, XWindowAttributes *wa) {
 
        if(!(c = calloc(1, sizeof(Client))))
                die("fatal: could not malloc() %u bytes\n", sizeof(Client));
+       c->opacity = -1; // who knows
        c->win = w;
        updatetitle(c);
        if(XGetTransientForHint(dpy, w, &trans) && (t = wintoclient(trans))) {
@@ -1126,8 +1149,13 @@ manage(Window w, XWindowAttributes *wa) {
                applyrules(c);
        }
        /* geometry */
-       c->x = c->oldx = wa->x;
-       c->y = c->oldy = wa->y;
+       if(c->x == -1 && c->y == -2) { // secret code for centered
+               c->x = c->oldx = (c->mon->ww - wa->width) / 2;
+               c->y = c->oldy = (c->mon->wh - wa->height) / 2;
+       } else {
+               c->x = c->oldx = wa->x;
+               c->y = c->oldy = wa->y;
+       }
        c->w = c->oldw = wa->width;
        c->h = c->oldh = wa->height;
        c->oldbw = wa->border_width;
@@ -1144,7 +1172,7 @@ manage(Window w, XWindowAttributes *wa) {
 
        wc.border_width = c->bw;
        XConfigureWindow(dpy, w, CWBorderWidth, &wc);
-       XSetWindowBorder(dpy, w, dc.norm[ColBorder]);
+       XSetWindowBorder(dpy, w, scheme[SchemeNorm].border->rgb);
        configure(c); /* propagates border_width, if size doesn't change */
        updatewindowtype(c);
        updatesizehints(c);
@@ -1157,6 +1185,8 @@ manage(Window w, XWindowAttributes *wa) {
                XRaiseWindow(dpy, c->win);
        attach(c);
        attachstack(c);
+       XChangeProperty(dpy, root, netatom[NetClientList], XA_WINDOW, 32, PropModeAppend,
+                       (unsigned char *) &(c->win), 1);
        XMoveResizeWindow(dpy, c->win, c->x + 2 * sw, c->y, c->w, c->h); /* some windows require this */
        setclientstate(c, NormalState);
        if (c->mon == selmon)
@@ -1164,12 +1194,17 @@ manage(Window w, XWindowAttributes *wa) {
        c->mon->sel = c;
        arrange(c->mon);
        XMapWindow(dpy, c->win);
-       focus(NULL);
+       focus(c);
+       if(key_buffering == KeyBufferingAwaitingWindow) {
+               // almost there! but wait until we are notified that it has focus
+               key_buffering = KeyBufferingAwaitingFocus;
+       }
 }
 
 void
 mappingnotify(XEvent *e) {
        XMappingEvent *ev = &e->xmapping;
+       // fprintf(stderr, "MapNotify\n");
 
        XRefreshKeyboardMapping(ev);
        if(ev->request == MappingKeyboard)
@@ -1181,10 +1216,12 @@ maprequest(XEvent *e) {
        static XWindowAttributes wa;
        XMapRequestEvent *ev = &e->xmaprequest;
 
+       // fprintf(stderr, "MapRequest\n");
        if(!XGetWindowAttributes(dpy, ev->window, &wa))
                return;
-       if(wa.override_redirect)
+       if(wa.override_redirect) {
                return;
+       }
        if(!wintoclient(ev->window))
                manage(ev->window, &wa);
 }
@@ -1199,8 +1236,14 @@ monocle(Monitor *m) {
                        n++;
        if(n > 0) /* override layout symbol */
                snprintf(m->ltsymbol, sizeof m->ltsymbol, "[%d]", n);
-       for(c = nexttiled(m->clients); c; c = nexttiled(c->next))
-               resize(c, m->wx, m->wy, m->ww - 2 * c->bw, m->wh - 2 * c->bw, False);
+       for(c = snexttiled(m->stack); c; c = snexttiled(c->snext)) {
+               if (c == m->sel) {
+                       resize(c, m->wx, m->wy, m->ww - 2 * c->bw, m->wh - 2 * c->bw, False, 0);
+               } else {
+                       // this window is should not be visible. move off top, but don't change h/w
+                       resize(c, m->wx, m->wy - 4000, c->w, c->h, False, 0);
+               }
+       }
 }
 
 void
@@ -1225,14 +1268,17 @@ movemouse(const Arg *arg) {
        Client *c;
        Monitor *m;
        XEvent ev;
+       Time lasttime = 0;
 
        if(!(c = selmon->sel))
                return;
+       if(c->isfullscreen) /* no support moving fullscreen windows by mouse */
+               return;
        restack(selmon);
        ocx = c->x;
        ocy = c->y;
        if(XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync,
-       None, cursor[CurMove], CurrentTime) != GrabSuccess)
+       None, cursor[CurMove]->cursor, CurrentTime) != GrabSuccess)
                return;
        if(!getrootptr(&x, &y))
                return;
@@ -1245,6 +1291,10 @@ movemouse(const Arg *arg) {
                        handler[ev.type](&ev);
                        break;
                case MotionNotify:
+                       if ((ev.xmotion.time - lasttime) <= (1000 / 60))
+                               continue;
+                       lasttime = ev.xmotion.time;
+
                        nx = ocx + (ev.xmotion.x - x);
                        ny = ocy + (ev.xmotion.y - y);
                        if(nx >= selmon->wx && nx <= selmon->wx + selmon->ww
@@ -1261,11 +1311,8 @@ movemouse(const Arg *arg) {
                                && (abs(nx - c->x) > snap || abs(ny - c->y) > snap))
                                        togglefloating(NULL);
                        }
-                       if(!selmon->lt[selmon->sellt]->arrange || c->isfloating) {
-                               if(c->isfullscreen)
-                                       setfullscreen(c, False);
-                               resize(c, nx, ny, c->w, c->h, True);
-                       }
+                       if(!selmon->lt[selmon->sellt]->arrange || c->isfloating)
+                               resize(c, nx, ny, c->w, c->h, True, 0);
                        break;
                }
        } while(ev.type != ButtonRelease);
@@ -1283,10 +1330,22 @@ nexttiled(Client *c) {
        return c;
 }
 
+Client *
+snexttiled(Client *c) {
+       for(; c && (c->isfloating || !ISVISIBLE(c)); c = c->snext);
+       return c;
+}
+
+Client *
+nextvisible(Client *c) {
+       for(; c && !ISVISIBLE(c); c = c->next);
+       return c;
+}
+
 void
 pop(Client *c) {
        detach(c);
-       attach(c);
+       attach_as_master(c);
        focus(c);
        arrange(c->mon);
 }
@@ -1346,40 +1405,49 @@ recttomon(int x, int y, int w, int h) {
 }
 
 void
-resize(Client *c, int x, int y, int w, int h, Bool interact) {
+resize(Client *c, int x, int y, int w, int h, Bool interact, Client *base) {
        if(applysizehints(c, &x, &y, &w, &h, interact))
-               resizeclient(c, x, y, w, h);
+               resizeclient(c, x, y, w, h, base);
 }
 
 void
-resizeclient(Client *c, int x, int y, int w, int h) {
+resizeclient(Client *c, int x, int y, int w, int h, Client *base) {
        XWindowChanges wc;
+       unsigned long mask = CWX|CWY|CWWidth|CWHeight|CWBorderWidth;
 
        c->oldx = c->x; c->x = wc.x = x;
        c->oldy = c->y; c->y = wc.y = y;
        c->oldw = c->w; c->w = wc.width = w;
        c->oldh = c->h; c->h = wc.height = h;
+       // base = 0;
+       if (base) {
+               wc.stack_mode = Above;
+               wc.sibling = base->win;
+               mask |= CWStackMode|CWSibling;
+       }
        wc.border_width = c->bw;
-       XConfigureWindow(dpy, c->win, CWX|CWY|CWWidth|CWHeight|CWBorderWidth, &wc);
+       XConfigureWindow(dpy, c->win, mask, &wc);
        configure(c);
        XSync(dpy, False);
 }
 
 void
 resizemouse(const Arg *arg) {
-       int ocx, ocy;
-       int nw, nh;
+       int ocx, ocy, nw, nh;
        Client *c;
        Monitor *m;
        XEvent ev;
+       Time lasttime = 0;
 
        if(!(c = selmon->sel))
                return;
+       if(c->isfullscreen) /* no support resizing fullscreen windows by mouse */
+               return;
        restack(selmon);
        ocx = c->x;
        ocy = c->y;
        if(XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync,
-                       None, cursor[CurResize], CurrentTime) != GrabSuccess)
+                       None, cursor[CurResize]->cursor, CurrentTime) != GrabSuccess)
                return;
        XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w + c->bw - 1, c->h + c->bw - 1);
        do {
@@ -1391,6 +1459,10 @@ resizemouse(const Arg *arg) {
                        handler[ev.type](&ev);
                        break;
                case MotionNotify:
+                       if ((ev.xmotion.time - lasttime) <= (1000 / 60))
+                               continue;
+                       lasttime = ev.xmotion.time;
+
                        nw = MAX(ev.xmotion.x - ocx - 2 * c->bw + 1, 1);
                        nh = MAX(ev.xmotion.y - ocy - 2 * c->bw + 1, 1);
                        if(c->mon->wx + nw >= selmon->wx && c->mon->wx + nw <= selmon->wx + selmon->ww
@@ -1400,11 +1472,8 @@ resizemouse(const Arg *arg) {
                                && (abs(nw - c->w) > snap || abs(nh - c->h) > snap))
                                        togglefloating(NULL);
                        }
-                       if(!selmon->lt[selmon->sellt]->arrange || c->isfloating) {
-                               if(c->isfullscreen)
-                                       setfullscreen(c, False);
-                               resize(c, c->x, c->y, nw, nh, True);
-                       }
+                       if(!selmon->lt[selmon->sellt]->arrange || c->isfloating)
+                               resize(c, c->x, c->y, nw, nh, True, 0);
                        break;
                }
        } while(ev.type != ButtonRelease);
@@ -1432,10 +1501,11 @@ restack(Monitor *m) {
        if(m->lt[m->sellt]->arrange) {
                wc.stack_mode = Below;
                wc.sibling = m->barwin;
-               for(c = m->stack; c; c = c->snext)
+               for(c = m->clients; c; c = c->next)
                        if(!c->isfloating && ISVISIBLE(c)) {
                                XConfigureWindow(dpy, c->win, CWSibling|CWStackMode, &wc);
                                wc.sibling = c->win;
+                               wc.stack_mode = Above;
                        }
        }
        XSync(dpy, False);
@@ -1447,9 +1517,14 @@ run(void) {
        XEvent ev;
        /* main event loop */
        XSync(dpy, False);
-       while(running && !XNextEvent(dpy, &ev))
-               if(handler[ev.type])
+       while(running && !XNextEvent(dpy, &ev)) {
+               if(handler[ev.type]) {
+                       // fprintf(stderr, "handling event type %i\n", ev.type);
                        handler[ev.type](&ev); /* call handler */
+               } else {
+                       // fprintf(stderr, "evt type %i\n", ev.type);
+               }
+       }
 }
 
 void
@@ -1483,8 +1558,8 @@ sendmon(Client *c, Monitor *m) {
        if(c->mon == m)
                return;
        unfocus(c, True);
-       detach(c);
        detachstack(c);
+       detach(c);
        c->mon = m;
        c->tags = m->tagset[m->seltags]; /* assign tags of target monitor */
        attach(c);
@@ -1527,8 +1602,12 @@ sendevent(Client *c, Atom proto) {
 
 void
 setfocus(Client *c) {
-       if(!c->neverfocus)
+       if(!c->neverfocus) {
                XSetInputFocus(dpy, c->win, RevertToPointerRoot, CurrentTime);
+               XChangeProperty(dpy, root, netatom[NetActiveWindow],
+                               XA_WINDOW, 32, PropModeReplace,
+                               (unsigned char *) &(c->win), 1);
+       }
        sendevent(c, wmatom[WMTakeFocus]);
 }
 
@@ -1542,7 +1621,7 @@ setfullscreen(Client *c, Bool fullscreen) {
                c->oldbw = c->bw;
                c->bw = 0;
                c->isfloating = True;
-               resizeclient(c, c->mon->mx, c->mon->my, c->mon->mw, c->mon->mh);
+               resizeclient(c, c->mon->mx, c->mon->my, c->mon->mw, c->mon->mh, 0);
                XRaiseWindow(dpy, c->win);
        }
        else {
@@ -1555,7 +1634,7 @@ setfullscreen(Client *c, Bool fullscreen) {
                c->y = c->oldy;
                c->w = c->oldw;
                c->h = c->oldh;
-               resizeclient(c, c->x, c->y, c->w, c->h);
+               resizeclient(c, c->x, c->y, c->w, c->h, 0);
                arrange(c->mon);
        }
 }
@@ -1597,10 +1676,12 @@ setup(void) {
        /* init screen */
        screen = DefaultScreen(dpy);
        root = RootWindow(dpy, screen);
-       initfont(font);
+       fnt = drw_font_create(dpy, font);
        sw = DisplayWidth(dpy, screen);
        sh = DisplayHeight(dpy, screen);
-       bh = dc.h = dc.font.height + 2;
+       bh = fnt->h + 2;
+       drw = drw_create(dpy, screen, root, sw, sh);
+       drw_setfont(drw, fnt);
        updategeom();
        /* init atoms */
        wmatom[WMProtocols] = XInternAtom(dpy, "WM_PROTOCOLS", False);
@@ -1612,37 +1693,38 @@ setup(void) {
        netatom[NetWMName] = XInternAtom(dpy, "_NET_WM_NAME", False);
        netatom[NetWMState] = XInternAtom(dpy, "_NET_WM_STATE", False);
        netatom[NetWMFullscreen] = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", False);
+       netatom[NetWMWindowOpacity] = XInternAtom(dpy, "_NET_WM_WINDOW_OPACITY", False);
        netatom[NetWMWindowType] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE", False);
        netatom[NetWMWindowTypeDialog] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DIALOG", False);
+       netatom[NetClientList] = XInternAtom(dpy, "_NET_CLIENT_LIST", False);
+       netatom[NetSupportingWMCheck] = XInternAtom(dpy, "_NET_SUPPORTING_WM_CHECK", False);
+       XInternAtom(dpy, "_MOTIF_WM_HINTS", False); /* clients may request borderless/fullscreen */
        /* init cursors */
-       cursor[CurNormal] = XCreateFontCursor(dpy, XC_left_ptr);
-       cursor[CurResize] = XCreateFontCursor(dpy, XC_sizing);
-       cursor[CurMove] = XCreateFontCursor(dpy, XC_fleur);
+       cursor[CurNormal] = drw_cur_create(drw, XC_left_ptr);
+       cursor[CurResize] = drw_cur_create(drw, XC_sizing);
+       cursor[CurMove] = drw_cur_create(drw, XC_fleur);
        /* init appearance */
-       dc.norm[ColBorder] = getcolor(normbordercolor);
-       dc.norm[ColBG] = getcolor(normbgcolor);
-       dc.norm[ColFG] = getcolor(normfgcolor);
-       dc.sel[ColBorder] = getcolor(selbordercolor);
-       dc.sel[ColBG] = getcolor(selbgcolor);
-       dc.sel[ColFG] = getcolor(selfgcolor);
-       dc.drawable = XCreatePixmap(dpy, root, DisplayWidth(dpy, screen), bh, DefaultDepth(dpy, screen));
-       dc.gc = XCreateGC(dpy, root, 0, NULL);
-       XSetLineAttributes(dpy, dc.gc, 1, LineSolid, CapButt, JoinMiter);
-       if(!dc.font.set)
-               XSetFont(dpy, dc.gc, dc.font.xfont->fid);
+       scheme[SchemeNorm].border = drw_clr_create(drw, normbordercolor);
+       scheme[SchemeNorm].bg = drw_clr_create(drw, normbgcolor);
+       scheme[SchemeNorm].fg = drw_clr_create(drw, normfgcolor);
+       scheme[SchemeSel].border = drw_clr_create(drw, selbordercolor);
+       scheme[SchemeSel].bg = drw_clr_create(drw, selbgcolor);
+       scheme[SchemeSel].fg = drw_clr_create(drw, selfgcolor);
        /* init bars */
        updatebars();
        updatestatus();
        /* EWMH support per view */
        XChangeProperty(dpy, root, netatom[NetSupported], XA_ATOM, 32,
                        PropModeReplace, (unsigned char *) netatom, NetLast);
+       XDeleteProperty(dpy, root, netatom[NetClientList]);
        /* select for events */
-       wa.cursor = cursor[CurNormal];
+       wa.cursor = cursor[CurNormal]->cursor;
        wa.event_mask = SubstructureRedirectMask|SubstructureNotifyMask|ButtonPressMask|PointerMotionMask
                        |EnterWindowMask|LeaveWindowMask|StructureNotifyMask|PropertyChangeMask;
        XChangeWindowAttributes(dpy, root, CWEventMask|CWCursor, &wa);
        XSelectInput(dpy, root, wa.event_mask);
        grabkeys();
+       focus(NULL);
 }
 
 void
@@ -1652,7 +1734,7 @@ showhide(Client *c) {
        if(ISVISIBLE(c)) { /* show clients top down */
                XMoveWindow(dpy, c->win, c->x, c->y);
                if((!c->mon->lt[c->mon->sellt]->arrange || c->isfloating) && !c->isfullscreen)
-                       resize(c, c->x, c->y, c->w, c->h, False);
+                       resize(c, c->x, c->y, c->w, c->h, False, 0);
                showhide(c->snext);
        }
        else { /* hide clients bottom up */
@@ -1669,7 +1751,26 @@ sigchld(int unused) {
 }
 
 void
+kbspawn(const Arg *arg) {
+       key_buffering = True;
+       spawn(arg);
+}
+
+void
 spawn(const Arg *arg) {
+       int tag = 0, i;
+       if(arg->v == termcmd || arg->v == belltermcmd || arg->v == runcmd) {
+               for(i = 0; i < 32; ++i) {
+                       if(selmon->tagset[selmon->seltags] & (1 << i)) {
+                               tag = i;
+                               break;
+                       }
+               }
+               WORKSPACE_NUMBER[17] = workspace_numbers_str[tag][0];
+               WORKSPACE_NUMBER[18] = workspace_numbers_str[tag][1]; // 2nd digit or null
+       }
+       if(arg->v == dmenucmd)
+               dmenumon[0] = '0' + selmon->num;
        if(fork() == 0) {
                if(dpy)
                        close(ConnectionNumber(dpy));
@@ -1697,17 +1798,6 @@ tagmon(const Arg *arg) {
        sendmon(selmon->sel, dirtomon(arg->i));
 }
 
-int
-textnw(const char *text, unsigned int len) {
-       XRectangle r;
-
-       if(dc.font.set) {
-               XmbTextExtents(dc.font.set, text, len, NULL, &r);
-               return r.width;
-       }
-       return XTextWidth(dc.font.xfont, text, len);
-}
-
 void
 tile(Monitor *m) {
        unsigned int i, n, h, mw, my, ty;
@@ -1719,21 +1809,122 @@ tile(Monitor *m) {
 
        if(n > m->nmaster)
                mw = m->nmaster ? m->ww * m->mfact : 0;
-       else
-               mw = m->ww;
+       else {
+               c = nexttiled(m->clients);
+               if (c && !c->screen_hog)
+                       mw = m->ww * m->mfact;
+               else
+                       mw = m->ww;
+       }
        for(i = my = ty = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++)
                if(i < m->nmaster) {
                        h = (m->wh - my) / (MIN(n, m->nmaster) - i);
-                       resize(c, m->wx, m->wy + my, mw - (2*c->bw), h - (2*c->bw), False);
+                       resize(c, m->wx, m->wy + my, mw - (2*c->bw), h - (2*c->bw), False, 0);
                        my += HEIGHT(c);
                }
                else {
                        h = (m->wh - ty) / (n - i);
-                       resize(c, m->wx + mw, m->wy + ty, m->ww - mw - (2*c->bw), h - (2*c->bw), False);
+                       resize(c, m->wx + mw, m->wy + ty, m->ww - mw - (2*c->bw), h - (2*c->bw), False, 0);
                        ty += HEIGHT(c);
                }
 }
 
+// search forward from start, return the last client before end
+Client*
+prev_tiled (Client *start, Client *end) {
+       Client *w, *n;
+       for (w = start; w && (n = nexttiled(w->next)); w = n) {
+               if (n == end) {
+                       return w;
+               }
+       }
+       return NULL;
+}
+
+// return the window to show on the right side
+Client*
+choose_slave (Client *master, Client *focused) {
+       if (!master) {
+               return NULL;
+       }
+       if (focused) {
+               // FIXME put this same algorithm in update_window_opacities
+               if (focused->isfloating) {
+                       // show the window just "under" it
+                       Client *prev = prev_tiled(master, focused);
+                       // fall back to the first slave
+                       if (prev && prev != master) {
+                               return prev;
+                       } else {
+                               return nexttiled(master->next);
+                       }
+               } else {
+                       // focused window is tiled
+                       if (focused == master) {
+                               // master is focused, show first slave
+                               return nexttiled(master->next);
+                       } else {
+                               // focused window is a slave, so show that one
+                               return focused;
+                       }
+               }
+       }
+       // maybe we get called when there's no focused?
+       return nexttiled(master->next);
+}
+
+#define GUTTER_PX   4
+
+void
+jason_layout(Monitor *m) {
+       Client *c, *master, *slave, *focused, *base = 0;
+       unsigned int master_width, slave_width, slave_left;
+
+       // find master
+       master = nexttiled(m->clients);
+
+       // no master? nothing to do
+       if (!master) {
+               return;
+       }
+
+       // find focused and slave
+       focused = (m->sel && ISVISIBLE(m->sel)) ? m->sel : NULL;
+       slave = choose_slave(master, focused);
+
+       // calculate window widths
+       if (!slave && master->screen_hog) {
+               master_width = m->ww;
+       } else {
+               master_width = m->ww * m->mfact - GUTTER_PX / 2;
+       }
+       slave_left = m->ww * m->mfact + GUTTER_PX / 2;
+       slave_width = m->ww * (1 - m->mfact) - GUTTER_PX / 2;
+
+       // resize/reposition master
+       resize(master,
+               m->wx,
+               m->wy,
+               master_width,
+               m->wh,
+               False,
+               0
+       );
+       // resize/reposition slaves
+       base = master; // window to be above in the stacking order
+       for (c = nexttiled(master->next); c; c = nexttiled(c->next)) {
+               resize(c,
+                       slave_left,
+                       c == slave ? m->wy : m->wy - m->wh,
+                       slave_width,
+                       m->wh,
+                       False,
+                       base
+               );
+               base = c;
+       }
+}
+
 void
 togglebar(const Arg *arg) {
        selmon->showbar = !selmon->showbar;
@@ -1746,12 +1937,12 @@ void
 togglefloating(const Arg *arg) {
        if(!selmon->sel)
                return;
+       if(selmon->sel->isfullscreen) /* no support for fullscreen windows */
+               return;
        selmon->sel->isfloating = !selmon->sel->isfloating || selmon->sel->isfixed;
        if(selmon->sel->isfloating)
                resize(selmon->sel, selmon->sel->x, selmon->sel->y,
-                      selmon->sel->w, selmon->sel->h, False);
-       else if(selmon->sel->isfullscreen)
-               setfullscreen(selmon->sel, False);
+                      selmon->sel->w, selmon->sel->h, False, 0);
        arrange(selmon);
 }
 
@@ -1785,9 +1976,11 @@ unfocus(Client *c, Bool setfocus) {
        if(!c)
                return;
        grabbuttons(c, False);
-       XSetWindowBorder(dpy, c->win, dc.norm[ColBorder]);
-       if(setfocus)
+       XSetWindowBorder(dpy, c->win, scheme[SchemeNorm].border->rgb);
+       if(setfocus) {
                XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime);
+               XDeleteProperty(dpy, root, netatom[NetActiveWindow]);
+       }
 }
 
 void
@@ -1796,8 +1989,8 @@ unmanage(Client *c, Bool destroyed) {
        XWindowChanges wc;
 
        /* The server grab construct avoids race conditions. */
-       detach(c);
        detachstack(c);
+       detach(c);
        if(!destroyed) {
                wc.border_width = c->oldbw;
                XGrabServer(dpy);
@@ -1810,7 +2003,8 @@ unmanage(Client *c, Bool destroyed) {
                XUngrabServer(dpy);
        }
        free(c);
-       focus(NULL);
+       focus(selmon ? selmon->sel : NULL);
+       updateclientlist();
        arrange(m);
 }
 
@@ -1841,7 +2035,13 @@ updatebars(void) {
                m->barwin = XCreateWindow(dpy, root, m->wx, m->by, m->ww, bh, 0, DefaultDepth(dpy, screen),
                                          CopyFromParent, DefaultVisual(dpy, screen),
                                          CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa);
-               XDefineCursor(dpy, m->barwin, cursor[CurNormal]);
+               XChangeProperty(dpy, root, netatom[NetSupportingWMCheck], XA_WINDOW, 32,
+                               PropModeReplace, (unsigned char *) &(m->barwin), 1);
+               XChangeProperty(dpy, m->barwin, netatom[NetSupportingWMCheck], XA_WINDOW, 32,
+                               PropModeReplace, (unsigned char *) &(m->barwin), 1);
+               XChangeProperty(dpy, m->barwin, netatom[NetWMName], XA_STRING, 8,
+                               PropModeReplace, (unsigned char *) "dwm", 3);
+               XDefineCursor(dpy, m->barwin, cursor[CurNormal]->cursor);
                XMapRaised(dpy, m->barwin);
        }
 }
@@ -1859,6 +2059,19 @@ updatebarpos(Monitor *m) {
                m->by = -bh;
 }
 
+void
+updateclientlist() {
+       Client *c;
+       Monitor *m;
+
+       XDeleteProperty(dpy, root, netatom[NetClientList]);
+       for(m = mons; m; m = m->next)
+               for(c = m->clients; c; c = c->next)
+                       XChangeProperty(dpy, root, netatom[NetClientList],
+                                       XA_WINDOW, 32, PropModeAppend,
+                                       (unsigned char *) &(c->win), 1);
+}
+
 Bool
 updategeom(void) {
        Bool dirty = False;
@@ -2139,7 +2352,7 @@ zoom(const Arg *arg) {
 int
 main(int argc, char *argv[]) {
        if(argc == 2 && !strcmp("-v", argv[1]))
-               die("dwm-"VERSION", © 2006-2012 dwm engineers, see LICENSE for details\n");
+               die("dwm-"VERSION", © 2006-2014 dwm engineers, see LICENSE for details\n");
        else if(argc != 1)
                die("usage: dwm [-v]\n");
        if(!setlocale(LC_CTYPE, "") || !XSupportsLocale())