JasonWoof Got questions, comments, patches, etc.? Contact Jason Woofenden
implemented reapply for re-applying the tagging rules during runtime, Mod-r
[dwm.git] / dwm.c
diff --git a/dwm.c b/dwm.c
index 487fafb..0af37d6 100644 (file)
--- a/dwm.c
+++ b/dwm.c
@@ -17,9 +17,7 @@
  * set the override_redirect flag.  Clients are organized in a global
  * doubly-linked client list, the focus history is remembered through a global
  * stack list. Each client contains an array of Bools of the same size as the
- * global tags array to indicate the tags of a client.  For each client dwm
- * creates a small title window, which is resized whenever the (_NET_)WM_NAME
- * properties are updated or the client is moved/resized.
+ * global tags array to indicate the tags of a client.  
  *
  * Keys and tagging rules are organized as arrays and defined in config.h.
  *
 /* macros */
 #define BUTTONMASK             (ButtonPressMask | ButtonReleaseMask)
 #define CLEANMASK(mask)                (mask & ~(numlockmask | LockMask))
+#define LENGTH(x)              (sizeof x / sizeof x[0])
+#define MAXTAGLEN              16
 #define MOUSEMASK              (BUTTONMASK | PointerMotionMask)
 
+
 /* enums */
 enum { BarTop, BarBot, BarOff };                       /* bar position */
 enum { CurNormal, CurResize, CurMove, CurLast };       /* cursor */
@@ -136,6 +137,7 @@ void eprint(const char *errstr, ...);
 void expose(XEvent *e);
 void floating(void); /* default floating layout */
 void focus(Client *c);
+void focusin(XEvent *e);
 void focusnext(const char *arg);
 void focusprev(const char *arg);
 Client *getclient(Window w);
@@ -143,9 +145,9 @@ unsigned long getcolor(const char *colstr);
 long getstate(Window w);
 Bool gettextprop(Window w, Atom atom, char *text, unsigned int size);
 void grabbuttons(Client *c, Bool focused);
+void grabkeys(void);
 unsigned int idxoftag(const char *tag);
 void initfont(const char *fontstr);
-Bool isarrange(void (*func)());
 Bool isoccupied(unsigned int t);
 Bool isprotodel(Client *c);
 Bool isvisible(Client *c);
@@ -159,6 +161,7 @@ void movemouse(Client *c);
 Client *nexttiled(Client *c);
 void propertynotify(XEvent *e);
 void quit(const char *arg);
+void reapply(const char *arg);
 void resize(Client *c, int x, int y, int w, int h, Bool sizehints);
 void resizemouse(Client *c);
 void restack(void);
@@ -198,9 +201,6 @@ int screen, sx, sy, sw, sh, wax, way, waw, wah;
 int (*xerrorxlib)(Display *, XErrorEvent *);
 unsigned int bh, bpos;
 unsigned int blw = 0;
-unsigned int ltidx = 0; /* default */
-unsigned int nlayouts = 0;
-unsigned int nrules = 0;
 unsigned int numlockmask = 0;
 void (*handler[LASTEvent]) (XEvent *) = {
        [ButtonPress] = buttonpress,
@@ -208,15 +208,18 @@ void (*handler[LASTEvent]) (XEvent *) = {
        [ConfigureNotify] = configurenotify,
        [DestroyNotify] = destroynotify,
        [EnterNotify] = enternotify,
-       [LeaveNotify] = leavenotify,
        [Expose] = expose,
+       [FocusIn] = focusin,
        [KeyPress] = keypress,
+       [LeaveNotify] = leavenotify,
        [MappingNotify] = mappingnotify,
        [MapRequest] = maprequest,
        [PropertyNotify] = propertynotify,
        [UnmapNotify] = unmapnotify
 };
 Atom wmatom[WMLast], netatom[NetLast];
+Bool domwfact = True;
+Bool dozoom = True;
 Bool otherwm, readin;
 Bool running = True;
 Bool selscreen = True;
@@ -226,15 +229,14 @@ Client *stack = NULL;
 Cursor cursor[CurLast];
 Display *dpy;
 DC dc = {0};
+Layout *layout = NULL;
 Window barwin, root;
 Regs *regs = NULL;
 
 /* configuration, allows nested code to access above variables */
 #include "config.h"
 
-#define NTAGS (sizeof tags / sizeof tags[0])
-Bool seltags[NTAGS] = {[0] = True};
-Bool prevtags[NTAGS] = {[0] = True};
+Bool prevtags[LENGTH(tags)];
 
 /* function implementations */
 void
@@ -250,10 +252,10 @@ applyrules(Client *c) {
        snprintf(buf, sizeof buf, "%s:%s:%s",
                        ch.res_class ? ch.res_class : "",
                        ch.res_name ? ch.res_name : "", c->name);
-       for(i = 0; i < nrules; i++)
+       for(i = 0; i < LENGTH(rules); i++)
                if(regs[i].propregex && !regexec(regs[i].propregex, buf, 1, &tmp, 0)) {
                        c->isfloating = rules[i].isfloating;
-                       for(j = 0; regs[i].tagregex && j < NTAGS; j++) {
+                       for(j = 0; regs[i].tagregex && j < LENGTH(tags); j++) {
                                if(!regexec(regs[i].tagregex, tags[j], 1, &tmp, 0)) {
                                        matched = True;
                                        c->tags[j] = True;
@@ -277,7 +279,7 @@ arrange(void) {
                        unban(c);
                else
                        ban(c);
-       layouts[ltidx].arrange();
+       layout->arrange();
        focus(NULL);
        restack();
 }
@@ -310,9 +312,9 @@ buttonpress(XEvent *e) {
        Client *c;
        XButtonPressedEvent *ev = &e->xbutton;
 
-       if(barwin == ev->window) {
+       if(ev->window == barwin) {
                x = 0;
-               for(i = 0; i < NTAGS; i++) {
+               for(i = 0; i < LENGTH(tags); i++) {
                        x += textw(tags[i]);
                        if(ev->x < x) {
                                if(ev->button == Button1) {
@@ -338,20 +340,20 @@ buttonpress(XEvent *e) {
                if(CLEANMASK(ev->state) != MODKEY)
                        return;
                if(ev->button == Button1) {
-                       if(isarrange(floating) || c->isfloating)
+                       if((layout->arrange == floating) || c->isfloating)
                                restack();
                        else
                                togglefloating(NULL);
                        movemouse(c);
                }
                else if(ev->button == Button2) {
-                       if((ISTILE) && !c->isfixed && c->isfloating)
+                       if((floating != layout->arrange) && c->isfloating)
                                togglefloating(NULL);
                        else
                                zoom(NULL);
                }
                else if(ev->button == Button3 && !c->isfixed) {
-                       if(isarrange(floating) || c->isfloating)
+                       if((floating == layout->arrange) || c->isfloating)
                                restack();
                        else
                                togglefloating(NULL);
@@ -405,9 +407,8 @@ compileregs(void) {
 
        if(regs)
                return;
-       nrules = sizeof rules / sizeof rules[0];
-       regs = emallocz(nrules * sizeof(Regs));
-       for(i = 0; i < nrules; i++) {
+       regs = emallocz(LENGTH(rules) * sizeof(Regs));
+       for(i = 0; i < LENGTH(rules); i++) {
                if(rules[i].prop) {
                        reg = emallocz(sizeof(regex_t));
                        if(regcomp(reg, rules[i].prop, REG_EXTENDED))
@@ -468,7 +469,7 @@ configurerequest(XEvent *e) {
                c->ismax = False;
                if(ev->value_mask & CWBorderWidth)
                        c->border = ev->border_width;
-               if(c->isfixed || c->isfloating || isarrange(floating)) {
+               if(c->isfixed || c->isfloating || (floating == layout->arrange)) {
                        if(ev->value_mask & CWX)
                                c->x = ev->x;
                        if(ev->value_mask & CWY)
@@ -536,7 +537,7 @@ drawbar(void) {
        int i, x;
 
        dc.x = dc.y = 0;
-       for(i = 0; i < NTAGS; i++) {
+       for(i = 0; i < LENGTH(tags); i++) {
                dc.w = textw(tags[i]);
                if(seltags[i]) {
                        drawtext(tags[i], dc.sel);
@@ -549,7 +550,7 @@ drawbar(void) {
                dc.x += dc.w;
        }
        dc.w = blw;
-       drawtext(layouts[ltidx].symbol, dc.norm);
+       drawtext(layout->symbol, dc.norm);
        x = dc.x + dc.w;
        dc.w = textw(stext);
        dc.x = sw - dc.w;
@@ -671,7 +672,7 @@ expose(XEvent *e) {
        XExposeEvent *ev = &e->xexpose;
 
        if(ev->count == 0) {
-               if(barwin == ev->window)
+               if(ev->window == barwin)
                        drawbar();
        }
 }
@@ -680,6 +681,7 @@ void
 floating(void) { /* default floating layout */
        Client *c;
 
+       domwfact = dozoom = False;
        for(c = clients; c; c = c->next)
                if(isvisible(c))
                        resize(c, c->x, c->y, c->w, c->h, True);
@@ -711,6 +713,14 @@ focus(Client *c) {
 }
 
 void
+focusin(XEvent *e) { /* there are some broken focus acquiring clients */
+       XFocusChangeEvent *ev = &e->xfocus;
+
+       if(sel && ev->window != sel->win)
+               XSetInputFocus(dpy, sel->win, RevertToPointerRoot, CurrentTime);
+}
+
+void
 focusnext(const char *arg) {
        Client *c;
 
@@ -794,8 +804,7 @@ gettextprop(Window w, Atom atom, char *text, unsigned int size) {
                strncpy(text, (char *)name.value, size - 1);
        else {
                if(XmbTextPropertyToTextList(dpy, &name, &list, &n) >= Success
-               && n > 0 && *list)
-               {
+               && n > 0 && *list) {
                        strncpy(text, *list, size - 1);
                        XFreeStringList(list);
                }
@@ -842,12 +851,31 @@ grabbuttons(Client *c, Bool focused) {
                                GrabModeAsync, GrabModeSync, None, None);
 }
 
+void
+grabkeys(void)  {
+       unsigned int i;
+       KeyCode code;
+
+       XUngrabKey(dpy, AnyKey, AnyModifier, root);
+       for(i = 0; i < LENGTH(keys); i++) {
+               code = XKeysymToKeycode(dpy, keys[i].keysym);
+               XGrabKey(dpy, code, keys[i].mod, root, True,
+                               GrabModeAsync, GrabModeAsync);
+               XGrabKey(dpy, code, keys[i].mod | LockMask, root, True,
+                               GrabModeAsync, GrabModeAsync);
+               XGrabKey(dpy, code, keys[i].mod | numlockmask, root, True,
+                               GrabModeAsync, GrabModeAsync);
+               XGrabKey(dpy, code, keys[i].mod | numlockmask | LockMask, root, True,
+                               GrabModeAsync, GrabModeAsync);
+       }
+}
+
 unsigned int
 idxoftag(const char *tag) {
        unsigned int i;
 
-       for(i = 0; (i < NTAGS) && (tags[i] != tag); i++);
-       return (i < NTAGS) ? i : 0;
+       for(i = 0; (i < LENGTH(tags)) && (tags[i] != tag); i++);
+       return (i < LENGTH(tags)) ? i : 0;
 }
 
 void
@@ -893,12 +921,6 @@ initfont(const char *fontstr) {
 }
 
 Bool
-isarrange(void (*func)())
-{
-       return func == layouts[ltidx].arrange;
-}
-
-Bool
 isoccupied(unsigned int t) {
        Client *c;
 
@@ -927,7 +949,7 @@ Bool
 isvisible(Client *c) {
        unsigned int i;
 
-       for(i = 0; i < NTAGS; i++)
+       for(i = 0; i < LENGTH(tags); i++)
                if(c->tags[i] && seltags[i])
                        return True;
        return False;
@@ -935,31 +957,13 @@ isvisible(Client *c) {
 
 void
 keypress(XEvent *e) {
-       KEYS
-       unsigned int len = sizeof keys / sizeof keys[0];
        unsigned int i;
-       KeyCode code;
        KeySym keysym;
        XKeyEvent *ev;
 
-       if(!e) { /* grabkeys */
-               XUngrabKey(dpy, AnyKey, AnyModifier, root);
-               for(i = 0; i < len; i++) {
-                       code = XKeysymToKeycode(dpy, keys[i].keysym);
-                       XGrabKey(dpy, code, keys[i].mod, root, True,
-                                       GrabModeAsync, GrabModeAsync);
-                       XGrabKey(dpy, code, keys[i].mod | LockMask, root, True,
-                                       GrabModeAsync, GrabModeAsync);
-                       XGrabKey(dpy, code, keys[i].mod | numlockmask, root, True,
-                                       GrabModeAsync, GrabModeAsync);
-                       XGrabKey(dpy, code, keys[i].mod | numlockmask | LockMask, root, True,
-                                       GrabModeAsync, GrabModeAsync);
-               }
-               return;
-       }
        ev = &e->xkey;
        keysym = XKeycodeToKeysym(dpy, (KeyCode)ev->keycode, 0);
-       for(i = 0; i < len; i++)
+       for(i = 0; i < LENGTH(keys); i++)
                if(keysym == keys[i].keysym
                && CLEANMASK(keys[i].mod) == CLEANMASK(ev->state))
                {
@@ -1033,8 +1037,7 @@ manage(Window w, XWindowAttributes *wa) {
        XSetWindowBorder(dpy, w, dc.norm[ColBorder]);
        configure(c); /* propagates border_width, if size doesn't change */
        updatesizehints(c);
-       XSelectInput(dpy, w,
-               StructureNotifyMask | PropertyChangeMask | EnterWindowMask);
+       XSelectInput(dpy, w, EnterWindowMask | FocusChangeMask | PropertyChangeMask | StructureNotifyMask);
        grabbuttons(c, False);
        updatetitle(c);
        if((rettrans = XGetTransientForHint(dpy, w, &trans) == Success))
@@ -1059,7 +1062,7 @@ mappingnotify(XEvent *e) {
 
        XRefreshKeyboardMapping(ev);
        if(ev->request == MappingKeyboard)
-               keypress(NULL);
+               grabkeys();
 }
 
 void
@@ -1137,7 +1140,7 @@ propertynotify(XEvent *e) {
                        default: break;
                        case XA_WM_TRANSIENT_FOR:
                                XGetTransientForHint(dpy, c->win, &trans);
-                               if(!c->isfloating && (c->isfloating = (NULL != getclient(trans))))
+                               if(!c->isfloating && (c->isfloating = (getclient(trans) != NULL)))
                                        arrange();
                                break;
                        case XA_WM_NORMAL_HINTS:
@@ -1158,6 +1161,18 @@ quit(const char *arg) {
 }
 
 void
+reapply(const char *arg) {
+       static Bool zerotags[LENGTH(tags)] = { 0 };
+       Client *c;
+
+       for(c = clients; c; c = c->next) {
+               memcpy(c->tags, zerotags, sizeof zerotags);
+               applyrules(c);
+       }
+       arrange();
+}
+
+void
 resize(Client *c, int x, int y, int w, int h, Bool sizehints) {
        XWindowChanges wc;
 
@@ -1270,9 +1285,9 @@ restack(void) {
        drawbar();
        if(!sel)
                return;
-       if(sel->isfloating || isarrange(floating))
+       if(sel->isfloating || (layout->arrange == floating))
                XRaiseWindow(dpy, sel->win);
-       if(!isarrange(floating)) {
+       if(layout->arrange != floating) {
                wc.stack_mode = Below;
                wc.sibling = barwin;
                if(!sel->isfloating) {
@@ -1293,14 +1308,19 @@ restack(void) {
 void
 run(void) {
        char *p;
-       int r, xfd;
+       char buf[sizeof stext];
        fd_set rd;
+       int r, xfd;
+       unsigned int len, offset;
        XEvent ev;
 
        /* main event loop, also reads status text from stdin */
        XSync(dpy, False);
        xfd = ConnectionNumber(dpy);
        readin = True;
+       offset = 0;
+       len = sizeof stext - 1;
+       buf[len] = stext[len] = '\0'; /* 0-terminator is never touched */
        while(running) {
                FD_ZERO(&rd);
                if(readin)
@@ -1312,10 +1332,9 @@ run(void) {
                        eprint("select failed\n");
                }
                if(FD_ISSET(STDIN_FILENO, &rd)) {
-                       switch(r = read(STDIN_FILENO, stext, sizeof stext - 1)) {
+                       switch((r = read(STDIN_FILENO, buf + offset, len - offset))) {
                        case -1:
-                               strncpy(stext, strerror(errno), sizeof stext - 1);
-                               stext[sizeof stext - 1] = '\0';
+                               strncpy(stext, strerror(errno), len);
                                readin = False;
                                break;
                        case 0:
@@ -1323,10 +1342,18 @@ run(void) {
                                readin = False;
                                break;
                        default:
-                               for(stext[r] = '\0', p = stext + strlen(stext) - 1; p >= stext && *p == '\n'; *p-- = '\0');
-                               for(; p >= stext && *p != '\n'; --p);
-                               if(p > stext)
-                                       strncpy(stext, p + 1, sizeof stext);
+                               for(p = buf + offset; r > 0; p++, r--, offset++)
+                                       if(*p == '\n' || *p == '\0') {
+                                               *p = '\0';
+                                               strncpy(stext, buf, len);
+                                               p += r - 1; /* p is buf + offset + r - 1 */
+                                               for(r = 0; *(p - r) && *(p - r) != '\n'; r++);
+                                               offset = r;
+                                               if(r)
+                                                       memmove(buf, p - r + 1, r);
+                                               break;
+                                       }
+                               break;
                        }
                        drawbar();
                }
@@ -1378,16 +1405,16 @@ setlayout(const char *arg) {
        unsigned int i;
 
        if(!arg) {
-               if(++ltidx == nlayouts)
-                       ltidx = 0;;
+               if(++layout == &layouts[LENGTH(layouts)])
+                       layout = &layouts[0];
        }
        else {
-               for(i = 0; i < nlayouts; i++)
+               for(i = 0; i < LENGTH(layouts); i++)
                        if(!strcmp(arg, layouts[i].symbol))
                                break;
-               if(i == nlayouts)
+               if(i == LENGTH(layouts))
                        return;
-               ltidx = i;
+               layout = &layouts[i];
        }
        if(sel)
                arrange();
@@ -1399,12 +1426,12 @@ void
 setmwfact(const char *arg) {
        double delta;
 
-       if(!(ISTILE))
+       if(!domwfact)
                return;
        /* arg handling, manipulate mwfact */
        if(arg == NULL)
                mwfact = MWFACT;
-       else if(1 == sscanf(arg, "%lf", &delta)) {
+       else if(sscanf(arg, "%lf", &delta) == 1) {
                if(arg[0] == '+' || arg[0] == '-')
                        mwfact += delta;
                else
@@ -1463,9 +1490,10 @@ setup(void) {
        XSelectInput(dpy, root, wa.event_mask);
 
        /* grab keys */
-       keypress(NULL);
+       grabkeys();
 
        /* init tags */
+       memcpy(prevtags, seltags, sizeof seltags);
        compileregs();
 
        /* init appearance */
@@ -1480,8 +1508,8 @@ setup(void) {
 
        /* init layouts */
        mwfact = MWFACT;
-       nlayouts = sizeof layouts / sizeof layouts[0];
-       for(blw = i = 0; i < nlayouts; i++) {
+       layout = &layouts[0];
+       for(blw = i = 0; i < LENGTH(layouts); i++) {
                j = textw(layouts[i].symbol);
                if(j > blw)
                        blw = j;
@@ -1507,6 +1535,7 @@ setup(void) {
 
        /* multihead support */
        selscreen = XQueryPointer(dpy, root, &w, &w, &d, &d, &d, &d, &mask);
+
 }
 
 void
@@ -1539,7 +1568,7 @@ tag(const char *arg) {
 
        if(!sel)
                return;
-       for(i = 0; i < NTAGS; i++)
+       for(i = 0; i < LENGTH(tags); i++)
                sel->tags[i] = (NULL == arg);
        sel->tags[idxoftag(arg)] = True;
        arrange();
@@ -1566,6 +1595,7 @@ tile(void) {
        unsigned int i, n, nx, ny, nw, nh, mw, th;
        Client *c, *mc;
 
+       domwfact = dozoom = True;
        for(n = 0, c = nexttiled(clients); c; c = nexttiled(c->next))
                n++;
 
@@ -1595,8 +1625,8 @@ tile(void) {
                        else
                                nh = th - 2 * c->border;
                }
-               resize(c, nx, ny, nw, nh, True);
-               if((c->h < bh) || (c->h > nh) || (c->w < bh) || (c->w > nw))
+               resize(c, nx, ny, nw, nh, RESIZEHINTS);
+               if((RESIZEHINTS) && ((c->h < bh) || (c->h > nh) || (c->w < bh) || (c->w > nw)))
                        /* client doesn't accept size constraints */
                        resize(c, nx, ny, nw, nh, False);
                if(n > 1 && th != wah)
@@ -1631,7 +1661,7 @@ togglemax(const char *arg) {
        if(!sel || sel->isfixed)
                return;
        if((sel->ismax = !sel->ismax)) {
-               if(isarrange(floating) || sel->isfloating)
+               if((layout->arrange == floating) || sel->isfloating)
                        sel->wasfloating = True;
                else {
                        togglefloating(NULL);
@@ -1660,8 +1690,8 @@ toggletag(const char *arg) {
                return;
        i = idxoftag(arg);
        sel->tags[i] = !sel->tags[i];
-       for(j = 0; j < NTAGS && !sel->tags[j]; j++);
-       if(j == NTAGS)
+       for(j = 0; j < LENGTH(tags) && !sel->tags[j]; j++);
+       if(j == LENGTH(tags))
                sel->tags[i] = True; /* at least one tag must be enabled */
        arrange();
 }
@@ -1672,8 +1702,8 @@ toggleview(const char *arg) {
 
        i = idxoftag(arg);
        seltags[i] = !seltags[i];
-       for(j = 0; j < NTAGS && !seltags[j]; j++);
-       if(j == NTAGS)
+       for(j = 0; j < LENGTH(tags) && !seltags[j]; j++);
+       if(j == LENGTH(tags))
                seltags[i] = True; /* at least one tag must be viewed */
        arrange();
 }
@@ -1839,19 +1869,19 @@ view(const char *arg) {
        unsigned int i;
 
        memcpy(prevtags, seltags, sizeof seltags);
-       for(i = 0; i < NTAGS; i++)
-               seltags[i] = arg == NULL;
+       for(i = 0; i < LENGTH(tags); i++)
+               seltags[i] = (NULL == arg);
        seltags[idxoftag(arg)] = True;
        arrange();
 }
 
 void
 viewprevtag(const char *arg) {
-       static Bool tmptags[sizeof tags / sizeof tags[0]];
+       static Bool tmp[LENGTH(tags)];
 
-       memcpy(tmptags, seltags, sizeof seltags);
+       memcpy(tmp, seltags, sizeof seltags);
        memcpy(seltags, prevtags, sizeof seltags);
-       memcpy(prevtags, tmptags, sizeof seltags);
+       memcpy(prevtags, tmp, sizeof seltags);
        arrange();
 }
 
@@ -1859,7 +1889,7 @@ void
 zoom(const char *arg) {
        Client *c;
 
-       if(!sel || !(ISTILE) || sel->isfloating)
+       if(!sel || !dozoom || sel->isfloating)
                return;
        if((c = sel) == nexttiled(clients))
                if(!(c = nexttiled(c->next)))
@@ -1873,7 +1903,8 @@ zoom(const char *arg) {
 int
 main(int argc, char *argv[]) {
        if(argc == 2 && !strcmp("-v", argv[1]))
-               eprint("dwm-"VERSION", © 2006-2007 A. R. Garbe, S. van Dijk, J. Salmi, P. Hruby, S. Nagy\n");
+               eprint("dwm-"VERSION", © 2006-2007 Anselm R. Garbe, Sander van Dijk, "
+                      "Jukka Salmi, Premysl Hruby, Szabolcs Nagy\n");
        else if(argc != 1)
                eprint("usage: dwm [-v]\n");