JasonWoof Got questions, comments, patches, etc.? Contact Jason Woofenden
made the basics of the tagging concept working -- if people want dynamic tags, that...
[dwm.git] / dwm.c
diff --git a/dwm.c b/dwm.c
index f7253d1..e50dfb8 100644 (file)
--- a/dwm.c
+++ b/dwm.c
@@ -110,12 +110,8 @@ typedef struct {
        Bool isfloating;
 } Rule;
 
-typedef struct {
-       const char name[MAXTAGLEN];
-       unsigned int view;
-} Tag;
-
 struct View {
+       unsigned int id;
        int x, y, w, h, wax, way, wah, waw;
        double mwfact;
        Layout *layout;
@@ -123,7 +119,6 @@ struct View {
 };
 
 /* function declarations */
-void addtag(Client *c, const char *t);
 void applyrules(Client *c);
 void arrange(void);
 void attach(Client *c);
@@ -135,16 +130,18 @@ void cleanup(void);
 void configure(Client *c);
 void configurenotify(XEvent *e);
 void configurerequest(XEvent *e);
+Bool conflicts(Client *c, unsigned int tidx);
 void destroynotify(XEvent *e);
 void detach(Client *c);
 void detachstack(Client *c);
 void drawbar(View *v);
-void drawsquare(View *v, Bool filled, Bool empty, Bool invert, unsigned long col[ColLast]);
-void drawtext(View *v, const char *text, unsigned long col[ColLast], Bool invert);
+void drawsquare(Bool filled, Bool empty, Bool invert, unsigned long col[ColLast]);
+void drawtext(const char *text, unsigned long col[ColLast], Bool invert);
 void *emallocz(unsigned int size);
 void enternotify(XEvent *e);
 void eprint(const char *errstr, ...);
 void expose(XEvent *e);
+unsigned int firstag(View *v);
 void floating(View *v); /* default floating layout */
 void focus(Client *c);
 void focusin(XEvent *e);
@@ -152,8 +149,8 @@ void focusnext(const char *arg);
 void focusprev(const char *arg);
 Client *getclient(Window w);
 unsigned long getcolor(const char *colstr);
-View *getviewbar(Window barwin);
 View *getview(Client *c);
+View *getviewbar(Window barwin);
 long getstate(Window w);
 Bool gettextprop(Window w, Atom atom, char *text, unsigned int size);
 void grabbuttons(Client *c, Bool focused);
@@ -211,7 +208,6 @@ void selectview(const char *arg);
 /* variables */
 char stext[256], buf[256];
 int nviews = 1;
-View *selview;
 int screen;
 int (*xerrorxlib)(Display *, XErrorEvent *);
 unsigned int bh, bpos;
@@ -232,7 +228,6 @@ void (*handler[LASTEvent]) (XEvent *) = {
        [UnmapNotify] = unmapnotify
 };
 Atom wmatom[WMLast], netatom[NetLast];
-Bool isxinerama = False;
 Bool domwfact = True;
 Bool dozoom = True;
 Bool otherwm, readin;
@@ -246,25 +241,18 @@ Cursor cursor[CurLast];
 Display *dpy;
 DC dc = {0};
 View *views;
+View *selview;
 Window root;
 
 /* configuration, allows nested code to access above variables */
 #include "config.h"
+#define TAGSZ (LENGTH(tags) * sizeof(Bool))
 
 /* function implementations */
-void
-addtag(Client *c, const char *t) {
-       unsigned int i, tidx = idxoftag(t);
-
-       for(i = 0; i < LENGTH(tags); i++)
-               if(c->tags[i] && vtags[i] != vtags[tidx])
-                       return; /* conflict */
-       c->tags[tidx] = True;
-}
 
 void
 applyrules(Client *c) {
-       unsigned int i;
+       unsigned int i, idx;
        Bool matched = False;
        Rule *r;
        XClassHint ch = { 0 };
@@ -278,8 +266,8 @@ applyrules(Client *c) {
                || (ch.res_name && strstr(ch.res_name, r->prop)))
                {
                        c->isfloating = r->isfloating;
-                       if(r->tag) {
-                               addtag(c, r->tag);
+                       if(r->tag && !conflicts(c, (idx = idxoftag(r->tag)))) {
+                               c->tags[idx] = True;
                                matched = True;
                        }
                }
@@ -288,12 +276,13 @@ applyrules(Client *c) {
                XFree(ch.res_class);
        if(ch.res_name)
                XFree(ch.res_name);
-       if(!matched)
-               memcpy(c->tags, seltags, sizeof initags);
+       if(!matched) {
+               for(i = 0; i < LENGTH(tags); i++)
+                       if(seltags[i] && vtags[i] == selview->id)
+                               c->tags[i] = True;
+       }
 }
 
-
-
 void
 arrange(void) {
        unsigned int i;
@@ -340,11 +329,11 @@ buttonpress(XEvent *e) {
        Client *c;
        XButtonPressedEvent *ev = &e->xbutton;
 
-       View *v = selview;
-
-       if(ev->window == v->barwin) {
+       if(ev->window == selview->barwin) {
                x = 0;
                for(i = 0; i < LENGTH(tags); i++) {
+                       if(&views[vtags[i]] != selview)
+                               continue;
                        x += textw(tags[i]);
                        if(ev->x < x) {
                                if(ev->button == Button1) {
@@ -374,7 +363,7 @@ buttonpress(XEvent *e) {
                        movemouse(c);
                }
                else if(ev->button == Button2) {
-                       if((floating != v->layout->arrange) && c->isfloating)
+                       if((floating != getview(c)->layout->arrange) && c->isfloating)
                                togglefloating(NULL);
                        else
                                zoom(NULL);
@@ -457,7 +446,7 @@ configurenotify(XEvent *e) {
                XFreePixmap(dpy, dc.drawable);
                dc.drawable = XCreatePixmap(dpy, root, DisplayWidth(root, screen), bh, DefaultDepth(dpy, screen));
                XResizeWindow(dpy, v->barwin, v->w, bh);
-               updatebarpos(v);
+               updatebarpos(selview);
                arrange();
        }
 }
@@ -507,6 +496,16 @@ configurerequest(XEvent *e) {
        XSync(dpy, False);
 }
 
+Bool
+conflicts(Client *c, unsigned int tidx) {
+       unsigned int i;
+
+       for(i = 0; i < LENGTH(tags); i++)
+               if(c->tags[i] && vtags[i] != vtags[tidx])
+                       return True; /* conflict */
+       return False;
+}
+
 void
 destroynotify(XEvent *e) {
        Client *c;
@@ -543,19 +542,21 @@ drawbar(View *v) {
        dc.x = 0;
        for(c = stack; c && (!isvisible(c) || getview(c) != v); c = c->snext);
        for(i = 0; i < LENGTH(tags); i++) {
+               if(&views[vtags[i]] != v)
+                       continue;
                dc.w = textw(tags[i]);
                if(seltags[i]) {
-                       drawtext(v, tags[i], dc.sel, isurgent(i));
-                       drawsquare(v, c && c->tags[i], isoccupied(i), isurgent(i), dc.sel);
+                       drawtext(tags[i], dc.sel, isurgent(i));
+                       drawsquare(c && c->tags[i], isoccupied(i), isurgent(i), dc.sel);
                }
                else {
-                       drawtext(v, tags[i], dc.norm, isurgent(i));
-                       drawsquare(v, c && c->tags[i], isoccupied(i), isurgent(i), dc.norm);
+                       drawtext(tags[i], dc.norm, isurgent(i));
+                       drawsquare(c && c->tags[i], isoccupied(i), isurgent(i), dc.norm);
                }
                dc.x += dc.w;
        }
        dc.w = blw;
-       drawtext(v, v->layout->symbol, dc.norm, False);
+       drawtext(v->layout->symbol, dc.norm, False);
        x = dc.x + dc.w;
        if(v == selview) {
                dc.w = textw(stext);
@@ -564,25 +565,25 @@ drawbar(View *v) {
                        dc.x = x;
                        dc.w = v->w - x;
                }
-               drawtext(v, stext, dc.norm, False);
+               drawtext(stext, dc.norm, False);
        }
        else
                dc.x = v->w;
        if((dc.w = dc.x - x) > bh) {
                dc.x = x;
                if(c) {
-                       drawtext(v, c->name, dc.sel, False);
-                       drawsquare(v, False, c->isfloating, False, dc.sel);
+                       drawtext(c->name, dc.sel, False);
+                       drawsquare(False, c->isfloating, False, dc.sel);
                }
                else
-                       drawtext(v, NULL, dc.norm, False);
+                       drawtext(NULL, dc.norm, False);
        }
        XCopyArea(dpy, dc.drawable, v->barwin, dc.gc, 0, 0, v->w, bh, 0, 0);
        XSync(dpy, False);
 }
 
 void
-drawsquare(View *v, Bool filled, Bool empty, Bool invert, unsigned long col[ColLast]) {
+drawsquare(Bool filled, Bool empty, Bool invert, unsigned long col[ColLast]) {
        int x;
        XGCValues gcv;
        XRectangle r = { dc.x, dc.y, dc.w, dc.h };
@@ -603,7 +604,7 @@ drawsquare(View *v, Bool filled, Bool empty, Bool invert, unsigned long col[ColL
 }
 
 void
-drawtext(View *v, const char *text, unsigned long col[ColLast], Bool invert) {
+drawtext(const char *text, unsigned long col[ColLast], Bool invert) {
        int x, y, w, h;
        unsigned int len, olen;
        XRectangle r = { dc.x, dc.y, dc.w, dc.h };
@@ -655,10 +656,8 @@ enternotify(XEvent *e) {
        Client *c;
        XCrossingEvent *ev = &e->xcrossing;
 
-       if(ev->mode != NotifyNormal || ev->detail == NotifyInferior) {
-               if(!isxinerama || ev->window != root)
-                       return;
-       }
+       if((ev->mode != NotifyNormal || ev->detail == NotifyInferior) && ev->window != root)
+               return;
        if((c = getclient(ev->window)))
                focus(c);
        else
@@ -680,10 +679,20 @@ expose(XEvent *e) {
        View *v;
        XExposeEvent *ev = &e->xexpose;
 
-       if(ev->count == 0 && (v = getviewbar(ev->window)))
+       if(ev->count == 0 && ((v = getviewbar(ev->window))))
                drawbar(v);
 }
 
+unsigned int
+firstag(View *v) {
+       unsigned int i;
+
+       for(i = 0; i < LENGTH(tags); i++)
+               if(vtags[i] == v->id)
+                       return i;
+       return 0; /* safe fallback */
+}
+
 void
 floating(View *v) { /* default floating layout */
        Client *c;
@@ -699,11 +708,10 @@ focus(Client *c) {
        View *v = selview;
        if(c)
                selview = getview(c);
-       else
-               selview = viewat();
        if(selview != v)
                drawbar(v);
        if(!c || (c && !isvisible(c)))
+               /* TODO: isvisible might take getview(c) as constraint? */
                for(c = stack; c && (!isvisible(c) || getview(c) != selview); c = c->snext);
        if(sel && sel != c) {
                grabbuttons(sel, False);
@@ -784,23 +792,23 @@ getcolor(const char *colstr) {
 }
 
 View *
-getviewbar(Window barwin) {
+getview(Client *c) {
        unsigned int i;
 
-       for(i = 0; i < nviews; i++)
-               if(views[i].barwin == barwin)
-                       return &views[i];
+       for(i = 0; i < LENGTH(tags); i++)
+               if(c->tags[i])
+                       return &views[vtags[i]];
        return NULL;
 }
 
 View *
-getview(Client *c) {
+getviewbar(Window barwin) {
        unsigned int i;
 
-       for(i = 0; i < LENGTH(tags); i++)
-               if(c->tags[i])
-                       return &views[c->tags[i] - 1];
-       return &views[0]; /* fallback */
+       for(i = 0; i < nviews; i++)
+               if(views[i].barwin == barwin)
+                       return &views[i];
+       return NULL;
 }
 
 long
@@ -918,7 +926,7 @@ idxoftag(const char *t) {
        unsigned int i;
 
        for(i = 0; (i < LENGTH(tags)) && (tags[i] != t); i++);
-       return (i < LENGTH(tags)) ? i : 0;
+       return (i < LENGTH(tags)) ? i : firstag(selview);
 }
 
 void
@@ -1053,7 +1061,7 @@ manage(Window w, XWindowAttributes *wa) {
        XWindowChanges wc;
 
        c = emallocz(sizeof(Client));
-       c->tags = emallocz(sizeof initags);
+       c->tags = emallocz(TAGSZ);
        c->win = w;
 
        applyrules(c);
@@ -1093,7 +1101,7 @@ manage(Window w, XWindowAttributes *wa) {
        if((rettrans = XGetTransientForHint(dpy, w, &trans) == Success))
                for(t = clients; t && t->win != trans; t = t->next);
        if(t)
-               memcpy(c->tags, t->tags, sizeof initags);
+               memcpy(c->tags, t->tags, TAGSZ);
        if(!c->isfloating)
                c->isfloating = (rettrans == Success) || c->isfixed;
        attach(c);
@@ -1484,13 +1492,12 @@ setlayout(const char *arg) {
        if(sel)
                arrange();
        else
-               drawbar(v);
+               drawbar(selview);
 }
 
 void
 setmwfact(const char *arg) {
        double delta;
-
        View *v = selview;
 
        if(!domwfact)
@@ -1513,7 +1520,7 @@ setmwfact(const char *arg) {
 
 void
 setup(void) {
-       unsigned int i;
+       unsigned int i, j;
        View *v;
        XSetWindowAttributes wa;
        XineramaScreenInfo *info = NULL;
@@ -1531,13 +1538,13 @@ setup(void) {
        cursor[CurResize] = XCreateFontCursor(dpy, XC_sizing);
        cursor[CurMove] = XCreateFontCursor(dpy, XC_fleur);
 
-       if((isxinerama = XineramaIsActive(dpy)))
+       if(XineramaIsActive(dpy))
                info = XineramaQueryScreens(dpy, &nviews);
+
 #if defined(AIM_XINERAMA)
-isxinerama = True;
 nviews = 2; /* aim Xinerama */
 #endif
-       selview = views = emallocz(nviews * sizeof(View));
+       views = emallocz(nviews * sizeof(View));
 
        screen = DefaultScreen(dpy);
        root = RootWindow(dpy, screen);
@@ -1563,16 +1570,24 @@ nviews = 2; /* aim Xinerama */
                        blw = i;
        }
 
-       seltags = emallocz(sizeof initags);
-       prevtags = emallocz(sizeof initags);
-       memcpy(seltags, initags, sizeof initags);
-       memcpy(prevtags, initags, sizeof initags);
+       seltags = emallocz(TAGSZ);
+       prevtags = emallocz(TAGSZ);
+
+       /* check, if vtags need assistance, because there is only 1 view */
+       if(nviews == 1)
+               for(i = 0; i < LENGTH(tags); i++)
+                       vtags[i] = 0;
 
        for(i = 0; i < nviews; i++) {
                /* init geometry */
                v = &views[i];
+               v->id = i;
+
+               /* select first tag of view */
+               j = firstag(v);
+               seltags[j] = prevtags[j] = True; 
 
-               if(nviews != 1 && isxinerama) {
+               if(info) {
 
 #if defined(AIM_XINERAMA)
 v->w = DisplayWidth(dpy, screen) / 2;
@@ -1627,10 +1642,10 @@ v->h = DisplayHeight(dpy, screen);
        if(info)
                XFree(info);
 
+       selview = viewat();
+
        /* grab keys */
        grabkeys();
-
-       selview = &views[0];
 }
 
 void
@@ -1664,8 +1679,10 @@ tag(const char *arg) {
        if(!sel)
                return;
        for(i = 0; i < LENGTH(tags); i++)
-               sel->tags[i] = (NULL == arg);
-       sel->tags[idxoftag(arg)] = True;
+               sel->tags[i] = (NULL == arg) && (vtags[i] == getview(sel)->id);
+       i = idxoftag(arg);
+       if(!conflicts(sel, i))
+               sel->tags[idxoftag(arg)] = True;
        arrange();
 }
 
@@ -1705,8 +1722,6 @@ tile(View *v) {
 
        for(i = 0, c = mc = nexttiled(clients, v); c; c = nexttiled(c->next, v)) {
                if(i == 0) { /* master */
-                       nx = v->wax;
-                       ny = v->way;
                        nw = mw - 2 * c->border;
                        nh = v->wah - 2 * c->border;
                }
@@ -1758,6 +1773,8 @@ toggletag(const char *arg) {
        if(!sel)
                return;
        i = idxoftag(arg);
+       if(conflicts(sel, i))
+               return;
        sel->tags[i] = !sel->tags[i];
        for(j = 0; j < LENGTH(tags) && !sel->tags[j]; j++);
        if(j == LENGTH(tags))
@@ -1913,16 +1930,33 @@ updatewmhints(Client *c) {
 
 void
 view(const char *arg) {
-       unsigned int i;
+       unsigned int i, j;
        Bool tmp[LENGTH(tags)];
 
-       for(i = 0; i < LENGTH(tags); i++)
-               tmp[i] = (NULL == arg);
-       tmp[idxoftag(arg)] = True;
-
-       if(memcmp(seltags, tmp, sizeof initags) != 0) {
-               memcpy(prevtags, seltags, sizeof initags);
-               memcpy(seltags, tmp, sizeof initags);
+       memcpy(tmp, seltags, TAGSZ);
+       if(arg == NULL) {
+               for(i = 0; i < LENGTH(tags); i++)
+                       tmp[i] = (vtags[i] == selview->id);
+       }
+       else {
+               i = idxoftag(arg);
+               for(j = 0; j < LENGTH(tags); j++)
+                       if(selview->id == vtags[i]) {
+                               /* view tag of selview */
+                               if(selview->id == vtags[j])
+                                       tmp[j] = False;
+                       }
+                       else {
+                               /* only touch the view the focus should go */
+                               if(vtags[j] == vtags[i])
+                                       tmp[j] = False;
+                       }
+               selview = &views[vtags[i]];
+               tmp[i] = True;
+       }
+       if(memcmp(seltags, tmp, TAGSZ) != 0) {
+               memcpy(prevtags, seltags, TAGSZ);
+               memcpy(seltags, tmp, TAGSZ);
                arrange();
        }
 }
@@ -1946,9 +1980,9 @@ void
 viewprevtag(const char *arg) {
        static Bool tmp[LENGTH(tags)];
 
-       memcpy(tmp, seltags, sizeof initags);
-       memcpy(seltags, prevtags, sizeof initags);
-       memcpy(prevtags, tmp, sizeof initags);
+       memcpy(tmp, seltags, TAGSZ);
+       memcpy(seltags, prevtags, TAGSZ);
+       memcpy(prevtags, tmp, TAGSZ);
        arrange();
 }