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 3d42441..898b917 100644 (file)
--- a/dwm.c
+++ b/dwm.c
@@ -55,7 +55,8 @@
 #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)                (drw_font_getexts_width(drw->font, X, strlen(X)) + drw->font->h)
+#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 */
@@ -241,13 +242,14 @@ static int xerrordummy(Display *dpy, XErrorEvent *ee);
 static int xerrorstart(Display *dpy, XErrorEvent *ee);
 static void zoom(const Arg *arg);
 
-/* variables */
-static Bool key_buffering = False;
+// 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 *) = {
@@ -282,6 +284,7 @@ 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) {
@@ -309,25 +312,23 @@ window_set_invisible(Client *c) {
 }
 void
 update_window_opacities(Monitor *m) {
-       Client *master, *slave, *c;
-       Bool selection_floating = False;
-       slave = master = nexttiled(m->clients);
-       if (master) slave = nexttiled(master->next);
-       if (m->sel && m->sel != master) {
-               if (nexttiled(m->sel) == m->sel) // if selection is tiled
-                       slave = m->sel;
-               else
-                       selection_floating = True;
-       }
-       for (c = m->clients; c; c = c->next) {
-               if (ISVISIBLE(c)) {
-                       if (c->isfloating || c == m->sel || (selection_floating && (c == master || c == slave))) {
-                               window_set_opaque(c);
-                       } else if (c == master || c == slave) {
-                               window_set_translucent(c);
-                       } else {
-                               window_set_opaque(c);
-                       }
+       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);
                }
        }
 }
@@ -804,11 +805,9 @@ drawbar(Monitor *m) {
        }
        x = 0;
        for(i = 0; i < LENGTH(tags); i++) {
-               w = TEXTW(tags[i]);
+               w = TEXTW_NOPAD(tags[i]);
                drw_setscheme(drw, m->tagset[m->seltags] & 1 << i ? &scheme[SchemeSel] : &scheme[SchemeNorm]);
-               drw_text(drw, x, 0, w, bh, tags[i], urg & 1 << i);
-               drw_rect(drw, x, 0, w, bh, m == selmon && selmon->sel && selmon->sel->tags & 1 << i,
-                          occ & 1 << i, urg & 1 << i);
+               drw_text_nopad(drw, x, 0, w, bh, tags[i], urg & 1 << i);
                x += w;
        }
        xx = x;
@@ -893,8 +892,7 @@ focus(Client *c) {
                grabbuttons(c, True);
                XSetWindowBorder(dpy, c->win, scheme[SchemeSel].border->rgb);
                setfocus(c);
-       }
-       else {
+       } else {
                XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime);
                XDeleteProperty(dpy, root, netatom[NetActiveWindow]);
        }
@@ -912,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
@@ -1063,7 +1074,8 @@ 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,
+                                                False, // report keys to focused window too?
+                                                GrabModeAsync, // mouse grab mode
                                                 keys[i].func == kbspawn ? GrabModeSync : GrabModeAsync);
        }
 }
@@ -1183,9 +1195,9 @@ manage(Window w, XWindowAttributes *wa) {
        arrange(c->mon);
        XMapWindow(dpy, c->win);
        focus(c);
-       if(key_buffering) {
-               key_buffering = False;
-               XAllowEvents(dpy, AsyncKeyboard, CurrentTime);
+       if(key_buffering == KeyBufferingAwaitingWindow) {
+               // almost there! but wait until we are notified that it has focus
+               key_buffering = KeyBufferingAwaitingFocus;
        }
 }
 
@@ -1593,8 +1605,8 @@ setfocus(Client *c) {
        if(!c->neverfocus) {
                XSetInputFocus(dpy, c->win, RevertToPointerRoot, CurrentTime);
                XChangeProperty(dpy, root, netatom[NetActiveWindow],
-                               XA_WINDOW, 32, PropModeReplace,
-                               (unsigned char *) &(c->win), 1);
+                               XA_WINDOW, 32, PropModeReplace,
+                               (unsigned char *) &(c->win), 1);
        }
        sendevent(c, wmatom[WMTakeFocus]);
 }
@@ -1747,7 +1759,7 @@ kbspawn(const Arg *arg) {
 void
 spawn(const Arg *arg) {
        int tag = 0, i;
-       if(arg->v == termcmd || arg->v == runcmd) {
+       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;
@@ -1817,63 +1829,98 @@ tile(Monitor *m) {
                }
 }
 
-#define TAB_HEIGHT 19
-#define TAB_PAD     7
-#define GUTTER_PX   8
+// 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;
+}
 
-void
-jason_layout(Monitor *m) {
-       unsigned int i, tiled_count, mw, right_width, tab_counts[2] = {0,0}, cur_tab = 0, *tab_count;
-       int tab_top;
-       Client *c, *vis_slave = 0, *base = 0;
-
-       tab_count = &(tab_counts[0]);
-
-       for(tiled_count = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), tiled_count++) {
-               if (tiled_count == 0) { // master
-                       if (c->next) {
-                               if (m->sel && (m->sel == c || m->sel->isfloating || !ISVISIBLE(m->sel))) {
-                                       vis_slave = nexttiled(c->next);
-                               } else {
-                                       vis_slave = m->sel;
-                               }
+// 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 {
-                       if (c == vis_slave) {
-                               tab_count = &(tab_counts[1]);
+                       // focused window is tiled
+                       if (focused == master) {
+                               // master is focused, show first slave
+                               return nexttiled(master->next);
                        } else {
-                               (*tab_count) += 1;
+                               // focused window is a slave, so show that one
+                               return focused;
                        }
                }
        }
-       if(tiled_count == 0) {
+       // 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;
        }
 
-       if(tiled_count > 1 || (tiled_count == 1 && !nexttiled(m->clients)->screen_hog)) {
-               mw = m->ww * m->mfact;
+       // 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 {
-               mw = m->ww;
-       }
-       right_width = m->ww - mw - GUTTER_PX;
-       tab_count = &(tab_counts[0]);
-       tab_top = m->wy - (m->wh - (2 * (TAB_HEIGHT + TAB_PAD))) + TAB_HEIGHT;
-       for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) {
-               if (i == 0) {
-                       resize(c, m->wx, m->wy, mw, m->wh, False, 0);
-               } else {
-                       if (c == vis_slave) {
-                               resize(c, m->wx + mw + GUTTER_PX, m->wy + TAB_HEIGHT + TAB_PAD, right_width, m->wh - 2 * (TAB_HEIGHT + TAB_PAD), False, base);
-                               tab_count = &(tab_counts[1]);
-                               tab_top = m->wy + m->wh - TAB_HEIGHT;
-                               cur_tab = 0;
-                       } else {
-                               // this function does not get called when focus changes
-                               // resize(c, m->wx + m->ww, m->wy, m->ww - mw, m->wh, False);
-                               resize(c, m->wx + mw + GUTTER_PX + right_width * cur_tab / (*tab_count), tab_top, right_width, m->wh - 2 * (TAB_HEIGHT + TAB_PAD), False, base);
-                               cur_tab += 1;
-                       }
-               }
+               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;
        }
 }