From e0a6dee30df84e1f4d63a428a9804bf47d82ca43 Mon Sep 17 00:00:00 2001 From: "Anselm R. Garbe" Date: Sat, 15 Sep 2007 10:31:28 +0200 Subject: [PATCH 01/16] dist target only needs to add config.default.h --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 691e44c..34745a9 100644 --- a/Makefile +++ b/Makefile @@ -35,7 +35,7 @@ clean: dist: clean @echo creating dist tarball @mkdir -p dwm-${VERSION} - @cp -R LICENSE Makefile README config.*.h config.mk \ + @cp -R LICENSE Makefile README config.default.h config.mk \ dwm.1 dwm.h tile.h ${SRC} dwm-${VERSION} @tar -cf dwm-${VERSION}.tar dwm-${VERSION} @gzip dwm-${VERSION}.tar -- 2.20.1 From 5d831eaa94e1ef91e4227298bdd5eec7b2181caf Mon Sep 17 00:00:00 2001 From: "Anselm R. Garbe" Date: Sat, 15 Sep 2007 10:35:18 +0200 Subject: [PATCH 02/16] renamed config.default.h into config.h --- Makefile | 6 +----- config.default.h => config.h | 2 +- 2 files changed, 2 insertions(+), 6 deletions(-) rename config.default.h => config.h (99%) diff --git a/Makefile b/Makefile index 34745a9..385b56e 100644 --- a/Makefile +++ b/Makefile @@ -20,10 +20,6 @@ options: ${OBJ}: dwm.h config.h config.mk -config.h: - @echo creating $@ from config.default.h - @cp config.default.h $@ - dwm: ${OBJ} @echo CC -o $@ @${CC} -o $@ ${OBJ} ${LDFLAGS} @@ -35,7 +31,7 @@ clean: dist: clean @echo creating dist tarball @mkdir -p dwm-${VERSION} - @cp -R LICENSE Makefile README config.default.h config.mk \ + @cp -R LICENSE Makefile README config.h config.mk \ dwm.1 dwm.h tile.h ${SRC} dwm-${VERSION} @tar -cf dwm-${VERSION}.tar dwm-${VERSION} @gzip dwm-${VERSION}.tar diff --git a/config.default.h b/config.h similarity index 99% rename from config.default.h rename to config.h index b016174..cf24e67 100644 --- a/config.default.h +++ b/config.h @@ -8,7 +8,7 @@ #ifdef DARK #define NORMBORDERCOLOR "#222" #define NORMBGCOLOR "#000" -#define NORMFGCOLOR "#eee" +#define NORMFGCOLOR "#ddd" #define SELBORDERCOLOR "#09f" #else #define NORMBORDERCOLOR "#666" -- 2.20.1 From 9800518ae3505e54c0e52ea3d40050f279e722d6 Mon Sep 17 00:00:00 2001 From: "Anselm R. Garbe" Date: Sat, 15 Sep 2007 12:36:42 +0200 Subject: [PATCH 03/16] renamed drawstatus into drawbar --- client.c | 2 +- draw.c | 2 +- dwm.h | 2 +- event.c | 4 ++-- main.c | 4 ++-- screen.c | 6 +++--- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/client.c b/client.c index cd16e11..c716625 100644 --- a/client.c +++ b/client.c @@ -145,7 +145,7 @@ focus(Client *c) { grabbuttons(c, True); } sel = c; - drawstatus(); + drawbar(); if(!selscreen) return; if(c) { diff --git a/draw.c b/draw.c index f7135d7..eaad7fc 100644 --- a/draw.c +++ b/draw.c @@ -49,7 +49,7 @@ textnw(const char *text, unsigned int len) { /* extern */ void -drawstatus(void) { +drawbar(void) { int i, x; dc.x = dc.y = 0; diff --git a/dwm.h b/dwm.h index 1a12322..cc8fd1a 100644 --- a/dwm.h +++ b/dwm.h @@ -104,7 +104,7 @@ void updatesizehints(Client *c); /* update the size hint variables of c */ void updatetitle(Client *c); /* update the name of c */ /* draw.c */ -void drawstatus(void); /* draw the bar */ +void drawbar(void); /* draw the bar */ void drawtext(const char *text, unsigned long col[ColLast]); /* draw text */ unsigned int textw(const char *text); /* return the width of text in px*/ diff --git a/event.c b/event.c index 9e83178..16201f3 100644 --- a/event.c +++ b/event.c @@ -245,7 +245,7 @@ expose(XEvent *e) { if(ev->count == 0) { if(barwin == ev->window) - drawstatus(); + drawbar(); } } @@ -322,7 +322,7 @@ propertynotify(XEvent *e) { if(ev->atom == XA_WM_NAME || ev->atom == netatom[NetWMName]) { updatetitle(c); if(c == sel) - drawstatus(); + drawbar(); } } } diff --git a/main.c b/main.c index 338fed4..26bec25 100644 --- a/main.c +++ b/main.c @@ -326,7 +326,7 @@ main(int argc, char *argv[]) { xerrorxlib = XSetErrorHandler(xerror); XSync(dpy, False); setup(); - drawstatus(); + drawbar(); scan(); /* main event loop, also reads status text from stdin */ @@ -359,7 +359,7 @@ main(int argc, char *argv[]) { if(p > stext) strncpy(stext, p + 1, sizeof stext); } - drawstatus(); + drawbar(); } while(XPending(dpy)) { XNextEvent(dpy, &ev); diff --git a/screen.c b/screen.c index 255184a..f793c5d 100644 --- a/screen.c +++ b/screen.c @@ -213,7 +213,7 @@ restack(void) { XEvent ev; XWindowChanges wc; - drawstatus(); + drawbar(); if(!sel) return; if(sel->isfloating || isfloating()) @@ -255,7 +255,7 @@ setlayout(const char *arg) { if(sel) arrange(); else - drawstatus(); + drawbar(); } void @@ -307,7 +307,7 @@ togglemax(const char *arg) { } else resize(sel, sel->rx, sel->ry, sel->rw, sel->rh, True); - drawstatus(); + drawbar(); while(XCheckMaskEvent(dpy, EnterWindowMask, &ev)); } -- 2.20.1 From 7e476fb86ba254e6fe3a916ed7b5298e432a469c Mon Sep 17 00:00:00 2001 From: "Anselm R. Garbe" Date: Sat, 15 Sep 2007 13:16:54 +0200 Subject: [PATCH 04/16] moved bar-related stuff to bar.c (merged draw.c into that) --- Makefile | 2 +- bar.c | 258 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ draw.c | 134 ----------------------------- dwm.h | 14 +-- main.c | 84 +----------------- screen.c | 36 -------- 6 files changed, 269 insertions(+), 259 deletions(-) create mode 100644 bar.c delete mode 100644 draw.c diff --git a/Makefile b/Makefile index 385b56e..8e48185 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ include config.mk -SRC += client.c draw.c event.c main.c screen.c util.c +SRC += bar.c client.c event.c main.c screen.c util.c OBJ = ${SRC:.c=.o} all: options dwm diff --git a/bar.c b/bar.c new file mode 100644 index 0000000..9e3c386 --- /dev/null +++ b/bar.c @@ -0,0 +1,258 @@ +/* See LICENSE file for copyright and license details. */ +#include "dwm.h" +#include +#include + +/* static */ + +static void +drawsquare(Bool filled, Bool empty, unsigned long col[ColLast]) { + int x; + XGCValues gcv; + XRectangle r = { dc.x, dc.y, dc.w, dc.h }; + + gcv.foreground = col[ColFG]; + XChangeGC(dpy, dc.gc, GCForeground, &gcv); + x = (dc.font.ascent + dc.font.descent + 2) / 4; + r.x = dc.x + 1; + r.y = dc.y + 1; + if(filled) { + r.width = r.height = x + 1; + XFillRectangles(dpy, dc.drawable, dc.gc, &r, 1); + } + else if(empty) { + r.width = r.height = x; + XDrawRectangles(dpy, dc.drawable, dc.gc, &r, 1); + } +} + +static unsigned long +initcolor(const char *colstr) { + Colormap cmap = DefaultColormap(dpy, screen); + XColor color; + + if(!XAllocNamedColor(dpy, cmap, colstr, &color, &color)) + eprint("error, cannot allocate color '%s'\n", colstr); + return color.pixel; +} + +static void +initfont(const char *fontstr) { + char *def, **missing; + int i, n; + + missing = NULL; + if(dc.font.set) + XFreeFontSet(dpy, dc.font.set); + 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) { + XFontSetExtents *font_extents; + XFontStruct **xfonts; + char **font_names; + dc.font.ascent = dc.font.descent = 0; + font_extents = XExtentsOfFontSet(dc.font.set); + n = XFontsOfFontSet(dc.font.set, &xfonts, &font_names); + for(i = 0, dc.font.ascent = 0, dc.font.descent = 0; i < n; i++) { + if(dc.font.ascent < (*xfonts)->ascent) + dc.font.ascent = (*xfonts)->ascent; + if(dc.font.descent < (*xfonts)->descent) + dc.font.descent = (*xfonts)->descent; + xfonts++; + } + } + else { + if(dc.font.xfont) + XFreeFont(dpy, dc.font.xfont); + dc.font.xfont = NULL; + if(!(dc.font.xfont = XLoadQueryFont(dpy, fontstr))) + eprint("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; +} + +static Bool +isoccupied(unsigned int t) { + Client *c; + + for(c = clients; c; c = c->next) + if(c->tags[t]) + return True; + return False; +} + +static unsigned 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); +} + +static void +drawtext(const char *text, unsigned long col[ColLast]) { + int x, y, w, h; + static char buf[256]; + unsigned int len, olen; + XRectangle r = { dc.x, dc.y, dc.w, dc.h }; + + XSetForeground(dpy, dc.gc, col[ColBG]); + XFillRectangles(dpy, dc.drawable, dc.gc, &r, 1); + if(!text) + return; + w = 0; + olen = len = strlen(text); + if(len >= sizeof buf) + len = sizeof buf - 1; + memcpy(buf, text, len); + buf[len] = 0; + 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 */ + while(len && (w = textnw(buf, len)) > dc.w - h) + buf[--len] = 0; + if(len < olen) { + if(len > 1) + buf[len - 1] = '.'; + if(len > 2) + buf[len - 2] = '.'; + if(len > 3) + buf[len - 3] = '.'; + } + if(w > dc.w) + return; /* too long */ + XSetForeground(dpy, dc.gc, col[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); +} + +/* extern */ + +unsigned int bh; +unsigned int bpos = BARPOS; +DC dc = {0}; +Window barwin; + +void +drawbar(void) { + int i, x; + + dc.x = dc.y = 0; + for(i = 0; i < ntags; i++) { + dc.w = textw(tags[i]); + if(seltags[i]) { + drawtext(tags[i], dc.sel); + drawsquare(sel && sel->tags[i], isoccupied(i), dc.sel); + } + else { + drawtext(tags[i], dc.norm); + drawsquare(sel && sel->tags[i], isoccupied(i), dc.norm); + } + dc.x += dc.w; + } + dc.w = blw; + drawtext(getsymbol(), dc.norm); + x = dc.x + dc.w; + dc.w = textw(stext); + dc.x = sw - dc.w; + if(dc.x < x) { + dc.x = x; + dc.w = sw - x; + } + drawtext(stext, dc.norm); + if((dc.w = dc.x - x) > bh) { + dc.x = x; + if(sel) { + drawtext(sel->name, dc.sel); + drawsquare(sel->ismax, sel->isfloating, dc.sel); + } + else + drawtext(NULL, dc.norm); + } + XCopyArea(dpy, dc.drawable, barwin, dc.gc, 0, 0, sw, bh, 0, 0); + XSync(dpy, False); +} + +void +initbar(void) { + XSetWindowAttributes wa; + + dc.norm[ColBorder] = initcolor(NORMBORDERCOLOR); + dc.norm[ColBG] = initcolor(NORMBGCOLOR); + dc.norm[ColFG] = initcolor(NORMFGCOLOR); + dc.sel[ColBorder] = initcolor(SELBORDERCOLOR); + dc.sel[ColBG] = initcolor(SELBGCOLOR); + dc.sel[ColFG] = initcolor(SELFGCOLOR); + initfont(FONT); + dc.h = bh = dc.font.height + 2; + wa.override_redirect = 1; + wa.background_pixmap = ParentRelative; + wa.event_mask = ButtonPressMask | ExposureMask; + barwin = XCreateWindow(dpy, root, sx, sy, sw, bh, 0, + DefaultDepth(dpy, screen), CopyFromParent, DefaultVisual(dpy, screen), + CWOverrideRedirect | CWBackPixmap | CWEventMask, &wa); + XDefineCursor(dpy, barwin, cursor[CurNormal]); + updatebarpos(); + XMapRaised(dpy, barwin); + strcpy(stext, "dwm-"VERSION); + dc.drawable = XCreatePixmap(dpy, root, sw, bh, DefaultDepth(dpy, screen)); + dc.gc = XCreateGC(dpy, root, 0, 0); + XSetLineAttributes(dpy, dc.gc, 1, LineSolid, CapButt, JoinMiter); + if(!dc.font.set) + XSetFont(dpy, dc.gc, dc.font.xfont->fid); +} + +unsigned int +textw(const char *text) { + return textnw(text, strlen(text)) + dc.font.height; +} + +void +togglebar(const char *arg) { + if(bpos == BarOff) + bpos = (BARPOS == BarOff) ? BarTop : BARPOS; + else + bpos = BarOff; + updatebarpos(); + arrange(); +} + +void +updatebarpos(void) { + XEvent ev; + + wax = sx; + way = sy; + wah = sh; + waw = sw; + switch(bpos) { + default: + wah -= bh; + way += bh; + XMoveWindow(dpy, barwin, sx, sy); + break; + case BarBot: + wah -= bh; + XMoveWindow(dpy, barwin, sx, sy + wah); + break; + case BarOff: + XMoveWindow(dpy, barwin, sx, sy - bh); + break; + } + XSync(dpy, False); + while(XCheckMaskEvent(dpy, EnterWindowMask, &ev)); +} + + diff --git a/draw.c b/draw.c deleted file mode 100644 index eaad7fc..0000000 --- a/draw.c +++ /dev/null @@ -1,134 +0,0 @@ -/* See LICENSE file for copyright and license details. */ -#include "dwm.h" -#include - -/* static */ - -static void -drawsquare(Bool filled, Bool empty, unsigned long col[ColLast]) { - int x; - XGCValues gcv; - XRectangle r = { dc.x, dc.y, dc.w, dc.h }; - - gcv.foreground = col[ColFG]; - XChangeGC(dpy, dc.gc, GCForeground, &gcv); - x = (dc.font.ascent + dc.font.descent + 2) / 4; - r.x = dc.x + 1; - r.y = dc.y + 1; - if(filled) { - r.width = r.height = x + 1; - XFillRectangles(dpy, dc.drawable, dc.gc, &r, 1); - } - else if(empty) { - r.width = r.height = x; - XDrawRectangles(dpy, dc.drawable, dc.gc, &r, 1); - } -} - -static Bool -isoccupied(unsigned int t) { - Client *c; - - for(c = clients; c; c = c->next) - if(c->tags[t]) - return True; - return False; -} - -static unsigned 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); -} - -/* extern */ - -void -drawbar(void) { - int i, x; - - dc.x = dc.y = 0; - for(i = 0; i < ntags; i++) { - dc.w = textw(tags[i]); - if(seltags[i]) { - drawtext(tags[i], dc.sel); - drawsquare(sel && sel->tags[i], isoccupied(i), dc.sel); - } - else { - drawtext(tags[i], dc.norm); - drawsquare(sel && sel->tags[i], isoccupied(i), dc.norm); - } - dc.x += dc.w; - } - dc.w = blw; - drawtext(getsymbol(), dc.norm); - x = dc.x + dc.w; - dc.w = textw(stext); - dc.x = sw - dc.w; - if(dc.x < x) { - dc.x = x; - dc.w = sw - x; - } - drawtext(stext, dc.norm); - if((dc.w = dc.x - x) > bh) { - dc.x = x; - if(sel) { - drawtext(sel->name, dc.sel); - drawsquare(sel->ismax, sel->isfloating, dc.sel); - } - else - drawtext(NULL, dc.norm); - } - XCopyArea(dpy, dc.drawable, barwin, dc.gc, 0, 0, sw, bh, 0, 0); - XSync(dpy, False); -} - -void -drawtext(const char *text, unsigned long col[ColLast]) { - int x, y, w, h; - static char buf[256]; - unsigned int len, olen; - XRectangle r = { dc.x, dc.y, dc.w, dc.h }; - - XSetForeground(dpy, dc.gc, col[ColBG]); - XFillRectangles(dpy, dc.drawable, dc.gc, &r, 1); - if(!text) - return; - w = 0; - olen = len = strlen(text); - if(len >= sizeof buf) - len = sizeof buf - 1; - memcpy(buf, text, len); - buf[len] = 0; - 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 */ - while(len && (w = textnw(buf, len)) > dc.w - h) - buf[--len] = 0; - if(len < olen) { - if(len > 1) - buf[len - 1] = '.'; - if(len > 2) - buf[len - 2] = '.'; - if(len > 3) - buf[len - 3] = '.'; - } - if(w > dc.w) - return; /* too long */ - XSetForeground(dpy, dc.gc, col[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); -} - -unsigned int -textw(const char *text) { - return textnw(text, strlen(text)) + dc.font.height; -} diff --git a/dwm.h b/dwm.h index cc8fd1a..6ab6bc3 100644 --- a/dwm.h +++ b/dwm.h @@ -88,6 +88,13 @@ extern DC dc; /* global draw context */ extern Display *dpy; extern Window root, barwin; +/* bar.c */ +void drawbar(void); /* draw the bar */ +void initbar(void); /* initializes the bar */ +unsigned int textw(const char *text); /* return the width of text in px*/ +void togglebar(const char *arg); /* shows/hides the bar */ +void updatebarpos(void); /* updates the bar position */ + /* client.c */ void attach(Client *c); /* attaches c to global client list */ void ban(Client *c); /* bans c */ @@ -103,11 +110,6 @@ void unmanage(Client *c); /* unmanage c */ void updatesizehints(Client *c); /* update the size hint variables of c */ void updatetitle(Client *c); /* update the name of c */ -/* draw.c */ -void drawbar(void); /* draw the bar */ -void drawtext(const char *text, unsigned long col[ColLast]); /* draw text */ -unsigned int textw(const char *text); /* return the width of text in px*/ - /* event.c */ void grabkeys(void); /* grab all keys defined in config.h */ @@ -132,12 +134,10 @@ Client *nexttiled(Client *c); /* returns tiled successor of c */ void restack(void); /* restores z layers of all clients */ void setlayout(const char *arg); /* sets layout, NULL means next layout */ void tag(const char *arg); /* tags sel with arg's index */ -void togglebar(const char *arg); /* shows/hides the bar */ void togglefloating(const char *arg); /* toggles sel between floating/tiled state */ void togglemax(const char *arg); /* toggles maximization of floating client */ void toggletag(const char *arg); /* toggles sel tags with arg's index */ void toggleview(const char *arg); /* toggles the tag with arg's index (in)visible */ -void updatebarpos(void); /* updates the bar position */ void view(const char *arg); /* views the tag with arg's index */ /* util.c */ diff --git a/main.c b/main.c index 26bec25..de38cbc 100644 --- a/main.c +++ b/main.c @@ -17,8 +17,7 @@ char stext[256]; int screen, sx, sy, sw, sh, wax, way, waw, wah; -unsigned int bh, ntags; -unsigned int bpos = BARPOS; +unsigned int ntags; unsigned int numlockmask = 0; Atom wmatom[WMLast], netatom[NetLast]; Bool *seltags; @@ -28,8 +27,7 @@ Client *sel = NULL; Client *stack = NULL; Cursor cursor[CurLast]; Display *dpy; -DC dc = {0}; -Window root, barwin; +Window root; /* static */ @@ -60,57 +58,6 @@ cleanup(void) { free(seltags); } -static unsigned long -initcolor(const char *colstr) { - Colormap cmap = DefaultColormap(dpy, screen); - XColor color; - - if(!XAllocNamedColor(dpy, cmap, colstr, &color, &color)) - eprint("error, cannot allocate color '%s'\n", colstr); - return color.pixel; -} - -static void -initfont(const char *fontstr) { - char *def, **missing; - int i, n; - - missing = NULL; - if(dc.font.set) - XFreeFontSet(dpy, dc.font.set); - 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) { - XFontSetExtents *font_extents; - XFontStruct **xfonts; - char **font_names; - dc.font.ascent = dc.font.descent = 0; - font_extents = XExtentsOfFontSet(dc.font.set); - n = XFontsOfFontSet(dc.font.set, &xfonts, &font_names); - for(i = 0, dc.font.ascent = 0, dc.font.descent = 0; i < n; i++) { - if(dc.font.ascent < (*xfonts)->ascent) - dc.font.ascent = (*xfonts)->ascent; - if(dc.font.descent < (*xfonts)->descent) - dc.font.descent = (*xfonts)->descent; - xfonts++; - } - } - else { - if(dc.font.xfont) - XFreeFont(dpy, dc.font.xfont); - dc.font.xfont = NULL; - if(!(dc.font.xfont = XLoadQueryFont(dpy, fontstr))) - eprint("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; -} - static long getstate(Window w) { int format, status; @@ -197,37 +144,12 @@ setup(void) { for(ntags = 0; tags[ntags]; ntags++); seltags = emallocz(sizeof(Bool) * ntags); seltags[0] = True; - /* style */ - dc.norm[ColBorder] = initcolor(NORMBORDERCOLOR); - dc.norm[ColBG] = initcolor(NORMBGCOLOR); - dc.norm[ColFG] = initcolor(NORMFGCOLOR); - dc.sel[ColBorder] = initcolor(SELBORDERCOLOR); - dc.sel[ColBG] = initcolor(SELBGCOLOR); - dc.sel[ColFG] = initcolor(SELFGCOLOR); - initfont(FONT); /* geometry */ sx = sy = 0; sw = DisplayWidth(dpy, screen); sh = DisplayHeight(dpy, screen); initlayouts(); - /* bar */ - dc.h = bh = dc.font.height + 2; - wa.override_redirect = 1; - wa.background_pixmap = ParentRelative; - wa.event_mask = ButtonPressMask | ExposureMask; - barwin = XCreateWindow(dpy, root, sx, sy, sw, bh, 0, - DefaultDepth(dpy, screen), CopyFromParent, DefaultVisual(dpy, screen), - CWOverrideRedirect | CWBackPixmap | CWEventMask, &wa); - XDefineCursor(dpy, barwin, cursor[CurNormal]); - updatebarpos(); - XMapRaised(dpy, barwin); - strcpy(stext, "dwm-"VERSION); - /* pixmap for everything */ - dc.drawable = XCreatePixmap(dpy, root, sw, bh, DefaultDepth(dpy, screen)); - dc.gc = XCreateGC(dpy, root, 0, 0); - XSetLineAttributes(dpy, dc.gc, 1, LineSolid, CapButt, JoinMiter); - if(!dc.font.set) - XSetFont(dpy, dc.gc, dc.font.xfont->fid); + initbar(); /* multihead support */ selscreen = XQueryPointer(dpy, root, &w, &w, &i, &i, &i, &i, &mask); } diff --git a/screen.c b/screen.c index f793c5d..142ed56 100644 --- a/screen.c +++ b/screen.c @@ -272,16 +272,6 @@ tag(const char *arg) { arrange(); } -void -togglebar(const char *arg) { - if(bpos == BarOff) - bpos = (BARPOS == BarOff) ? BarTop : BARPOS; - else - bpos = BarOff; - updatebarpos(); - arrange(); -} - void togglefloating(const char *arg) { if(!sel) @@ -337,32 +327,6 @@ toggleview(const char *arg) { arrange(); } -void -updatebarpos(void) { - XEvent ev; - - wax = sx; - way = sy; - wah = sh; - waw = sw; - switch(bpos) { - default: - wah -= bh; - way += bh; - XMoveWindow(dpy, barwin, sx, sy); - break; - case BarBot: - wah -= bh; - XMoveWindow(dpy, barwin, sx, sy + wah); - break; - case BarOff: - XMoveWindow(dpy, barwin, sx, sy - bh); - break; - } - XSync(dpy, False); - while(XCheckMaskEvent(dpy, EnterWindowMask, &ev)); -} - void view(const char *arg) { unsigned int i; -- 2.20.1 From 2091200c957783deed032380d56c4199a23c6b81 Mon Sep 17 00:00:00 2001 From: "Anselm R. Garbe" Date: Sat, 15 Sep 2007 20:39:31 +0200 Subject: [PATCH 05/16] new colorscheme (16-bit compliant) --- bar.c | 13 +++++++++---- config.h | 14 +++----------- dwm.h | 1 + main.c | 1 + 4 files changed, 14 insertions(+), 15 deletions(-) diff --git a/bar.c b/bar.c index 9e3c386..91689cf 100644 --- a/bar.c +++ b/bar.c @@ -69,7 +69,8 @@ initfont(const char *fontstr) { if(dc.font.xfont) XFreeFont(dpy, dc.font.xfont); dc.font.xfont = NULL; - if(!(dc.font.xfont = XLoadQueryFont(dpy, fontstr))) + if(!(dc.font.xfont = XLoadQueryFont(dpy, fontstr)) + || !(dc.font.xfont = XLoadQueryFont(dpy, "fixed"))) eprint("error, cannot load font: '%s'\n", fontstr); dc.font.ascent = dc.font.xfont->ascent; dc.font.descent = dc.font.xfont->descent; @@ -186,9 +187,7 @@ drawbar(void) { } void -initbar(void) { - XSetWindowAttributes wa; - +initstyle(void) { dc.norm[ColBorder] = initcolor(NORMBORDERCOLOR); dc.norm[ColBG] = initcolor(NORMBGCOLOR); dc.norm[ColFG] = initcolor(NORMFGCOLOR); @@ -197,6 +196,12 @@ initbar(void) { dc.sel[ColFG] = initcolor(SELFGCOLOR); initfont(FONT); dc.h = bh = dc.font.height + 2; +} + +void +initbar(void) { + XSetWindowAttributes wa; + wa.override_redirect = 1; wa.background_pixmap = ParentRelative; wa.event_mask = ButtonPressMask | ExposureMask; diff --git a/config.h b/config.h index cf24e67..f946407 100644 --- a/config.h +++ b/config.h @@ -4,19 +4,11 @@ #define BARPOS BarTop /* BarBot, BarOff */ #define BORDERPX 1 #define FONT "-*-terminus-medium-r-*-*-12-*-*-*-*-*-iso10646-*" -#define DARK /* LIGHT */ -#ifdef DARK -#define NORMBORDERCOLOR "#222" +#define NORMBORDERCOLOR "#333" #define NORMBGCOLOR "#000" -#define NORMFGCOLOR "#ddd" -#define SELBORDERCOLOR "#09f" -#else -#define NORMBORDERCOLOR "#666" -#define NORMBGCOLOR "#eee" -#define NORMFGCOLOR "#222" +#define NORMFGCOLOR "#ccc" #define SELBORDERCOLOR "#f00" -#endif -#define SELBGCOLOR "#369" +#define SELBGCOLOR "#00f" #define SELFGCOLOR "#fff" /* tagging */ diff --git a/dwm.h b/dwm.h index 6ab6bc3..8c9a141 100644 --- a/dwm.h +++ b/dwm.h @@ -91,6 +91,7 @@ extern Window root, barwin; /* bar.c */ void drawbar(void); /* draw the bar */ void initbar(void); /* initializes the bar */ +void initstyle(void); /* initializes colors and font */ unsigned int textw(const char *text); /* return the width of text in px*/ void togglebar(const char *arg); /* shows/hides the bar */ void updatebarpos(void); /* updates the bar position */ diff --git a/main.c b/main.c index de38cbc..1e1ad4c 100644 --- a/main.c +++ b/main.c @@ -148,6 +148,7 @@ setup(void) { sx = sy = 0; sw = DisplayWidth(dpy, screen); sh = DisplayHeight(dpy, screen); + initstyle(); initlayouts(); initbar(); /* multihead support */ -- 2.20.1 From 0235a84ef286db5c97239615ae525ce973641941 Mon Sep 17 00:00:00 2001 From: "Anselm R. Garbe" Date: Sat, 15 Sep 2007 22:25:27 +0200 Subject: [PATCH 06/16] micromizing dwm step 1 --- Makefile | 6 +- bar.c | 263 -------- client.c | 386 ----------- config.h | 26 +- config.mk | 3 - dwm.c | 1847 +++++++++++++++++++++++++++++++++++++++++++++++++++++ dwm.h | 147 ----- event.c | 375 ----------- main.c | 296 --------- screen.c | 340 ---------- tile.c | 85 --- tile.h | 6 - util.c | 52 -- 13 files changed, 1861 insertions(+), 1971 deletions(-) delete mode 100644 bar.c delete mode 100644 client.c create mode 100644 dwm.c delete mode 100644 dwm.h delete mode 100644 event.c delete mode 100644 main.c delete mode 100644 screen.c delete mode 100644 tile.c delete mode 100644 tile.h delete mode 100644 util.c diff --git a/Makefile b/Makefile index 8e48185..4f03721 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ include config.mk -SRC += bar.c client.c event.c main.c screen.c util.c +SRC = dwm.c OBJ = ${SRC:.c=.o} all: options dwm @@ -18,7 +18,7 @@ options: @echo CC $< @${CC} -c ${CFLAGS} $< -${OBJ}: dwm.h config.h config.mk +${OBJ}: config.h config.mk dwm: ${OBJ} @echo CC -o $@ @@ -32,7 +32,7 @@ dist: clean @echo creating dist tarball @mkdir -p dwm-${VERSION} @cp -R LICENSE Makefile README config.h config.mk \ - dwm.1 dwm.h tile.h ${SRC} dwm-${VERSION} + dwm.1 ${SRC} dwm-${VERSION} @tar -cf dwm-${VERSION}.tar dwm-${VERSION} @gzip dwm-${VERSION}.tar @rm -rf dwm-${VERSION} diff --git a/bar.c b/bar.c deleted file mode 100644 index 91689cf..0000000 --- a/bar.c +++ /dev/null @@ -1,263 +0,0 @@ -/* See LICENSE file for copyright and license details. */ -#include "dwm.h" -#include -#include - -/* static */ - -static void -drawsquare(Bool filled, Bool empty, unsigned long col[ColLast]) { - int x; - XGCValues gcv; - XRectangle r = { dc.x, dc.y, dc.w, dc.h }; - - gcv.foreground = col[ColFG]; - XChangeGC(dpy, dc.gc, GCForeground, &gcv); - x = (dc.font.ascent + dc.font.descent + 2) / 4; - r.x = dc.x + 1; - r.y = dc.y + 1; - if(filled) { - r.width = r.height = x + 1; - XFillRectangles(dpy, dc.drawable, dc.gc, &r, 1); - } - else if(empty) { - r.width = r.height = x; - XDrawRectangles(dpy, dc.drawable, dc.gc, &r, 1); - } -} - -static unsigned long -initcolor(const char *colstr) { - Colormap cmap = DefaultColormap(dpy, screen); - XColor color; - - if(!XAllocNamedColor(dpy, cmap, colstr, &color, &color)) - eprint("error, cannot allocate color '%s'\n", colstr); - return color.pixel; -} - -static void -initfont(const char *fontstr) { - char *def, **missing; - int i, n; - - missing = NULL; - if(dc.font.set) - XFreeFontSet(dpy, dc.font.set); - 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) { - XFontSetExtents *font_extents; - XFontStruct **xfonts; - char **font_names; - dc.font.ascent = dc.font.descent = 0; - font_extents = XExtentsOfFontSet(dc.font.set); - n = XFontsOfFontSet(dc.font.set, &xfonts, &font_names); - for(i = 0, dc.font.ascent = 0, dc.font.descent = 0; i < n; i++) { - if(dc.font.ascent < (*xfonts)->ascent) - dc.font.ascent = (*xfonts)->ascent; - if(dc.font.descent < (*xfonts)->descent) - dc.font.descent = (*xfonts)->descent; - xfonts++; - } - } - else { - if(dc.font.xfont) - XFreeFont(dpy, dc.font.xfont); - dc.font.xfont = NULL; - if(!(dc.font.xfont = XLoadQueryFont(dpy, fontstr)) - || !(dc.font.xfont = XLoadQueryFont(dpy, "fixed"))) - eprint("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; -} - -static Bool -isoccupied(unsigned int t) { - Client *c; - - for(c = clients; c; c = c->next) - if(c->tags[t]) - return True; - return False; -} - -static unsigned 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); -} - -static void -drawtext(const char *text, unsigned long col[ColLast]) { - int x, y, w, h; - static char buf[256]; - unsigned int len, olen; - XRectangle r = { dc.x, dc.y, dc.w, dc.h }; - - XSetForeground(dpy, dc.gc, col[ColBG]); - XFillRectangles(dpy, dc.drawable, dc.gc, &r, 1); - if(!text) - return; - w = 0; - olen = len = strlen(text); - if(len >= sizeof buf) - len = sizeof buf - 1; - memcpy(buf, text, len); - buf[len] = 0; - 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 */ - while(len && (w = textnw(buf, len)) > dc.w - h) - buf[--len] = 0; - if(len < olen) { - if(len > 1) - buf[len - 1] = '.'; - if(len > 2) - buf[len - 2] = '.'; - if(len > 3) - buf[len - 3] = '.'; - } - if(w > dc.w) - return; /* too long */ - XSetForeground(dpy, dc.gc, col[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); -} - -/* extern */ - -unsigned int bh; -unsigned int bpos = BARPOS; -DC dc = {0}; -Window barwin; - -void -drawbar(void) { - int i, x; - - dc.x = dc.y = 0; - for(i = 0; i < ntags; i++) { - dc.w = textw(tags[i]); - if(seltags[i]) { - drawtext(tags[i], dc.sel); - drawsquare(sel && sel->tags[i], isoccupied(i), dc.sel); - } - else { - drawtext(tags[i], dc.norm); - drawsquare(sel && sel->tags[i], isoccupied(i), dc.norm); - } - dc.x += dc.w; - } - dc.w = blw; - drawtext(getsymbol(), dc.norm); - x = dc.x + dc.w; - dc.w = textw(stext); - dc.x = sw - dc.w; - if(dc.x < x) { - dc.x = x; - dc.w = sw - x; - } - drawtext(stext, dc.norm); - if((dc.w = dc.x - x) > bh) { - dc.x = x; - if(sel) { - drawtext(sel->name, dc.sel); - drawsquare(sel->ismax, sel->isfloating, dc.sel); - } - else - drawtext(NULL, dc.norm); - } - XCopyArea(dpy, dc.drawable, barwin, dc.gc, 0, 0, sw, bh, 0, 0); - XSync(dpy, False); -} - -void -initstyle(void) { - dc.norm[ColBorder] = initcolor(NORMBORDERCOLOR); - dc.norm[ColBG] = initcolor(NORMBGCOLOR); - dc.norm[ColFG] = initcolor(NORMFGCOLOR); - dc.sel[ColBorder] = initcolor(SELBORDERCOLOR); - dc.sel[ColBG] = initcolor(SELBGCOLOR); - dc.sel[ColFG] = initcolor(SELFGCOLOR); - initfont(FONT); - dc.h = bh = dc.font.height + 2; -} - -void -initbar(void) { - XSetWindowAttributes wa; - - wa.override_redirect = 1; - wa.background_pixmap = ParentRelative; - wa.event_mask = ButtonPressMask | ExposureMask; - barwin = XCreateWindow(dpy, root, sx, sy, sw, bh, 0, - DefaultDepth(dpy, screen), CopyFromParent, DefaultVisual(dpy, screen), - CWOverrideRedirect | CWBackPixmap | CWEventMask, &wa); - XDefineCursor(dpy, barwin, cursor[CurNormal]); - updatebarpos(); - XMapRaised(dpy, barwin); - strcpy(stext, "dwm-"VERSION); - dc.drawable = XCreatePixmap(dpy, root, sw, bh, DefaultDepth(dpy, screen)); - dc.gc = XCreateGC(dpy, root, 0, 0); - XSetLineAttributes(dpy, dc.gc, 1, LineSolid, CapButt, JoinMiter); - if(!dc.font.set) - XSetFont(dpy, dc.gc, dc.font.xfont->fid); -} - -unsigned int -textw(const char *text) { - return textnw(text, strlen(text)) + dc.font.height; -} - -void -togglebar(const char *arg) { - if(bpos == BarOff) - bpos = (BARPOS == BarOff) ? BarTop : BARPOS; - else - bpos = BarOff; - updatebarpos(); - arrange(); -} - -void -updatebarpos(void) { - XEvent ev; - - wax = sx; - way = sy; - wah = sh; - waw = sw; - switch(bpos) { - default: - wah -= bh; - way += bh; - XMoveWindow(dpy, barwin, sx, sy); - break; - case BarBot: - wah -= bh; - XMoveWindow(dpy, barwin, sx, sy + wah); - break; - case BarOff: - XMoveWindow(dpy, barwin, sx, sy - bh); - break; - } - XSync(dpy, False); - while(XCheckMaskEvent(dpy, EnterWindowMask, &ev)); -} - - diff --git a/client.c b/client.c deleted file mode 100644 index c716625..0000000 --- a/client.c +++ /dev/null @@ -1,386 +0,0 @@ -/* See LICENSE file for copyright and license details. */ -#include "dwm.h" -#include -#include - -/* static */ - -static void -attachstack(Client *c) { - c->snext = stack; - stack = c; -} - -static void -detachstack(Client *c) { - Client **tc; - - for(tc=&stack; *tc && *tc != c; tc=&(*tc)->snext); - *tc = c->snext; -} - -static void -grabbuttons(Client *c, Bool focused) { - XUngrabButton(dpy, AnyButton, AnyModifier, c->win); - - if(focused) { - XGrabButton(dpy, Button1, MODKEY, c->win, False, BUTTONMASK, - GrabModeAsync, GrabModeSync, None, None); - XGrabButton(dpy, Button1, MODKEY | LockMask, c->win, False, BUTTONMASK, - GrabModeAsync, GrabModeSync, None, None); - XGrabButton(dpy, Button1, MODKEY | numlockmask, c->win, False, BUTTONMASK, - GrabModeAsync, GrabModeSync, None, None); - XGrabButton(dpy, Button1, MODKEY | numlockmask | LockMask, c->win, False, BUTTONMASK, - GrabModeAsync, GrabModeSync, None, None); - - XGrabButton(dpy, Button2, MODKEY, c->win, False, BUTTONMASK, - GrabModeAsync, GrabModeSync, None, None); - XGrabButton(dpy, Button2, MODKEY | LockMask, c->win, False, BUTTONMASK, - GrabModeAsync, GrabModeSync, None, None); - XGrabButton(dpy, Button2, MODKEY | numlockmask, c->win, False, BUTTONMASK, - GrabModeAsync, GrabModeSync, None, None); - XGrabButton(dpy, Button2, MODKEY | numlockmask | LockMask, c->win, False, BUTTONMASK, - GrabModeAsync, GrabModeSync, None, None); - - XGrabButton(dpy, Button3, MODKEY, c->win, False, BUTTONMASK, - GrabModeAsync, GrabModeSync, None, None); - XGrabButton(dpy, Button3, MODKEY | LockMask, c->win, False, BUTTONMASK, - GrabModeAsync, GrabModeSync, None, None); - XGrabButton(dpy, Button3, MODKEY | numlockmask, c->win, False, BUTTONMASK, - GrabModeAsync, GrabModeSync, None, None); - XGrabButton(dpy, Button3, MODKEY | numlockmask | LockMask, c->win, False, BUTTONMASK, - GrabModeAsync, GrabModeSync, None, None); - } - else - XGrabButton(dpy, AnyButton, AnyModifier, c->win, False, BUTTONMASK, - GrabModeAsync, GrabModeSync, None, None); -} - -static Bool -isprotodel(Client *c) { - int i, n; - Atom *protocols; - Bool ret = False; - - if(XGetWMProtocols(dpy, c->win, &protocols, &n)) { - for(i = 0; !ret && i < n; i++) - if(protocols[i] == wmatom[WMDelete]) - ret = True; - XFree(protocols); - } - return ret; -} - -static void -setclientstate(Client *c, long state) { - long data[] = {state, None}; - - XChangeProperty(dpy, c->win, wmatom[WMState], wmatom[WMState], 32, - PropModeReplace, (unsigned char *)data, 2); -} - -static int -xerrordummy(Display *dsply, XErrorEvent *ee) { - return 0; -} - -/* extern */ - -void -attach(Client *c) { - if(clients) - clients->prev = c; - c->next = clients; - clients = c; -} - -void -ban(Client *c) { - if(c->isbanned) - return; - XMoveWindow(dpy, c->win, c->x + 2 * sw, c->y); - c->isbanned = True; -} - -void -configure(Client *c) { - XConfigureEvent ce; - - ce.type = ConfigureNotify; - ce.display = dpy; - ce.event = c->win; - ce.window = c->win; - ce.x = c->x; - ce.y = c->y; - ce.width = c->w; - ce.height = c->h; - ce.border_width = c->border; - ce.above = None; - ce.override_redirect = False; - XSendEvent(dpy, c->win, False, StructureNotifyMask, (XEvent *)&ce); -} - -void -detach(Client *c) { - if(c->prev) - c->prev->next = c->next; - if(c->next) - c->next->prev = c->prev; - if(c == clients) - clients = c->next; - c->next = c->prev = NULL; -} - -void -focus(Client *c) { - if((!c && selscreen) || (c && !isvisible(c))) - for(c = stack; c && !isvisible(c); c = c->snext); - if(sel && sel != c) { - grabbuttons(sel, False); - XSetWindowBorder(dpy, sel->win, dc.norm[ColBorder]); - } - if(c) { - detachstack(c); - attachstack(c); - grabbuttons(c, True); - } - sel = c; - drawbar(); - if(!selscreen) - return; - if(c) { - XSetWindowBorder(dpy, c->win, dc.sel[ColBorder]); - XSetInputFocus(dpy, c->win, RevertToPointerRoot, CurrentTime); - } - else - XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime); -} - -void -killclient(const char *arg) { - XEvent ev; - - if(!sel) - return; - if(isprotodel(sel)) { - ev.type = ClientMessage; - ev.xclient.window = sel->win; - ev.xclient.message_type = wmatom[WMProtocols]; - ev.xclient.format = 32; - ev.xclient.data.l[0] = wmatom[WMDelete]; - ev.xclient.data.l[1] = CurrentTime; - XSendEvent(dpy, sel->win, False, NoEventMask, &ev); - } - else - XKillClient(dpy, sel->win); -} - -void -manage(Window w, XWindowAttributes *wa) { - unsigned int i; - Client *c, *t = NULL; - Window trans; - Status rettrans; - XWindowChanges wc; - - c = emallocz(sizeof(Client)); - c->tags = emallocz(ntags * sizeof(Bool)); - c->win = w; - c->x = wa->x; - c->y = wa->y; - c->w = wa->width; - c->h = wa->height; - c->oldborder = wa->border_width; - if(c->w == sw && c->h == sh) { - c->x = sx; - c->y = sy; - c->border = wa->border_width; - } - else { - if(c->x + c->w + 2 * c->border > wax + waw) - c->x = wax + waw - c->w - 2 * c->border; - if(c->y + c->h + 2 * c->border > way + wah) - c->y = way + wah - c->h - 2 * c->border; - if(c->x < wax) - c->x = wax; - if(c->y < way) - c->y = way; - c->border = BORDERPX; - } - wc.border_width = c->border; - XConfigureWindow(dpy, w, CWBorderWidth, &wc); - XSetWindowBorder(dpy, w, dc.norm[ColBorder]); - configure(c); /* propagates border_width, if size doesn't change */ - updatesizehints(c); - XSelectInput(dpy, w, - StructureNotifyMask | PropertyChangeMask | EnterWindowMask); - grabbuttons(c, False); - updatetitle(c); - if((rettrans = XGetTransientForHint(dpy, w, &trans) == Success)) - for(t = clients; t && t->win != trans; t = t->next); - if(t) - for(i = 0; i < ntags; i++) - c->tags[i] = t->tags[i]; - applyrules(c); - if(!c->isfloating) - c->isfloating = (rettrans == Success) || c->isfixed; - attach(c); - attachstack(c); - XMoveResizeWindow(dpy, c->win, c->x, c->y, c->w, c->h); /* some windows require this */ - ban(c); - XMapWindow(dpy, c->win); - setclientstate(c, NormalState); - arrange(); -} - -void -resize(Client *c, int x, int y, int w, int h, Bool sizehints) { - double dx, dy, max, min, ratio; - XWindowChanges wc; - - if(sizehints) { - if(c->minay > 0 && c->maxay > 0 && (h - c->baseh) > 0 && (w - c->basew) > 0) { - dx = (double)(w - c->basew); - dy = (double)(h - c->baseh); - min = (double)(c->minax) / (double)(c->minay); - max = (double)(c->maxax) / (double)(c->maxay); - ratio = dx / dy; - if(max > 0 && min > 0 && ratio > 0) { - if(ratio < min) { - dy = (dx * min + dy) / (min * min + 1); - dx = dy * min; - w = (int)dx + c->basew; - h = (int)dy + c->baseh; - } - else if(ratio > max) { - dy = (dx * min + dy) / (max * max + 1); - dx = dy * min; - w = (int)dx + c->basew; - h = (int)dy + c->baseh; - } - } - } - if(c->minw && w < c->minw) - w = c->minw; - if(c->minh && h < c->minh) - h = c->minh; - if(c->maxw && w > c->maxw) - w = c->maxw; - if(c->maxh && h > c->maxh) - h = c->maxh; - if(c->incw) - w -= (w - c->basew) % c->incw; - if(c->inch) - h -= (h - c->baseh) % c->inch; - } - if(w <= 0 || h <= 0) - return; - /* offscreen appearance fixes */ - if(x > sw) - x = sw - w - 2 * c->border; - if(y > sh) - y = sh - h - 2 * c->border; - if(x + w + 2 * c->border < sx) - x = sx; - if(y + h + 2 * c->border < sy) - y = sy; - if(c->x != x || c->y != y || c->w != w || c->h != h) { - c->x = wc.x = x; - c->y = wc.y = y; - c->w = wc.width = w; - c->h = wc.height = h; - wc.border_width = c->border; - XConfigureWindow(dpy, c->win, CWX | CWY | CWWidth | CWHeight | CWBorderWidth, &wc); - configure(c); - XSync(dpy, False); - } -} - -void -unban(Client *c) { - if(!c->isbanned) - return; - XMoveWindow(dpy, c->win, c->x, c->y); - c->isbanned = False; -} - -void -unmanage(Client *c) { - XWindowChanges wc; - - wc.border_width = c->oldborder; - /* The server grab construct avoids race conditions. */ - XGrabServer(dpy); - XSetErrorHandler(xerrordummy); - XConfigureWindow(dpy, c->win, CWBorderWidth, &wc); /* restore border */ - detach(c); - detachstack(c); - if(sel == c) - focus(NULL); - XUngrabButton(dpy, AnyButton, AnyModifier, c->win); - setclientstate(c, WithdrawnState); - free(c->tags); - free(c); - XSync(dpy, False); - XSetErrorHandler(xerror); - XUngrabServer(dpy); - arrange(); -} - -void -updatesizehints(Client *c) { - long msize; - XSizeHints size; - - if(!XGetWMNormalHints(dpy, c->win, &size, &msize) || !size.flags) - size.flags = PSize; - c->flags = size.flags; - if(c->flags & PBaseSize) { - c->basew = size.base_width; - c->baseh = size.base_height; - } - else if(c->flags & PMinSize) { - c->basew = size.min_width; - c->baseh = size.min_height; - } - else - c->basew = c->baseh = 0; - if(c->flags & PResizeInc) { - c->incw = size.width_inc; - c->inch = size.height_inc; - } - else - c->incw = c->inch = 0; - if(c->flags & PMaxSize) { - c->maxw = size.max_width; - c->maxh = size.max_height; - } - else - c->maxw = c->maxh = 0; - if(c->flags & PMinSize) { - c->minw = size.min_width; - c->minh = size.min_height; - } - else if(c->flags & PBaseSize) { - c->minw = size.base_width; - c->minh = size.base_height; - } - else - c->minw = c->minh = 0; - if(c->flags & PAspect) { - c->minax = size.min_aspect.x; - c->maxax = size.max_aspect.x; - c->minay = size.min_aspect.y; - c->maxay = size.max_aspect.y; - } - else - c->minax = c->maxax = c->minay = c->maxay = 0; - c->isfixed = (c->maxw && c->minw && c->maxh && c->minh - && c->maxw == c->minw && c->maxh == c->minh); -} - -void -updatetitle(Client *c) { - if(!gettextprop(c->win, netatom[NetWMName], c->name, sizeof c->name)) - gettextprop(c->win, wmatom[WMName], c->name, sizeof c->name); -} diff --git a/config.h b/config.h index f946407..9dec069 100644 --- a/config.h +++ b/config.h @@ -12,24 +12,20 @@ #define SELFGCOLOR "#fff" /* tagging */ -#define TAGS \ -const char *tags[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9", NULL }; -#define RULES \ -static Rule rules[] = { \ - /* class:instance:title regex tags regex isfloating */ \ - { "Firefox", "3", False }, \ - { "Gimp", NULL, True }, \ - { "MPlayer", NULL, True }, \ - { "Acroread", NULL, True }, \ +static const char *tags[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9", NULL }; +static Rule rules[] = { + /* class:instance:title regex tags regex isfloating */ + { "Firefox", "3", False }, + { "Gimp", NULL, True }, + { "MPlayer", NULL, True }, + { "Acroread", NULL, True }, }; /* layout(s) */ -#include "tile.h" -#define LAYOUTS \ -static Layout layouts[] = { \ - /* symbol function */ \ - { "[]=", tile }, /* first entry is default */ \ - { "><>", floating }, \ +static Layout layouts[] = { + /* symbol function */ + { "[]=", tile }, /* first entry is default */ + { "><>", floating }, }; #define RESIZEHINTS True /* False - respect size hints in tiled resizals */ #define MWFACT 0.6 /* master width factor [0.1 .. 0.9] */ diff --git a/config.mk b/config.mk index 4b2ff6c..3a3ed7a 100644 --- a/config.mk +++ b/config.mk @@ -3,9 +3,6 @@ VERSION = 4.5 # Customize below to fit your system -# additional layouts beside floating -SRC = tile.c - # paths PREFIX = /usr/local MANPREFIX = ${PREFIX}/share/man diff --git a/dwm.c b/dwm.c new file mode 100644 index 0000000..d497f4c --- /dev/null +++ b/dwm.c @@ -0,0 +1,1847 @@ +/* See LICENSE file for copyright and license details. */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* macros */ +#define BUTTONMASK (ButtonPressMask | ButtonReleaseMask) +#define CLEANMASK(mask) (mask & ~(numlockmask | LockMask)) +#define MOUSEMASK (BUTTONMASK | PointerMotionMask) + +/* enums */ +enum { BarTop, BarBot, BarOff }; /* bar position */ +enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */ +enum { ColBorder, ColFG, ColBG, ColLast }; /* color */ +enum { NetSupported, NetWMName, NetLast }; /* EWMH atoms */ +enum { WMProtocols, WMDelete, WMName, WMState, WMLast };/* default atoms */ + +/* typedefs */ +typedef struct Client Client; +struct Client { + char name[256]; + int x, y, w, h; + int rx, ry, rw, rh; /* revert geometry */ + int basew, baseh, incw, inch, maxw, maxh, minw, minh; + int minax, maxax, minay, maxay; + long flags; + unsigned int border, oldborder; + Bool isbanned, isfixed, ismax, isfloating; + Bool *tags; + Client *next; + Client *prev; + Client *snext; + Window win; +}; + +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 long mod; + KeySym keysym; + void (*func)(const char *arg); + const char *arg; +} Key; + +typedef struct { + const char *symbol; + void (*arrange)(void); +} Layout; + +typedef struct { + const char *prop; + const char *tags; + Bool isfloating; +} Rule; + +typedef struct { + regex_t *propregex; + regex_t *tagregex; +} Regs; + +/* functions */ +static void eprint(const char *errstr, ...); +static void *emallocz(unsigned int size); +static void spawn(const char *arg); +static void drawsquare(Bool filled, Bool empty, unsigned long col[ColLast]); +static unsigned long initcolor(const char *colstr); +static void initfont(const char *fontstr); +static Bool isoccupied(unsigned int t); +static unsigned int textnw(const char *text, unsigned int len); +static void drawtext(const char *text, unsigned long col[ColLast]); +static void drawbar(void); +static void initstyle(void); +static void initbar(void); +static unsigned int textw(const char *text); +static void togglebar(const char *arg); +static void updatebarpos(void); +static void attachstack(Client *c); +static void detachstack(Client *c); +static void grabbuttons(Client *c, Bool focused); +static Bool isprotodel(Client *c); +static void setclientstate(Client *c, long state); +static int xerrordummy(Display *dsply, XErrorEvent *ee); +static void ban(Client *c); +static void configure(Client *c); +static void killclient(const char *arg); +static void manage(Window w, XWindowAttributes *wa); +static void resize(Client *c, int x, int y, int w, int h, Bool sizehints); +static void unban(Client *c); +static void unmanage(Client *c); +static void updatesizehints(Client *c); +static void updatetitle(Client *c); +static Client *getclient(Window w); +static void movemouse(Client *c); +static void resizemouse(Client *c); +static void buttonpress(XEvent *e); +static void configurerequest(XEvent *e); +static void configurenotify(XEvent *e); +static void destroynotify(XEvent *e); +static void enternotify(XEvent *e); +static void expose(XEvent *e); +static void keypress(XEvent *e); +static void leavenotify(XEvent *e); +static void mappingnotify(XEvent *e); +static void maprequest(XEvent *e); +static void propertynotify(XEvent *e); +static void unmapnotify(XEvent *e); +static void grabkeys(void); +static unsigned int idxoftag(const char *tag); +static void floating(void); /* default floating layout */ +static void applyrules(Client *c); +static void compileregs(void); +static void focusnext(const char *arg); +static void focusprev(const char *arg); +static void initlayouts(void); +static Bool isfloating(void); +static Bool isvisible(Client *c); +static void restack(void); +static void setlayout(const char *arg); +static void tag(const char *arg); +static void togglefloating(const char *arg); +static void togglemax(const char *arg); +static void toggletag(const char *arg); +static void toggleview(const char *arg); +static void view(const char *arg); +static void cleanup(void); +static long getstate(Window w); +static void scan(void); +static void setup(void); +static int xerrorstart(Display *dsply, XErrorEvent *ee); +static Bool gettextprop(Window w, Atom atom, char *text, unsigned int size); +static void quit(const char *arg); +static int xerror(Display *dpy, XErrorEvent *ee); +static void arrange(void); +static void attach(Client *c); +static void detach(Client *c); +static void focus(Client *c); +static Bool isarrange(void (*func)()); +static Client *nexttiled(Client *c); +static void setmwfact(const char *arg); +static void tile(void); +static void zoom(const char *arg); + +#include "config.h" + +/* variables */ +static char stext[256]; +static double mwfact = MWFACT; +static int screen, sx, sy, sw, sh, wax, way, waw, wah; +static int (*xerrorxlib)(Display *, XErrorEvent *); +static unsigned int bh; +static unsigned int blw = 0; +static unsigned int bpos = BARPOS; +static unsigned int ltidx = 0; /* default */ +static unsigned int nlayouts = 0; +static unsigned int nrules = 0; +static unsigned int ntags; +static unsigned int numlockmask = 0; +static void (*handler[LASTEvent]) (XEvent *) = { + [ButtonPress] = buttonpress, + [ConfigureRequest] = configurerequest, + [ConfigureNotify] = configurenotify, + [DestroyNotify] = destroynotify, + [EnterNotify] = enternotify, + [LeaveNotify] = leavenotify, + [Expose] = expose, + [KeyPress] = keypress, + [MappingNotify] = mappingnotify, + [MapRequest] = maprequest, + [PropertyNotify] = propertynotify, + [UnmapNotify] = unmapnotify +}; +static Atom wmatom[WMLast], netatom[NetLast]; +static Bool otherwm, readin; +static Bool running = True; +static Bool *seltags; +static Bool selscreen = True; +static Client *clients = NULL; +static Client *sel = NULL; +static Client *stack = NULL; +static Cursor cursor[CurLast]; +static Display *dpy; +static DC dc = {0}; +static Window barwin, root; +static Regs *regs = NULL; + +static void +eprint(const char *errstr, ...) { + va_list ap; + + va_start(ap, errstr); + vfprintf(stderr, errstr, ap); + va_end(ap); + exit(EXIT_FAILURE); +} + +static void * +emallocz(unsigned int size) { + void *res = calloc(1, size); + + if(!res) + eprint("fatal: could not malloc() %u bytes\n", size); + return res; +} + +static void +spawn(const char *arg) { + static char *shell = NULL; + + if(!shell && !(shell = getenv("SHELL"))) + shell = "/bin/sh"; + if(!arg) + return; + /* The double-fork construct avoids zombie processes and keeps the code + * clean from stupid signal handlers. */ + if(fork() == 0) { + if(fork() == 0) { + if(dpy) + close(ConnectionNumber(dpy)); + setsid(); + execl(shell, shell, "-c", arg, (char *)NULL); + fprintf(stderr, "dwm: execl '%s -c %s'", shell, arg); + perror(" failed"); + } + exit(0); + } + wait(0); +} + +static void +drawsquare(Bool filled, Bool empty, unsigned long col[ColLast]) { + int x; + XGCValues gcv; + XRectangle r = { dc.x, dc.y, dc.w, dc.h }; + + gcv.foreground = col[ColFG]; + XChangeGC(dpy, dc.gc, GCForeground, &gcv); + x = (dc.font.ascent + dc.font.descent + 2) / 4; + r.x = dc.x + 1; + r.y = dc.y + 1; + if(filled) { + r.width = r.height = x + 1; + XFillRectangles(dpy, dc.drawable, dc.gc, &r, 1); + } + else if(empty) { + r.width = r.height = x; + XDrawRectangles(dpy, dc.drawable, dc.gc, &r, 1); + } +} + +static unsigned long +initcolor(const char *colstr) { + Colormap cmap = DefaultColormap(dpy, screen); + XColor color; + + if(!XAllocNamedColor(dpy, cmap, colstr, &color, &color)) + eprint("error, cannot allocate color '%s'\n", colstr); + return color.pixel; +} + +static void +initfont(const char *fontstr) { + char *def, **missing; + int i, n; + + missing = NULL; + if(dc.font.set) + XFreeFontSet(dpy, dc.font.set); + 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) { + XFontSetExtents *font_extents; + XFontStruct **xfonts; + char **font_names; + dc.font.ascent = dc.font.descent = 0; + font_extents = XExtentsOfFontSet(dc.font.set); + n = XFontsOfFontSet(dc.font.set, &xfonts, &font_names); + for(i = 0, dc.font.ascent = 0, dc.font.descent = 0; i < n; i++) { + if(dc.font.ascent < (*xfonts)->ascent) + dc.font.ascent = (*xfonts)->ascent; + if(dc.font.descent < (*xfonts)->descent) + dc.font.descent = (*xfonts)->descent; + xfonts++; + } + } + else { + if(dc.font.xfont) + XFreeFont(dpy, dc.font.xfont); + dc.font.xfont = NULL; + if(!(dc.font.xfont = XLoadQueryFont(dpy, fontstr)) + || !(dc.font.xfont = XLoadQueryFont(dpy, "fixed"))) + eprint("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; +} + +static Bool +isoccupied(unsigned int t) { + Client *c; + + for(c = clients; c; c = c->next) + if(c->tags[t]) + return True; + return False; +} + +static unsigned 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); +} + +static void +drawtext(const char *text, unsigned long col[ColLast]) { + int x, y, w, h; + static char buf[256]; + unsigned int len, olen; + XRectangle r = { dc.x, dc.y, dc.w, dc.h }; + + XSetForeground(dpy, dc.gc, col[ColBG]); + XFillRectangles(dpy, dc.drawable, dc.gc, &r, 1); + if(!text) + return; + w = 0; + olen = len = strlen(text); + if(len >= sizeof buf) + len = sizeof buf - 1; + memcpy(buf, text, len); + buf[len] = 0; + 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 */ + while(len && (w = textnw(buf, len)) > dc.w - h) + buf[--len] = 0; + if(len < olen) { + if(len > 1) + buf[len - 1] = '.'; + if(len > 2) + buf[len - 2] = '.'; + if(len > 3) + buf[len - 3] = '.'; + } + if(w > dc.w) + return; /* too long */ + XSetForeground(dpy, dc.gc, col[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); +} + +static void +drawbar(void) { + int i, x; + + dc.x = dc.y = 0; + for(i = 0; i < ntags; i++) { + dc.w = textw(tags[i]); + if(seltags[i]) { + drawtext(tags[i], dc.sel); + drawsquare(sel && sel->tags[i], isoccupied(i), dc.sel); + } + else { + drawtext(tags[i], dc.norm); + drawsquare(sel && sel->tags[i], isoccupied(i), dc.norm); + } + dc.x += dc.w; + } + dc.w = blw; + drawtext(layouts[ltidx].symbol, dc.norm); + x = dc.x + dc.w; + dc.w = textw(stext); + dc.x = sw - dc.w; + if(dc.x < x) { + dc.x = x; + dc.w = sw - x; + } + drawtext(stext, dc.norm); + if((dc.w = dc.x - x) > bh) { + dc.x = x; + if(sel) { + drawtext(sel->name, dc.sel); + drawsquare(sel->ismax, sel->isfloating, dc.sel); + } + else + drawtext(NULL, dc.norm); + } + XCopyArea(dpy, dc.drawable, barwin, dc.gc, 0, 0, sw, bh, 0, 0); + XSync(dpy, False); +} + +static void +initstyle(void) { + dc.norm[ColBorder] = initcolor(NORMBORDERCOLOR); + dc.norm[ColBG] = initcolor(NORMBGCOLOR); + dc.norm[ColFG] = initcolor(NORMFGCOLOR); + dc.sel[ColBorder] = initcolor(SELBORDERCOLOR); + dc.sel[ColBG] = initcolor(SELBGCOLOR); + dc.sel[ColFG] = initcolor(SELFGCOLOR); + initfont(FONT); + dc.h = bh = dc.font.height + 2; +} + +static void +initbar(void) { + XSetWindowAttributes wa; + + wa.override_redirect = 1; + wa.background_pixmap = ParentRelative; + wa.event_mask = ButtonPressMask | ExposureMask; + barwin = XCreateWindow(dpy, root, sx, sy, sw, bh, 0, + DefaultDepth(dpy, screen), CopyFromParent, DefaultVisual(dpy, screen), + CWOverrideRedirect | CWBackPixmap | CWEventMask, &wa); + XDefineCursor(dpy, barwin, cursor[CurNormal]); + updatebarpos(); + XMapRaised(dpy, barwin); + strcpy(stext, "dwm-"VERSION); + dc.drawable = XCreatePixmap(dpy, root, sw, bh, DefaultDepth(dpy, screen)); + dc.gc = XCreateGC(dpy, root, 0, 0); + XSetLineAttributes(dpy, dc.gc, 1, LineSolid, CapButt, JoinMiter); + if(!dc.font.set) + XSetFont(dpy, dc.gc, dc.font.xfont->fid); +} + +static unsigned int +textw(const char *text) { + return textnw(text, strlen(text)) + dc.font.height; +} + +static void +togglebar(const char *arg) { + if(bpos == BarOff) + bpos = (BARPOS == BarOff) ? BarTop : BARPOS; + else + bpos = BarOff; + updatebarpos(); + arrange(); +} + +static void +updatebarpos(void) { + XEvent ev; + + wax = sx; + way = sy; + wah = sh; + waw = sw; + switch(bpos) { + default: + wah -= bh; + way += bh; + XMoveWindow(dpy, barwin, sx, sy); + break; + case BarBot: + wah -= bh; + XMoveWindow(dpy, barwin, sx, sy + wah); + break; + case BarOff: + XMoveWindow(dpy, barwin, sx, sy - bh); + break; + } + XSync(dpy, False); + while(XCheckMaskEvent(dpy, EnterWindowMask, &ev)); +} + +static void +attachstack(Client *c) { + c->snext = stack; + stack = c; +} + +static void +detachstack(Client *c) { + Client **tc; + + for(tc=&stack; *tc && *tc != c; tc=&(*tc)->snext); + *tc = c->snext; +} + +static void +grabbuttons(Client *c, Bool focused) { + XUngrabButton(dpy, AnyButton, AnyModifier, c->win); + + if(focused) { + XGrabButton(dpy, Button1, MODKEY, c->win, False, BUTTONMASK, + GrabModeAsync, GrabModeSync, None, None); + XGrabButton(dpy, Button1, MODKEY | LockMask, c->win, False, BUTTONMASK, + GrabModeAsync, GrabModeSync, None, None); + XGrabButton(dpy, Button1, MODKEY | numlockmask, c->win, False, BUTTONMASK, + GrabModeAsync, GrabModeSync, None, None); + XGrabButton(dpy, Button1, MODKEY | numlockmask | LockMask, c->win, False, BUTTONMASK, + GrabModeAsync, GrabModeSync, None, None); + + XGrabButton(dpy, Button2, MODKEY, c->win, False, BUTTONMASK, + GrabModeAsync, GrabModeSync, None, None); + XGrabButton(dpy, Button2, MODKEY | LockMask, c->win, False, BUTTONMASK, + GrabModeAsync, GrabModeSync, None, None); + XGrabButton(dpy, Button2, MODKEY | numlockmask, c->win, False, BUTTONMASK, + GrabModeAsync, GrabModeSync, None, None); + XGrabButton(dpy, Button2, MODKEY | numlockmask | LockMask, c->win, False, BUTTONMASK, + GrabModeAsync, GrabModeSync, None, None); + + XGrabButton(dpy, Button3, MODKEY, c->win, False, BUTTONMASK, + GrabModeAsync, GrabModeSync, None, None); + XGrabButton(dpy, Button3, MODKEY | LockMask, c->win, False, BUTTONMASK, + GrabModeAsync, GrabModeSync, None, None); + XGrabButton(dpy, Button3, MODKEY | numlockmask, c->win, False, BUTTONMASK, + GrabModeAsync, GrabModeSync, None, None); + XGrabButton(dpy, Button3, MODKEY | numlockmask | LockMask, c->win, False, BUTTONMASK, + GrabModeAsync, GrabModeSync, None, None); + } + else + XGrabButton(dpy, AnyButton, AnyModifier, c->win, False, BUTTONMASK, + GrabModeAsync, GrabModeSync, None, None); +} + +static Bool +isprotodel(Client *c) { + int i, n; + Atom *protocols; + Bool ret = False; + + if(XGetWMProtocols(dpy, c->win, &protocols, &n)) { + for(i = 0; !ret && i < n; i++) + if(protocols[i] == wmatom[WMDelete]) + ret = True; + XFree(protocols); + } + return ret; +} + +static void +setclientstate(Client *c, long state) { + long data[] = {state, None}; + + XChangeProperty(dpy, c->win, wmatom[WMState], wmatom[WMState], 32, + PropModeReplace, (unsigned char *)data, 2); +} + +static int +xerrordummy(Display *dsply, XErrorEvent *ee) { + return 0; +} + +static void +ban(Client *c) { + if(c->isbanned) + return; + XMoveWindow(dpy, c->win, c->x + 2 * sw, c->y); + c->isbanned = True; +} + +static void +configure(Client *c) { + XConfigureEvent ce; + + ce.type = ConfigureNotify; + ce.display = dpy; + ce.event = c->win; + ce.window = c->win; + ce.x = c->x; + ce.y = c->y; + ce.width = c->w; + ce.height = c->h; + ce.border_width = c->border; + ce.above = None; + ce.override_redirect = False; + XSendEvent(dpy, c->win, False, StructureNotifyMask, (XEvent *)&ce); +} + +static void +killclient(const char *arg) { + XEvent ev; + + if(!sel) + return; + if(isprotodel(sel)) { + ev.type = ClientMessage; + ev.xclient.window = sel->win; + ev.xclient.message_type = wmatom[WMProtocols]; + ev.xclient.format = 32; + ev.xclient.data.l[0] = wmatom[WMDelete]; + ev.xclient.data.l[1] = CurrentTime; + XSendEvent(dpy, sel->win, False, NoEventMask, &ev); + } + else + XKillClient(dpy, sel->win); +} + +static void +manage(Window w, XWindowAttributes *wa) { + unsigned int i; + Client *c, *t = NULL; + Window trans; + Status rettrans; + XWindowChanges wc; + + c = emallocz(sizeof(Client)); + c->tags = emallocz(ntags * sizeof(Bool)); + c->win = w; + c->x = wa->x; + c->y = wa->y; + c->w = wa->width; + c->h = wa->height; + c->oldborder = wa->border_width; + if(c->w == sw && c->h == sh) { + c->x = sx; + c->y = sy; + c->border = wa->border_width; + } + else { + if(c->x + c->w + 2 * c->border > wax + waw) + c->x = wax + waw - c->w - 2 * c->border; + if(c->y + c->h + 2 * c->border > way + wah) + c->y = way + wah - c->h - 2 * c->border; + if(c->x < wax) + c->x = wax; + if(c->y < way) + c->y = way; + c->border = BORDERPX; + } + wc.border_width = c->border; + XConfigureWindow(dpy, w, CWBorderWidth, &wc); + XSetWindowBorder(dpy, w, dc.norm[ColBorder]); + configure(c); /* propagates border_width, if size doesn't change */ + updatesizehints(c); + XSelectInput(dpy, w, + StructureNotifyMask | PropertyChangeMask | EnterWindowMask); + grabbuttons(c, False); + updatetitle(c); + if((rettrans = XGetTransientForHint(dpy, w, &trans) == Success)) + for(t = clients; t && t->win != trans; t = t->next); + if(t) + for(i = 0; i < ntags; i++) + c->tags[i] = t->tags[i]; + applyrules(c); + if(!c->isfloating) + c->isfloating = (rettrans == Success) || c->isfixed; + attach(c); + attachstack(c); + XMoveResizeWindow(dpy, c->win, c->x, c->y, c->w, c->h); /* some windows require this */ + ban(c); + XMapWindow(dpy, c->win); + setclientstate(c, NormalState); + arrange(); +} + +static void +resize(Client *c, int x, int y, int w, int h, Bool sizehints) { + double dx, dy, max, min, ratio; + XWindowChanges wc; + + if(sizehints) { + if(c->minay > 0 && c->maxay > 0 && (h - c->baseh) > 0 && (w - c->basew) > 0) { + dx = (double)(w - c->basew); + dy = (double)(h - c->baseh); + min = (double)(c->minax) / (double)(c->minay); + max = (double)(c->maxax) / (double)(c->maxay); + ratio = dx / dy; + if(max > 0 && min > 0 && ratio > 0) { + if(ratio < min) { + dy = (dx * min + dy) / (min * min + 1); + dx = dy * min; + w = (int)dx + c->basew; + h = (int)dy + c->baseh; + } + else if(ratio > max) { + dy = (dx * min + dy) / (max * max + 1); + dx = dy * min; + w = (int)dx + c->basew; + h = (int)dy + c->baseh; + } + } + } + if(c->minw && w < c->minw) + w = c->minw; + if(c->minh && h < c->minh) + h = c->minh; + if(c->maxw && w > c->maxw) + w = c->maxw; + if(c->maxh && h > c->maxh) + h = c->maxh; + if(c->incw) + w -= (w - c->basew) % c->incw; + if(c->inch) + h -= (h - c->baseh) % c->inch; + } + if(w <= 0 || h <= 0) + return; + /* offscreen appearance fixes */ + if(x > sw) + x = sw - w - 2 * c->border; + if(y > sh) + y = sh - h - 2 * c->border; + if(x + w + 2 * c->border < sx) + x = sx; + if(y + h + 2 * c->border < sy) + y = sy; + if(c->x != x || c->y != y || c->w != w || c->h != h) { + c->x = wc.x = x; + c->y = wc.y = y; + c->w = wc.width = w; + c->h = wc.height = h; + wc.border_width = c->border; + XConfigureWindow(dpy, c->win, CWX | CWY | CWWidth | CWHeight | CWBorderWidth, &wc); + configure(c); + XSync(dpy, False); + } +} + +static void +unban(Client *c) { + if(!c->isbanned) + return; + XMoveWindow(dpy, c->win, c->x, c->y); + c->isbanned = False; +} + +static void +unmanage(Client *c) { + XWindowChanges wc; + + wc.border_width = c->oldborder; + /* The server grab construct avoids race conditions. */ + XGrabServer(dpy); + XSetErrorHandler(xerrordummy); + XConfigureWindow(dpy, c->win, CWBorderWidth, &wc); /* restore border */ + detach(c); + detachstack(c); + if(sel == c) + focus(NULL); + XUngrabButton(dpy, AnyButton, AnyModifier, c->win); + setclientstate(c, WithdrawnState); + free(c->tags); + free(c); + XSync(dpy, False); + XSetErrorHandler(xerror); + XUngrabServer(dpy); + arrange(); +} + +static void +updatesizehints(Client *c) { + long msize; + XSizeHints size; + + if(!XGetWMNormalHints(dpy, c->win, &size, &msize) || !size.flags) + size.flags = PSize; + c->flags = size.flags; + if(c->flags & PBaseSize) { + c->basew = size.base_width; + c->baseh = size.base_height; + } + else if(c->flags & PMinSize) { + c->basew = size.min_width; + c->baseh = size.min_height; + } + else + c->basew = c->baseh = 0; + if(c->flags & PResizeInc) { + c->incw = size.width_inc; + c->inch = size.height_inc; + } + else + c->incw = c->inch = 0; + if(c->flags & PMaxSize) { + c->maxw = size.max_width; + c->maxh = size.max_height; + } + else + c->maxw = c->maxh = 0; + if(c->flags & PMinSize) { + c->minw = size.min_width; + c->minh = size.min_height; + } + else if(c->flags & PBaseSize) { + c->minw = size.base_width; + c->minh = size.base_height; + } + else + c->minw = c->minh = 0; + if(c->flags & PAspect) { + c->minax = size.min_aspect.x; + c->maxax = size.max_aspect.x; + c->minay = size.min_aspect.y; + c->maxay = size.max_aspect.y; + } + else + c->minax = c->maxax = c->minay = c->maxay = 0; + c->isfixed = (c->maxw && c->minw && c->maxh && c->minh + && c->maxw == c->minw && c->maxh == c->minh); +} + +static void +updatetitle(Client *c) { + if(!gettextprop(c->win, netatom[NetWMName], c->name, sizeof c->name)) + gettextprop(c->win, wmatom[WMName], c->name, sizeof c->name); +} + +static Client * +getclient(Window w) { + Client *c; + + for(c = clients; c && c->win != w; c = c->next); + return c; +} + +static void +movemouse(Client *c) { + int x1, y1, ocx, ocy, di, nx, ny; + unsigned int dui; + Window dummy; + XEvent ev; + + ocx = nx = c->x; + ocy = ny = c->y; + if(XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync, + None, cursor[CurMove], CurrentTime) != GrabSuccess) + return; + c->ismax = False; + XQueryPointer(dpy, root, &dummy, &dummy, &x1, &y1, &di, &di, &dui); + for(;;) { + XMaskEvent(dpy, MOUSEMASK | ExposureMask | SubstructureRedirectMask, &ev); + switch (ev.type) { + case ButtonRelease: + XUngrabPointer(dpy, CurrentTime); + return; + case ConfigureRequest: + case Expose: + case MapRequest: + handler[ev.type](&ev); + break; + case MotionNotify: + XSync(dpy, False); + nx = ocx + (ev.xmotion.x - x1); + ny = ocy + (ev.xmotion.y - y1); + if(abs(wax + nx) < SNAP) + nx = wax; + else if(abs((wax + waw) - (nx + c->w + 2 * c->border)) < SNAP) + nx = wax + waw - c->w - 2 * c->border; + if(abs(way - ny) < SNAP) + ny = way; + else if(abs((way + wah) - (ny + c->h + 2 * c->border)) < SNAP) + ny = way + wah - c->h - 2 * c->border; + resize(c, nx, ny, c->w, c->h, False); + break; + } + } +} + +static void +resizemouse(Client *c) { + int ocx, ocy; + int nw, nh; + XEvent ev; + + ocx = c->x; + ocy = c->y; + if(XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync, + None, cursor[CurResize], CurrentTime) != GrabSuccess) + return; + c->ismax = False; + XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w + c->border - 1, c->h + c->border - 1); + for(;;) { + XMaskEvent(dpy, MOUSEMASK | ExposureMask | SubstructureRedirectMask , &ev); + switch(ev.type) { + case ButtonRelease: + XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, + c->w + c->border - 1, c->h + c->border - 1); + XUngrabPointer(dpy, CurrentTime); + while(XCheckMaskEvent(dpy, EnterWindowMask, &ev)); + return; + case ConfigureRequest: + case Expose: + case MapRequest: + handler[ev.type](&ev); + break; + case MotionNotify: + XSync(dpy, False); + if((nw = ev.xmotion.x - ocx - 2 * c->border + 1) <= 0) + nw = 1; + if((nh = ev.xmotion.y - ocy - 2 * c->border + 1) <= 0) + nh = 1; + resize(c, c->x, c->y, nw, nh, True); + break; + } + } +} + +static void +buttonpress(XEvent *e) { + unsigned int i, x; + Client *c; + XButtonPressedEvent *ev = &e->xbutton; + + if(barwin == ev->window) { + x = 0; + for(i = 0; i < ntags; i++) { + x += textw(tags[i]); + if(ev->x < x) { + if(ev->button == Button1) { + if(ev->state & MODKEY) + tag(tags[i]); + else + view(tags[i]); + } + else if(ev->button == Button3) { + if(ev->state & MODKEY) + toggletag(tags[i]); + else + toggleview(tags[i]); + } + return; + } + } + if((ev->x < x + blw) && ev->button == Button1) + setlayout(NULL); + } + else if((c = getclient(ev->window))) { + focus(c); + if(CLEANMASK(ev->state) != MODKEY) + return; + if(ev->button == Button1 && (isfloating() || c->isfloating)) { + restack(); + movemouse(c); + } + else if(ev->button == Button2) + zoom(NULL); + else if(ev->button == Button3 + && (isfloating() || c->isfloating) && !c->isfixed) + { + restack(); + resizemouse(c); + } + } +} + +static void +configurerequest(XEvent *e) { + Client *c; + XConfigureRequestEvent *ev = &e->xconfigurerequest; + XWindowChanges wc; + + if((c = getclient(ev->window))) { + c->ismax = False; + if(ev->value_mask & CWBorderWidth) + c->border = ev->border_width; + if(c->isfixed || c->isfloating || isfloating()) { + if(ev->value_mask & CWX) + c->x = ev->x; + if(ev->value_mask & CWY) + c->y = ev->y; + if(ev->value_mask & CWWidth) + c->w = ev->width; + if(ev->value_mask & CWHeight) + c->h = ev->height; + if((c->x + c->w) > sw && c->isfloating) + c->x = sw / 2 - c->w / 2; /* center in x direction */ + if((c->y + c->h) > sh && c->isfloating) + c->y = sh / 2 - c->h / 2; /* center in y direction */ + if((ev->value_mask & (CWX | CWY)) + && !(ev->value_mask & (CWWidth | CWHeight))) + configure(c); + if(isvisible(c)) + XMoveResizeWindow(dpy, c->win, c->x, c->y, c->w, c->h); + } + else + configure(c); + } + else { + wc.x = ev->x; + wc.y = ev->y; + wc.width = ev->width; + wc.height = ev->height; + wc.border_width = ev->border_width; + wc.sibling = ev->above; + wc.stack_mode = ev->detail; + XConfigureWindow(dpy, ev->window, ev->value_mask, &wc); + } + XSync(dpy, False); +} + +static void +configurenotify(XEvent *e) { + XConfigureEvent *ev = &e->xconfigure; + + if (ev->window == root && (ev->width != sw || ev->height != sh)) { + sw = ev->width; + sh = ev->height; + XFreePixmap(dpy, dc.drawable); + dc.drawable = XCreatePixmap(dpy, root, sw, bh, DefaultDepth(dpy, screen)); + XResizeWindow(dpy, barwin, sw, bh); + updatebarpos(); + arrange(); + } +} + +static void +destroynotify(XEvent *e) { + Client *c; + XDestroyWindowEvent *ev = &e->xdestroywindow; + + if((c = getclient(ev->window))) + unmanage(c); +} + +static void +enternotify(XEvent *e) { + Client *c; + XCrossingEvent *ev = &e->xcrossing; + + if(ev->mode != NotifyNormal || ev->detail == NotifyInferior) + return; + if((c = getclient(ev->window))) + focus(c); + else if(ev->window == root) { + selscreen = True; + focus(NULL); + } +} + +static void +expose(XEvent *e) { + XExposeEvent *ev = &e->xexpose; + + if(ev->count == 0) { + if(barwin == ev->window) + drawbar(); + } +} + +static void +keypress(XEvent *e) { + KEYS + unsigned int len = sizeof keys / sizeof keys[0]; + unsigned int i; + KeySym keysym; + XKeyEvent *ev = &e->xkey; + + keysym = XKeycodeToKeysym(dpy, (KeyCode)ev->keycode, 0); + for(i = 0; i < len; i++) + if(keysym == keys[i].keysym + && CLEANMASK(keys[i].mod) == CLEANMASK(ev->state)) + { + if(keys[i].func) + keys[i].func(keys[i].arg); + } +} + +static void +leavenotify(XEvent *e) { + XCrossingEvent *ev = &e->xcrossing; + + if((ev->window == root) && !ev->same_screen) { + selscreen = False; + focus(NULL); + } +} + +static void +mappingnotify(XEvent *e) { + XMappingEvent *ev = &e->xmapping; + + XRefreshKeyboardMapping(ev); + if(ev->request == MappingKeyboard) + grabkeys(); +} + +static void +maprequest(XEvent *e) { + static XWindowAttributes wa; + XMapRequestEvent *ev = &e->xmaprequest; + + if(!XGetWindowAttributes(dpy, ev->window, &wa)) + return; + if(wa.override_redirect) + return; + if(!getclient(ev->window)) + manage(ev->window, &wa); +} + +static void +propertynotify(XEvent *e) { + Client *c; + Window trans; + XPropertyEvent *ev = &e->xproperty; + + if(ev->state == PropertyDelete) + return; /* ignore */ + if((c = getclient(ev->window))) { + switch (ev->atom) { + default: break; + case XA_WM_TRANSIENT_FOR: + XGetTransientForHint(dpy, c->win, &trans); + if(!c->isfloating && (c->isfloating = (getclient(trans) != NULL))) + arrange(); + break; + case XA_WM_NORMAL_HINTS: + updatesizehints(c); + break; + } + if(ev->atom == XA_WM_NAME || ev->atom == netatom[NetWMName]) { + updatetitle(c); + if(c == sel) + drawbar(); + } + } +} + +static void +unmapnotify(XEvent *e) { + Client *c; + XUnmapEvent *ev = &e->xunmap; + + if((c = getclient(ev->window))) + unmanage(c); +} + +static void +grabkeys(void) { + KEYS + unsigned int len = sizeof keys / sizeof keys[0]; + unsigned int i; + KeyCode code; + + 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); + } +} + +static unsigned int +idxoftag(const char *tag) { + unsigned int i; + + for(i = 0; i < ntags; i++) + if(tags[i] == tag) + return i; + return 0; +} + +static void +floating(void) { /* default floating layout */ + Client *c; + + for(c = clients; c; c = c->next) + if(isvisible(c)) + resize(c, c->x, c->y, c->w, c->h, True); +} + +static void +applyrules(Client *c) { + static char buf[512]; + unsigned int i, j; + regmatch_t tmp; + Bool matched = False; + XClassHint ch = { 0 }; + + /* rule matching */ + XGetClassHint(dpy, c->win, &ch); + 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++) + 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++) { + if(!regexec(regs[i].tagregex, tags[j], 1, &tmp, 0)) { + matched = True; + c->tags[j] = True; + } + } + } + if(ch.res_class) + XFree(ch.res_class); + if(ch.res_name) + XFree(ch.res_name); + if(!matched) + for(i = 0; i < ntags; i++) + c->tags[i] = seltags[i]; +} + +static void +compileregs(void) { + unsigned int i; + regex_t *reg; + + if(regs) + return; + nrules = sizeof rules / sizeof rules[0]; + regs = emallocz(nrules * sizeof(Regs)); + for(i = 0; i < nrules; i++) { + if(rules[i].prop) { + reg = emallocz(sizeof(regex_t)); + if(regcomp(reg, rules[i].prop, REG_EXTENDED)) + free(reg); + else + regs[i].propregex = reg; + } + if(rules[i].tags) { + reg = emallocz(sizeof(regex_t)); + if(regcomp(reg, rules[i].tags, REG_EXTENDED)) + free(reg); + else + regs[i].tagregex = reg; + } + } +} + +static void +focusnext(const char *arg) { + Client *c; + + if(!sel) + return; + for(c = sel->next; c && !isvisible(c); c = c->next); + if(!c) + for(c = clients; c && !isvisible(c); c = c->next); + if(c) { + focus(c); + restack(); + } +} + +static void +focusprev(const char *arg) { + Client *c; + + if(!sel) + return; + for(c = sel->prev; c && !isvisible(c); c = c->prev); + if(!c) { + for(c = clients; c && c->next; c = c->next); + for(; c && !isvisible(c); c = c->prev); + } + if(c) { + focus(c); + restack(); + } +} + +static void +initlayouts(void) { + unsigned int i, w; + + nlayouts = sizeof layouts / sizeof layouts[0]; + for(blw = i = 0; i < nlayouts; i++) { + w = textw(layouts[i].symbol); + if(w > blw) + blw = w; + } +} + +static Bool +isfloating(void) { + return layouts[ltidx].arrange == floating; +} + +static Bool +isvisible(Client *c) { + unsigned int i; + + for(i = 0; i < ntags; i++) + if(c->tags[i] && seltags[i]) + return True; + return False; +} + +static void +restack(void) { + Client *c; + XEvent ev; + XWindowChanges wc; + + drawbar(); + if(!sel) + return; + if(sel->isfloating || isfloating()) + XRaiseWindow(dpy, sel->win); + if(!isfloating()) { + wc.stack_mode = Below; + wc.sibling = barwin; + if(!sel->isfloating) { + XConfigureWindow(dpy, sel->win, CWSibling | CWStackMode, &wc); + wc.sibling = sel->win; + } + for(c = nexttiled(clients); c; c = nexttiled(c->next)) { + if(c == sel) + continue; + XConfigureWindow(dpy, c->win, CWSibling | CWStackMode, &wc); + wc.sibling = c->win; + } + } + XSync(dpy, False); + while(XCheckMaskEvent(dpy, EnterWindowMask, &ev)); +} + +static void +setlayout(const char *arg) { + unsigned int i; + + if(!arg) { + if(++ltidx == nlayouts) + ltidx = 0;; + } + else { + for(i = 0; i < nlayouts; i++) + if(!strcmp(arg, layouts[i].symbol)) + break; + if(i == nlayouts) + return; + ltidx = i; + } + if(sel) + arrange(); + else + drawbar(); +} + +static void +tag(const char *arg) { + unsigned int i; + + if(!sel) + return; + for(i = 0; i < ntags; i++) + sel->tags[i] = arg == NULL; + i = idxoftag(arg); + if(i >= 0 && i < ntags) + sel->tags[i] = True; + arrange(); +} + +static void +togglefloating(const char *arg) { + if(!sel) + return; + sel->isfloating = !sel->isfloating; + if(sel->isfloating) + resize(sel, sel->x, sel->y, sel->w, sel->h, True); + arrange(); +} + +static void +togglemax(const char *arg) { + XEvent ev; + + if(!sel || (!isfloating() && !sel->isfloating) || sel->isfixed) + return; + if((sel->ismax = !sel->ismax)) { + sel->rx = sel->x; + sel->ry = sel->y; + sel->rw = sel->w; + sel->rh = sel->h; + resize(sel, wax, way, waw - 2 * sel->border, wah - 2 * sel->border, True); + } + else + resize(sel, sel->rx, sel->ry, sel->rw, sel->rh, True); + drawbar(); + while(XCheckMaskEvent(dpy, EnterWindowMask, &ev)); +} + +static void +toggletag(const char *arg) { + unsigned int i, j; + + if(!sel) + return; + i = idxoftag(arg); + sel->tags[i] = !sel->tags[i]; + for(j = 0; j < ntags && !sel->tags[j]; j++); + if(j == ntags) + sel->tags[i] = True; + arrange(); +} + +static void +toggleview(const char *arg) { + unsigned int i, j; + + i = idxoftag(arg); + seltags[i] = !seltags[i]; + for(j = 0; j < ntags && !seltags[j]; j++); + if(j == ntags) + seltags[i] = True; /* cannot toggle last view */ + arrange(); +} + +static void +view(const char *arg) { + unsigned int i; + + for(i = 0; i < ntags; i++) + seltags[i] = arg == NULL; + i = idxoftag(arg); + if(i >= 0 && i < ntags) + seltags[i] = True; + arrange(); +} + +static void +cleanup(void) { + close(STDIN_FILENO); + while(stack) { + unban(stack); + unmanage(stack); + } + 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); + XDestroyWindow(dpy, barwin); + XFreeCursor(dpy, cursor[CurNormal]); + XFreeCursor(dpy, cursor[CurResize]); + XFreeCursor(dpy, cursor[CurMove]); + XSetInputFocus(dpy, PointerRoot, RevertToPointerRoot, CurrentTime); + XSync(dpy, False); + free(seltags); +} + +static long +getstate(Window w) { + int format, status; + long result = -1; + unsigned char *p = NULL; + unsigned long n, extra; + Atom real; + + status = XGetWindowProperty(dpy, w, wmatom[WMState], 0L, 2L, False, wmatom[WMState], + &real, &format, &n, &extra, (unsigned char **)&p); + if(status != Success) + return -1; + if(n != 0) + result = *p; + XFree(p); + return result; +} + +static void +scan(void) { + unsigned int i, num; + Window *wins, d1, d2; + XWindowAttributes wa; + + wins = NULL; + if(XQueryTree(dpy, root, &d1, &d2, &wins, &num)) { + for(i = 0; i < num; i++) { + if(!XGetWindowAttributes(dpy, wins[i], &wa) + || wa.override_redirect || XGetTransientForHint(dpy, wins[i], &d1)) + continue; + if(wa.map_state == IsViewable || getstate(wins[i]) == IconicState) + manage(wins[i], &wa); + } + for(i = 0; i < num; i++) { /* now the transients */ + if(!XGetWindowAttributes(dpy, wins[i], &wa)) + continue; + if(XGetTransientForHint(dpy, wins[i], &d1) + && (wa.map_state == IsViewable || getstate(wins[i]) == IconicState)) + manage(wins[i], &wa); + } + } + if(wins) + XFree(wins); +} + +static void +setup(void) { + int i, j; + unsigned int mask; + Window w; + XModifierKeymap *modmap; + XSetWindowAttributes wa; + + /* init atoms */ + wmatom[WMProtocols] = XInternAtom(dpy, "WM_PROTOCOLS", False); + wmatom[WMDelete] = XInternAtom(dpy, "WM_DELETE_WINDOW", False); + wmatom[WMName] = XInternAtom(dpy, "WM_NAME", False); + wmatom[WMState] = XInternAtom(dpy, "WM_STATE", False); + netatom[NetSupported] = XInternAtom(dpy, "_NET_SUPPORTED", False); + netatom[NetWMName] = XInternAtom(dpy, "_NET_WM_NAME", False); + XChangeProperty(dpy, root, netatom[NetSupported], XA_ATOM, 32, + PropModeReplace, (unsigned char *) netatom, NetLast); + /* init cursors */ + cursor[CurNormal] = XCreateFontCursor(dpy, XC_left_ptr); + cursor[CurResize] = XCreateFontCursor(dpy, XC_sizing); + cursor[CurMove] = XCreateFontCursor(dpy, XC_fleur); + /* init modifier map */ + modmap = XGetModifierMapping(dpy); + for (i = 0; i < 8; i++) + for (j = 0; j < modmap->max_keypermod; j++) { + if(modmap->modifiermap[i * modmap->max_keypermod + j] + == XKeysymToKeycode(dpy, XK_Num_Lock)) + numlockmask = (1 << i); + } + XFreeModifiermap(modmap); + /* select for events */ + wa.event_mask = SubstructureRedirectMask | SubstructureNotifyMask + | EnterWindowMask | LeaveWindowMask | StructureNotifyMask; + wa.cursor = cursor[CurNormal]; + XChangeWindowAttributes(dpy, root, CWEventMask | CWCursor, &wa); + XSelectInput(dpy, root, wa.event_mask); + grabkeys(); + compileregs(); + for(ntags = 0; tags[ntags]; ntags++); + seltags = emallocz(sizeof(Bool) * ntags); + seltags[0] = True; + /* geometry */ + sx = sy = 0; + sw = DisplayWidth(dpy, screen); + sh = DisplayHeight(dpy, screen); + initstyle(); + initlayouts(); + initbar(); + /* multihead support */ + selscreen = XQueryPointer(dpy, root, &w, &w, &i, &i, &i, &i, &mask); +} + +/* + * Startup Error handler to check if another window manager + * is already running. + */ +static int +xerrorstart(Display *dsply, XErrorEvent *ee) { + otherwm = True; + return -1; +} + +static Bool +gettextprop(Window w, Atom atom, char *text, unsigned int size) { + char **list = NULL; + int n; + XTextProperty name; + + if(!text || size == 0) + return False; + text[0] = '\0'; + XGetTextProperty(dpy, w, &name, atom); + if(!name.nitems) + return False; + if(name.encoding == XA_STRING) + strncpy(text, (char *)name.value, size - 1); + else { + if(XmbTextPropertyToTextList(dpy, &name, &list, &n) >= Success + && n > 0 && *list) + { + strncpy(text, *list, size - 1); + XFreeStringList(list); + } + } + text[size - 1] = '\0'; + XFree(name.value); + return True; +} + +static void +quit(const char *arg) { + readin = running = False; +} + +/* There's no way to check accesses to destroyed windows, thus those cases are + * ignored (especially on UnmapNotify's). Other types of errors call Xlibs + * default error handler, which may call exit. + */ +static int +xerror(Display *dpy, XErrorEvent *ee) { + if(ee->error_code == BadWindow + || (ee->request_code == X_SetInputFocus && ee->error_code == BadMatch) + || (ee->request_code == X_PolyText8 && ee->error_code == BadDrawable) + || (ee->request_code == X_PolyFillRectangle && ee->error_code == BadDrawable) + || (ee->request_code == X_PolySegment && ee->error_code == BadDrawable) + || (ee->request_code == X_ConfigureWindow && ee->error_code == BadMatch) + || (ee->request_code == X_GrabKey && ee->error_code == BadAccess) + || (ee->request_code == X_CopyArea && ee->error_code == BadDrawable)) + return 0; + fprintf(stderr, "dwm: fatal error: request code=%d, error code=%d\n", + ee->request_code, ee->error_code); + return xerrorxlib(dpy, ee); /* may call exit */ +} + +static void +arrange(void) { + Client *c; + + for(c = clients; c; c = c->next) + if(isvisible(c)) + unban(c); + else + ban(c); + layouts[ltidx].arrange(); + focus(NULL); + restack(); +} + +static void +attach(Client *c) { + if(clients) + clients->prev = c; + c->next = clients; + clients = c; +} + +static void +detach(Client *c) { + if(c->prev) + c->prev->next = c->next; + if(c->next) + c->next->prev = c->prev; + if(c == clients) + clients = c->next; + c->next = c->prev = NULL; +} + +static void +focus(Client *c) { + if((!c && selscreen) || (c && !isvisible(c))) + for(c = stack; c && !isvisible(c); c = c->snext); + if(sel && sel != c) { + grabbuttons(sel, False); + XSetWindowBorder(dpy, sel->win, dc.norm[ColBorder]); + } + if(c) { + detachstack(c); + attachstack(c); + grabbuttons(c, True); + } + sel = c; + drawbar(); + if(!selscreen) + return; + if(c) { + XSetWindowBorder(dpy, c->win, dc.sel[ColBorder]); + XSetInputFocus(dpy, c->win, RevertToPointerRoot, CurrentTime); + } + else + XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime); +} + +static Bool +isarrange(void (*func)()) +{ + return func == layouts[ltidx].arrange; +} + +static Client * +nexttiled(Client *c) { + for(; c && (c->isfloating || !isvisible(c)); c = c->next); + return c; +} + +static void +setmwfact(const char *arg) { + double delta; + + if(!isarrange(tile)) + return; + /* arg handling, manipulate mwfact */ + if(arg == NULL) + mwfact = MWFACT; + else if(1 == sscanf(arg, "%lf", &delta)) { + if(arg[0] != '+' && arg[0] != '-') + mwfact = delta; + else + mwfact += delta; + if(mwfact < 0.1) + mwfact = 0.1; + else if(mwfact > 0.9) + mwfact = 0.9; + } + arrange(); +} + +static void +tile(void) { + unsigned int i, n, nx, ny, nw, nh, mw, th; + Client *c; + + for(n = 0, c = nexttiled(clients); c; c = nexttiled(c->next)) + n++; + + /* window geoms */ + mw = (n == 1) ? waw : mwfact * waw; + th = (n > 1) ? wah / (n - 1) : 0; + if(n > 1 && th < bh) + th = wah; + + nx = wax; + ny = way; + for(i = 0, c = nexttiled(clients); c; c = nexttiled(c->next), i++) { + c->ismax = False; + if(i == 0) { /* master */ + nw = mw - 2 * c->border; + nh = wah - 2 * c->border; + } + else { /* tile window */ + if(i == 1) { + ny = way; + nx += mw; + } + nw = waw - mw - 2 * c->border; + if(i + 1 == n) /* remainder */ + nh = (way + wah) - ny - 2 * c->border; + else + nh = th - 2 * c->border; + } + resize(c, nx, ny, nw, nh, RESIZEHINTS); + if(n > 1 && th != wah) + ny += nh + 2 * c->border; + } +} + +static void +zoom(const char *arg) { + Client *c; + + if(!sel || !isarrange(tile) || sel->isfloating) + return; + if((c = sel) == nexttiled(clients)) + if(!(c = nexttiled(c->next))) + return; + detach(c); + attach(c); + focus(c); + arrange(); +} + +int +main(int argc, char *argv[]) { + char *p; + int r, xfd; + fd_set rd; + XEvent ev; + + 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"); + else if(argc != 1) + eprint("usage: dwm [-v]\n"); + setlocale(LC_CTYPE, ""); + if(!(dpy = XOpenDisplay(0))) + eprint("dwm: cannot open display\n"); + xfd = ConnectionNumber(dpy); + screen = DefaultScreen(dpy); + root = RootWindow(dpy, screen); + otherwm = False; + XSetErrorHandler(xerrorstart); + /* this causes an error if some other window manager is running */ + XSelectInput(dpy, root, SubstructureRedirectMask); + XSync(dpy, False); + if(otherwm) + eprint("dwm: another window manager is already running\n"); + + XSync(dpy, False); + XSetErrorHandler(NULL); + xerrorxlib = XSetErrorHandler(xerror); + XSync(dpy, False); + setup(); + drawbar(); + scan(); + + /* main event loop, also reads status text from stdin */ + XSync(dpy, False); + readin = True; + while(running) { + FD_ZERO(&rd); + if(readin) + FD_SET(STDIN_FILENO, &rd); + FD_SET(xfd, &rd); + if(select(xfd + 1, &rd, NULL, NULL, NULL) == -1) { + if(errno == EINTR) + continue; + eprint("select failed\n"); + } + if(FD_ISSET(STDIN_FILENO, &rd)) { + switch(r = read(STDIN_FILENO, stext, sizeof stext - 1)) { + case -1: + strncpy(stext, strerror(errno), sizeof stext - 1); + stext[sizeof stext - 1] = '\0'; + readin = False; + break; + case 0: + strncpy(stext, "EOF", 4); + 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); + } + drawbar(); + } + while(XPending(dpy)) { + XNextEvent(dpy, &ev); + if(handler[ev.type]) + (handler[ev.type])(&ev); /* call handler */ + } + } + cleanup(); + XCloseDisplay(dpy); + return 0; +} diff --git a/dwm.h b/dwm.h deleted file mode 100644 index 8c9a141..0000000 --- a/dwm.h +++ /dev/null @@ -1,147 +0,0 @@ -/* See LICENSE file for copyright and license details. - * - * dynamic window manager is designed like any other X client as well. It is - * driven through handling X events. In contrast to other X clients, a window - * manager selects for SubstructureRedirectMask on the root window, to receive - * events about window (dis-)appearance. Only one X connection at a time is - * allowed to select for this event mask. - * - * Calls to fetch an X event from the event queue are blocking. Due reading - * status text from standard input, a select()-driven main loop has been - * implemented which selects for reads on the X connection and STDIN_FILENO to - * handle all data smoothly. The event handlers of dwm are organized in an - * array which is accessed whenever a new event has been fetched. This allows - * event dispatching in O(1) time. - * - * Each child of the root window is called a client, except windows which have - * 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. - * - * Keys and tagging rules are organized as arrays and defined in the config.h - * file. These arrays are kept static in event.o and tag.o respectively, - * because no other part of dwm needs access to them. The current layout is - * represented by the lt pointer. - * - * To understand everything else, start reading main.c:main(). - */ - -#include "config.h" -#include - -/* mask shorthands, used in event.c and client.c */ -#define BUTTONMASK (ButtonPressMask | ButtonReleaseMask) - -enum { BarTop, BarBot, BarOff }; /* bar position */ -enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */ -enum { ColBorder, ColFG, ColBG, ColLast }; /* color */ -enum { NetSupported, NetWMName, NetLast }; /* EWMH atoms */ -enum { WMProtocols, WMDelete, WMName, WMState, WMLast };/* default atoms */ - -typedef struct Client Client; -struct Client { - char name[256]; - int x, y, w, h; - int rx, ry, rw, rh; /* revert geometry */ - int basew, baseh, incw, inch, maxw, maxh, minw, minh; - int minax, maxax, minay, maxay; - long flags; - unsigned int border, oldborder; - Bool isbanned, isfixed, ismax, isfloating; - Bool *tags; - Client *next; - Client *prev; - Client *snext; - Window win; -}; - -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 */ - -extern const char *tags[]; /* all tags */ -extern char stext[256]; /* status text */ -extern int screen, sx, sy, sw, sh; /* screen geometry */ -extern int wax, way, wah, waw; /* windowarea geometry */ -extern unsigned int bh, blw, bpos; /* bar height, bar layout label width, bar position */ -extern unsigned int ntags, numlockmask; /* number of tags, numlock mask */ -extern void (*handler[LASTEvent])(XEvent *); /* event handler */ -extern Atom wmatom[WMLast], netatom[NetLast]; -extern Bool selscreen, *seltags; /* seltags is array of Bool */ -extern Client *clients, *sel, *stack; /* global client list and stack */ -extern Cursor cursor[CurLast]; -extern DC dc; /* global draw context */ -extern Display *dpy; -extern Window root, barwin; - -/* bar.c */ -void drawbar(void); /* draw the bar */ -void initbar(void); /* initializes the bar */ -void initstyle(void); /* initializes colors and font */ -unsigned int textw(const char *text); /* return the width of text in px*/ -void togglebar(const char *arg); /* shows/hides the bar */ -void updatebarpos(void); /* updates the bar position */ - -/* client.c */ -void attach(Client *c); /* attaches c to global client list */ -void ban(Client *c); /* bans c */ -void configure(Client *c); /* send synthetic configure event */ -void detach(Client *c); /* detaches c from global client list */ -void focus(Client *c); /* focus c if visible && !NULL, or focus top visible */ -void killclient(const char *arg); /* kill sel nicely */ -void manage(Window w, XWindowAttributes *wa); /* manage new client */ -void resize(Client *c, int x, int y, - int w, int h, Bool sizehints); /* resize with given coordinates c*/ -void unban(Client *c); /* unbans c */ -void unmanage(Client *c); /* unmanage c */ -void updatesizehints(Client *c); /* update the size hint variables of c */ -void updatetitle(Client *c); /* update the name of c */ - -/* event.c */ -void grabkeys(void); /* grab all keys defined in config.h */ - -/* main.c */ -Bool gettextprop(Window w, Atom atom, - char *text, unsigned int size); /* return text property, UTF-8 compliant */ -void quit(const char *arg); /* quit dwm nicely */ -int xerror(Display *dsply, XErrorEvent *ee); /* dwm's X error handler */ - -/* screen.c */ -void applyrules(Client *c); /* applies rules to c */ -void arrange(void); /* arranges all windows depending on the layout in use */ -void compileregs(void); /* initialize regexps of rules defined in config.h */ -void focusnext(const char *arg); /* focuses next visible client */ -void focusprev(const char *arg); /* focuses prev visible client */ -const char *getsymbol(void); /* returns symbol of enabled layout */ -void initlayouts(void); /* initialize layout array */ -Bool isarrange(void (*func)()); /* returns True if func is the layout function in use */ -Bool isfloating(void); /* returns True if floating layout is enabled */ -Bool isvisible(Client *c); /* returns True if client is visible */ -Client *nexttiled(Client *c); /* returns tiled successor of c */ -void restack(void); /* restores z layers of all clients */ -void setlayout(const char *arg); /* sets layout, NULL means next layout */ -void tag(const char *arg); /* tags sel with arg's index */ -void togglefloating(const char *arg); /* toggles sel between floating/tiled state */ -void togglemax(const char *arg); /* toggles maximization of floating client */ -void toggletag(const char *arg); /* toggles sel tags with arg's index */ -void toggleview(const char *arg); /* toggles the tag with arg's index (in)visible */ -void view(const char *arg); /* views the tag with arg's index */ - -/* util.c */ -void *emallocz(unsigned int size); /* allocates zero-initialized memory, exits on error */ -void eprint(const char *errstr, ...); /* prints errstr and exits with 1 */ -void spawn(const char *arg); /* forks a new subprocess with arg's cmd */ diff --git a/event.c b/event.c deleted file mode 100644 index 16201f3..0000000 --- a/event.c +++ /dev/null @@ -1,375 +0,0 @@ -/* See LICENSE file for copyright and license details. */ -#include "dwm.h" -#include -#include -#include -#include - -/* static */ - -typedef struct { - unsigned long mod; - KeySym keysym; - void (*func)(const char *arg); - const char *arg; -} Key; - -#define CLEANMASK(mask) (mask & ~(numlockmask | LockMask)) -#define MOUSEMASK (BUTTONMASK | PointerMotionMask) - -static Client * -getclient(Window w) { - Client *c; - - for(c = clients; c && c->win != w; c = c->next); - return c; -} - -static void -movemouse(Client *c) { - int x1, y1, ocx, ocy, di, nx, ny; - unsigned int dui; - Window dummy; - XEvent ev; - - ocx = nx = c->x; - ocy = ny = c->y; - if(XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync, - None, cursor[CurMove], CurrentTime) != GrabSuccess) - return; - c->ismax = False; - XQueryPointer(dpy, root, &dummy, &dummy, &x1, &y1, &di, &di, &dui); - for(;;) { - XMaskEvent(dpy, MOUSEMASK | ExposureMask | SubstructureRedirectMask, &ev); - switch (ev.type) { - case ButtonRelease: - XUngrabPointer(dpy, CurrentTime); - return; - case ConfigureRequest: - case Expose: - case MapRequest: - handler[ev.type](&ev); - break; - case MotionNotify: - XSync(dpy, False); - nx = ocx + (ev.xmotion.x - x1); - ny = ocy + (ev.xmotion.y - y1); - if(abs(wax + nx) < SNAP) - nx = wax; - else if(abs((wax + waw) - (nx + c->w + 2 * c->border)) < SNAP) - nx = wax + waw - c->w - 2 * c->border; - if(abs(way - ny) < SNAP) - ny = way; - else if(abs((way + wah) - (ny + c->h + 2 * c->border)) < SNAP) - ny = way + wah - c->h - 2 * c->border; - resize(c, nx, ny, c->w, c->h, False); - break; - } - } -} - -static void -resizemouse(Client *c) { - int ocx, ocy; - int nw, nh; - XEvent ev; - - ocx = c->x; - ocy = c->y; - if(XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync, - None, cursor[CurResize], CurrentTime) != GrabSuccess) - return; - c->ismax = False; - XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w + c->border - 1, c->h + c->border - 1); - for(;;) { - XMaskEvent(dpy, MOUSEMASK | ExposureMask | SubstructureRedirectMask , &ev); - switch(ev.type) { - case ButtonRelease: - XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, - c->w + c->border - 1, c->h + c->border - 1); - XUngrabPointer(dpy, CurrentTime); - while(XCheckMaskEvent(dpy, EnterWindowMask, &ev)); - return; - case ConfigureRequest: - case Expose: - case MapRequest: - handler[ev.type](&ev); - break; - case MotionNotify: - XSync(dpy, False); - if((nw = ev.xmotion.x - ocx - 2 * c->border + 1) <= 0) - nw = 1; - if((nh = ev.xmotion.y - ocy - 2 * c->border + 1) <= 0) - nh = 1; - resize(c, c->x, c->y, nw, nh, True); - break; - } - } -} - -static void -buttonpress(XEvent *e) { - unsigned int i, x; - Client *c; - XButtonPressedEvent *ev = &e->xbutton; - - if(barwin == ev->window) { - x = 0; - for(i = 0; i < ntags; i++) { - x += textw(tags[i]); - if(ev->x < x) { - if(ev->button == Button1) { - if(ev->state & MODKEY) - tag(tags[i]); - else - view(tags[i]); - } - else if(ev->button == Button3) { - if(ev->state & MODKEY) - toggletag(tags[i]); - else - toggleview(tags[i]); - } - return; - } - } - if((ev->x < x + blw) && ev->button == Button1) - setlayout(NULL); - } - else if((c = getclient(ev->window))) { - focus(c); - if(CLEANMASK(ev->state) != MODKEY) - return; - if(ev->button == Button1 && (isfloating() || c->isfloating)) { - restack(); - movemouse(c); - } - else if(ev->button == Button2) - zoom(NULL); - else if(ev->button == Button3 - && (isfloating() || c->isfloating) && !c->isfixed) - { - restack(); - resizemouse(c); - } - } -} - -static void -configurerequest(XEvent *e) { - Client *c; - XConfigureRequestEvent *ev = &e->xconfigurerequest; - XWindowChanges wc; - - if((c = getclient(ev->window))) { - c->ismax = False; - if(ev->value_mask & CWBorderWidth) - c->border = ev->border_width; - if(c->isfixed || c->isfloating || isfloating()) { - if(ev->value_mask & CWX) - c->x = ev->x; - if(ev->value_mask & CWY) - c->y = ev->y; - if(ev->value_mask & CWWidth) - c->w = ev->width; - if(ev->value_mask & CWHeight) - c->h = ev->height; - if((c->x + c->w) > sw && c->isfloating) - c->x = sw / 2 - c->w / 2; /* center in x direction */ - if((c->y + c->h) > sh && c->isfloating) - c->y = sh / 2 - c->h / 2; /* center in y direction */ - if((ev->value_mask & (CWX | CWY)) - && !(ev->value_mask & (CWWidth | CWHeight))) - configure(c); - if(isvisible(c)) - XMoveResizeWindow(dpy, c->win, c->x, c->y, c->w, c->h); - } - else - configure(c); - } - else { - wc.x = ev->x; - wc.y = ev->y; - wc.width = ev->width; - wc.height = ev->height; - wc.border_width = ev->border_width; - wc.sibling = ev->above; - wc.stack_mode = ev->detail; - XConfigureWindow(dpy, ev->window, ev->value_mask, &wc); - } - XSync(dpy, False); -} - -static void -configurenotify(XEvent *e) { - XConfigureEvent *ev = &e->xconfigure; - - if (ev->window == root && (ev->width != sw || ev->height != sh)) { - sw = ev->width; - sh = ev->height; - XFreePixmap(dpy, dc.drawable); - dc.drawable = XCreatePixmap(dpy, root, sw, bh, DefaultDepth(dpy, screen)); - XResizeWindow(dpy, barwin, sw, bh); - updatebarpos(); - arrange(); - } -} - -static void -destroynotify(XEvent *e) { - Client *c; - XDestroyWindowEvent *ev = &e->xdestroywindow; - - if((c = getclient(ev->window))) - unmanage(c); -} - -static void -enternotify(XEvent *e) { - Client *c; - XCrossingEvent *ev = &e->xcrossing; - - if(ev->mode != NotifyNormal || ev->detail == NotifyInferior) - return; - if((c = getclient(ev->window))) - focus(c); - else if(ev->window == root) { - selscreen = True; - focus(NULL); - } -} - -static void -expose(XEvent *e) { - XExposeEvent *ev = &e->xexpose; - - if(ev->count == 0) { - if(barwin == ev->window) - drawbar(); - } -} - -static void -keypress(XEvent *e) { - KEYS - unsigned int len = sizeof keys / sizeof keys[0]; - unsigned int i; - KeySym keysym; - XKeyEvent *ev = &e->xkey; - - keysym = XKeycodeToKeysym(dpy, (KeyCode)ev->keycode, 0); - for(i = 0; i < len; i++) - if(keysym == keys[i].keysym - && CLEANMASK(keys[i].mod) == CLEANMASK(ev->state)) - { - if(keys[i].func) - keys[i].func(keys[i].arg); - } -} - -static void -leavenotify(XEvent *e) { - XCrossingEvent *ev = &e->xcrossing; - - if((ev->window == root) && !ev->same_screen) { - selscreen = False; - focus(NULL); - } -} - -static void -mappingnotify(XEvent *e) { - XMappingEvent *ev = &e->xmapping; - - XRefreshKeyboardMapping(ev); - if(ev->request == MappingKeyboard) - grabkeys(); -} - -static void -maprequest(XEvent *e) { - static XWindowAttributes wa; - XMapRequestEvent *ev = &e->xmaprequest; - - if(!XGetWindowAttributes(dpy, ev->window, &wa)) - return; - if(wa.override_redirect) - return; - if(!getclient(ev->window)) - manage(ev->window, &wa); -} - -static void -propertynotify(XEvent *e) { - Client *c; - Window trans; - XPropertyEvent *ev = &e->xproperty; - - if(ev->state == PropertyDelete) - return; /* ignore */ - if((c = getclient(ev->window))) { - switch (ev->atom) { - default: break; - case XA_WM_TRANSIENT_FOR: - XGetTransientForHint(dpy, c->win, &trans); - if(!c->isfloating && (c->isfloating = (getclient(trans) != NULL))) - arrange(); - break; - case XA_WM_NORMAL_HINTS: - updatesizehints(c); - break; - } - if(ev->atom == XA_WM_NAME || ev->atom == netatom[NetWMName]) { - updatetitle(c); - if(c == sel) - drawbar(); - } - } -} - -static void -unmapnotify(XEvent *e) { - Client *c; - XUnmapEvent *ev = &e->xunmap; - - if((c = getclient(ev->window))) - unmanage(c); -} - -/* extern */ - -void (*handler[LASTEvent]) (XEvent *) = { - [ButtonPress] = buttonpress, - [ConfigureRequest] = configurerequest, - [ConfigureNotify] = configurenotify, - [DestroyNotify] = destroynotify, - [EnterNotify] = enternotify, - [LeaveNotify] = leavenotify, - [Expose] = expose, - [KeyPress] = keypress, - [MappingNotify] = mappingnotify, - [MapRequest] = maprequest, - [PropertyNotify] = propertynotify, - [UnmapNotify] = unmapnotify -}; - -void -grabkeys(void) { - KEYS - unsigned int len = sizeof keys / sizeof keys[0]; - unsigned int i; - KeyCode code; - - 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); - } -} diff --git a/main.c b/main.c deleted file mode 100644 index 1e1ad4c..0000000 --- a/main.c +++ /dev/null @@ -1,296 +0,0 @@ -/* See LICENSE file for copyright and license details. */ -#include "dwm.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* extern */ - -char stext[256]; -int screen, sx, sy, sw, sh, wax, way, waw, wah; -unsigned int ntags; -unsigned int numlockmask = 0; -Atom wmatom[WMLast], netatom[NetLast]; -Bool *seltags; -Bool selscreen = True; -Client *clients = NULL; -Client *sel = NULL; -Client *stack = NULL; -Cursor cursor[CurLast]; -Display *dpy; -Window root; - -/* static */ - -static int (*xerrorxlib)(Display *, XErrorEvent *); -static Bool otherwm, readin; -static Bool running = True; - -static void -cleanup(void) { - close(STDIN_FILENO); - while(stack) { - unban(stack); - unmanage(stack); - } - 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); - XDestroyWindow(dpy, barwin); - XFreeCursor(dpy, cursor[CurNormal]); - XFreeCursor(dpy, cursor[CurResize]); - XFreeCursor(dpy, cursor[CurMove]); - XSetInputFocus(dpy, PointerRoot, RevertToPointerRoot, CurrentTime); - XSync(dpy, False); - free(seltags); -} - -static long -getstate(Window w) { - int format, status; - long result = -1; - unsigned char *p = NULL; - unsigned long n, extra; - Atom real; - - status = XGetWindowProperty(dpy, w, wmatom[WMState], 0L, 2L, False, wmatom[WMState], - &real, &format, &n, &extra, (unsigned char **)&p); - if(status != Success) - return -1; - if(n != 0) - result = *p; - XFree(p); - return result; -} - -static void -scan(void) { - unsigned int i, num; - Window *wins, d1, d2; - XWindowAttributes wa; - - wins = NULL; - if(XQueryTree(dpy, root, &d1, &d2, &wins, &num)) { - for(i = 0; i < num; i++) { - if(!XGetWindowAttributes(dpy, wins[i], &wa) - || wa.override_redirect || XGetTransientForHint(dpy, wins[i], &d1)) - continue; - if(wa.map_state == IsViewable || getstate(wins[i]) == IconicState) - manage(wins[i], &wa); - } - for(i = 0; i < num; i++) { /* now the transients */ - if(!XGetWindowAttributes(dpy, wins[i], &wa)) - continue; - if(XGetTransientForHint(dpy, wins[i], &d1) - && (wa.map_state == IsViewable || getstate(wins[i]) == IconicState)) - manage(wins[i], &wa); - } - } - if(wins) - XFree(wins); -} - -static void -setup(void) { - int i, j; - unsigned int mask; - Window w; - XModifierKeymap *modmap; - XSetWindowAttributes wa; - - /* init atoms */ - wmatom[WMProtocols] = XInternAtom(dpy, "WM_PROTOCOLS", False); - wmatom[WMDelete] = XInternAtom(dpy, "WM_DELETE_WINDOW", False); - wmatom[WMName] = XInternAtom(dpy, "WM_NAME", False); - wmatom[WMState] = XInternAtom(dpy, "WM_STATE", False); - netatom[NetSupported] = XInternAtom(dpy, "_NET_SUPPORTED", False); - netatom[NetWMName] = XInternAtom(dpy, "_NET_WM_NAME", False); - XChangeProperty(dpy, root, netatom[NetSupported], XA_ATOM, 32, - PropModeReplace, (unsigned char *) netatom, NetLast); - /* init cursors */ - cursor[CurNormal] = XCreateFontCursor(dpy, XC_left_ptr); - cursor[CurResize] = XCreateFontCursor(dpy, XC_sizing); - cursor[CurMove] = XCreateFontCursor(dpy, XC_fleur); - /* init modifier map */ - modmap = XGetModifierMapping(dpy); - for (i = 0; i < 8; i++) - for (j = 0; j < modmap->max_keypermod; j++) { - if(modmap->modifiermap[i * modmap->max_keypermod + j] - == XKeysymToKeycode(dpy, XK_Num_Lock)) - numlockmask = (1 << i); - } - XFreeModifiermap(modmap); - /* select for events */ - wa.event_mask = SubstructureRedirectMask | SubstructureNotifyMask - | EnterWindowMask | LeaveWindowMask | StructureNotifyMask; - wa.cursor = cursor[CurNormal]; - XChangeWindowAttributes(dpy, root, CWEventMask | CWCursor, &wa); - XSelectInput(dpy, root, wa.event_mask); - grabkeys(); - compileregs(); - for(ntags = 0; tags[ntags]; ntags++); - seltags = emallocz(sizeof(Bool) * ntags); - seltags[0] = True; - /* geometry */ - sx = sy = 0; - sw = DisplayWidth(dpy, screen); - sh = DisplayHeight(dpy, screen); - initstyle(); - initlayouts(); - initbar(); - /* multihead support */ - selscreen = XQueryPointer(dpy, root, &w, &w, &i, &i, &i, &i, &mask); -} - -/* - * Startup Error handler to check if another window manager - * is already running. - */ -static int -xerrorstart(Display *dsply, XErrorEvent *ee) { - otherwm = True; - return -1; -} - -/* extern */ - -Bool -gettextprop(Window w, Atom atom, char *text, unsigned int size) { - char **list = NULL; - int n; - XTextProperty name; - - if(!text || size == 0) - return False; - text[0] = '\0'; - XGetTextProperty(dpy, w, &name, atom); - if(!name.nitems) - return False; - if(name.encoding == XA_STRING) - strncpy(text, (char *)name.value, size - 1); - else { - if(XmbTextPropertyToTextList(dpy, &name, &list, &n) >= Success - && n > 0 && *list) - { - strncpy(text, *list, size - 1); - XFreeStringList(list); - } - } - text[size - 1] = '\0'; - XFree(name.value); - return True; -} - -void -quit(const char *arg) { - readin = running = False; -} - -/* There's no way to check accesses to destroyed windows, thus those cases are - * ignored (especially on UnmapNotify's). Other types of errors call Xlibs - * default error handler, which may call exit. - */ -int -xerror(Display *dpy, XErrorEvent *ee) { - if(ee->error_code == BadWindow - || (ee->request_code == X_SetInputFocus && ee->error_code == BadMatch) - || (ee->request_code == X_PolyText8 && ee->error_code == BadDrawable) - || (ee->request_code == X_PolyFillRectangle && ee->error_code == BadDrawable) - || (ee->request_code == X_PolySegment && ee->error_code == BadDrawable) - || (ee->request_code == X_ConfigureWindow && ee->error_code == BadMatch) - || (ee->request_code == X_GrabKey && ee->error_code == BadAccess) - || (ee->request_code == X_CopyArea && ee->error_code == BadDrawable)) - return 0; - fprintf(stderr, "dwm: fatal error: request code=%d, error code=%d\n", - ee->request_code, ee->error_code); - return xerrorxlib(dpy, ee); /* may call exit */ -} - -int -main(int argc, char *argv[]) { - char *p; - int r, xfd; - fd_set rd; - XEvent ev; - - 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"); - else if(argc != 1) - eprint("usage: dwm [-v]\n"); - setlocale(LC_CTYPE, ""); - if(!(dpy = XOpenDisplay(0))) - eprint("dwm: cannot open display\n"); - xfd = ConnectionNumber(dpy); - screen = DefaultScreen(dpy); - root = RootWindow(dpy, screen); - otherwm = False; - XSetErrorHandler(xerrorstart); - /* this causes an error if some other window manager is running */ - XSelectInput(dpy, root, SubstructureRedirectMask); - XSync(dpy, False); - if(otherwm) - eprint("dwm: another window manager is already running\n"); - - XSync(dpy, False); - XSetErrorHandler(NULL); - xerrorxlib = XSetErrorHandler(xerror); - XSync(dpy, False); - setup(); - drawbar(); - scan(); - - /* main event loop, also reads status text from stdin */ - XSync(dpy, False); - readin = True; - while(running) { - FD_ZERO(&rd); - if(readin) - FD_SET(STDIN_FILENO, &rd); - FD_SET(xfd, &rd); - if(select(xfd + 1, &rd, NULL, NULL, NULL) == -1) { - if(errno == EINTR) - continue; - eprint("select failed\n"); - } - if(FD_ISSET(STDIN_FILENO, &rd)) { - switch(r = read(STDIN_FILENO, stext, sizeof stext - 1)) { - case -1: - strncpy(stext, strerror(errno), sizeof stext - 1); - stext[sizeof stext - 1] = '\0'; - readin = False; - break; - case 0: - strncpy(stext, "EOF", 4); - 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); - } - drawbar(); - } - while(XPending(dpy)) { - XNextEvent(dpy, &ev); - if(handler[ev.type]) - (handler[ev.type])(&ev); /* call handler */ - } - } - cleanup(); - XCloseDisplay(dpy); - return 0; -} diff --git a/screen.c b/screen.c deleted file mode 100644 index 142ed56..0000000 --- a/screen.c +++ /dev/null @@ -1,340 +0,0 @@ -/* See LICENSE file for copyright and license details. */ -#include "dwm.h" -#include -#include -#include -#include -#include - -/* static */ - -typedef struct { - const char *symbol; - void (*arrange)(void); -} Layout; - -typedef struct { - const char *prop; - const char *tags; - Bool isfloating; -} Rule; - -typedef struct { - regex_t *propregex; - regex_t *tagregex; -} Regs; - -TAGS -RULES - -static unsigned int nrules = 0; -static unsigned int nlayouts = 0; -static unsigned int ltidx = 0; /* default */ -static Regs *regs = NULL; - -static unsigned int -idxoftag(const char *tag) { - unsigned int i; - - for(i = 0; i < ntags; i++) - if(tags[i] == tag) - return i; - return 0; -} - -static void -floating(void) { /* default floating layout */ - Client *c; - - for(c = clients; c; c = c->next) - if(isvisible(c)) - resize(c, c->x, c->y, c->w, c->h, True); -} - -LAYOUTS - -/* extern */ - -unsigned int blw = 0; - -void -applyrules(Client *c) { - static char buf[512]; - unsigned int i, j; - regmatch_t tmp; - Bool matched = False; - XClassHint ch = { 0 }; - - /* rule matching */ - XGetClassHint(dpy, c->win, &ch); - 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++) - 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++) { - if(!regexec(regs[i].tagregex, tags[j], 1, &tmp, 0)) { - matched = True; - c->tags[j] = True; - } - } - } - if(ch.res_class) - XFree(ch.res_class); - if(ch.res_name) - XFree(ch.res_name); - if(!matched) - for(i = 0; i < ntags; i++) - c->tags[i] = seltags[i]; -} - -void -arrange(void) { - Client *c; - - for(c = clients; c; c = c->next) - if(isvisible(c)) - unban(c); - else - ban(c); - layouts[ltidx].arrange(); - focus(NULL); - restack(); -} - -void -compileregs(void) { - unsigned int i; - regex_t *reg; - - if(regs) - return; - nrules = sizeof rules / sizeof rules[0]; - regs = emallocz(nrules * sizeof(Regs)); - for(i = 0; i < nrules; i++) { - if(rules[i].prop) { - reg = emallocz(sizeof(regex_t)); - if(regcomp(reg, rules[i].prop, REG_EXTENDED)) - free(reg); - else - regs[i].propregex = reg; - } - if(rules[i].tags) { - reg = emallocz(sizeof(regex_t)); - if(regcomp(reg, rules[i].tags, REG_EXTENDED)) - free(reg); - else - regs[i].tagregex = reg; - } - } -} - -void -focusnext(const char *arg) { - Client *c; - - if(!sel) - return; - for(c = sel->next; c && !isvisible(c); c = c->next); - if(!c) - for(c = clients; c && !isvisible(c); c = c->next); - if(c) { - focus(c); - restack(); - } -} - -void -focusprev(const char *arg) { - Client *c; - - if(!sel) - return; - for(c = sel->prev; c && !isvisible(c); c = c->prev); - if(!c) { - for(c = clients; c && c->next; c = c->next); - for(; c && !isvisible(c); c = c->prev); - } - if(c) { - focus(c); - restack(); - } -} - -const char * -getsymbol(void) -{ - return layouts[ltidx].symbol; -} - -void -initlayouts(void) { - unsigned int i, w; - - nlayouts = sizeof layouts / sizeof layouts[0]; - for(blw = i = 0; i < nlayouts; i++) { - w = textw(layouts[i].symbol); - if(w > blw) - blw = w; - } -} - -Bool -isfloating(void) { - return layouts[ltidx].arrange == floating; -} - -Bool -isarrange(void (*func)()) -{ - return func == layouts[ltidx].arrange; -} - -Bool -isvisible(Client *c) { - unsigned int i; - - for(i = 0; i < ntags; i++) - if(c->tags[i] && seltags[i]) - return True; - return False; -} - -Client * -nexttiled(Client *c) { - for(; c && (c->isfloating || !isvisible(c)); c = c->next); - return c; -} - -void -restack(void) { - Client *c; - XEvent ev; - XWindowChanges wc; - - drawbar(); - if(!sel) - return; - if(sel->isfloating || isfloating()) - XRaiseWindow(dpy, sel->win); - if(!isfloating()) { - wc.stack_mode = Below; - wc.sibling = barwin; - if(!sel->isfloating) { - XConfigureWindow(dpy, sel->win, CWSibling | CWStackMode, &wc); - wc.sibling = sel->win; - } - for(c = nexttiled(clients); c; c = nexttiled(c->next)) { - if(c == sel) - continue; - XConfigureWindow(dpy, c->win, CWSibling | CWStackMode, &wc); - wc.sibling = c->win; - } - } - XSync(dpy, False); - while(XCheckMaskEvent(dpy, EnterWindowMask, &ev)); -} - -void -setlayout(const char *arg) { - unsigned int i; - - if(!arg) { - if(++ltidx == nlayouts) - ltidx = 0;; - } - else { - for(i = 0; i < nlayouts; i++) - if(!strcmp(arg, layouts[i].symbol)) - break; - if(i == nlayouts) - return; - ltidx = i; - } - if(sel) - arrange(); - else - drawbar(); -} - -void -tag(const char *arg) { - unsigned int i; - - if(!sel) - return; - for(i = 0; i < ntags; i++) - sel->tags[i] = arg == NULL; - i = idxoftag(arg); - if(i >= 0 && i < ntags) - sel->tags[i] = True; - arrange(); -} - -void -togglefloating(const char *arg) { - if(!sel) - return; - sel->isfloating = !sel->isfloating; - if(sel->isfloating) - resize(sel, sel->x, sel->y, sel->w, sel->h, True); - arrange(); -} - -void -togglemax(const char *arg) { - XEvent ev; - - if(!sel || (!isfloating() && !sel->isfloating) || sel->isfixed) - return; - if((sel->ismax = !sel->ismax)) { - sel->rx = sel->x; - sel->ry = sel->y; - sel->rw = sel->w; - sel->rh = sel->h; - resize(sel, wax, way, waw - 2 * sel->border, wah - 2 * sel->border, True); - } - else - resize(sel, sel->rx, sel->ry, sel->rw, sel->rh, True); - drawbar(); - while(XCheckMaskEvent(dpy, EnterWindowMask, &ev)); -} - -void -toggletag(const char *arg) { - unsigned int i, j; - - if(!sel) - return; - i = idxoftag(arg); - sel->tags[i] = !sel->tags[i]; - for(j = 0; j < ntags && !sel->tags[j]; j++); - if(j == ntags) - sel->tags[i] = True; - arrange(); -} - -void -toggleview(const char *arg) { - unsigned int i, j; - - i = idxoftag(arg); - seltags[i] = !seltags[i]; - for(j = 0; j < ntags && !seltags[j]; j++); - if(j == ntags) - seltags[i] = True; /* cannot toggle last view */ - arrange(); -} - -void -view(const char *arg) { - unsigned int i; - - for(i = 0; i < ntags; i++) - seltags[i] = arg == NULL; - i = idxoftag(arg); - if(i >= 0 && i < ntags) - seltags[i] = True; - arrange(); -} diff --git a/tile.c b/tile.c deleted file mode 100644 index 56a06d3..0000000 --- a/tile.c +++ /dev/null @@ -1,85 +0,0 @@ -/* See LICENSE file for copyright and license details. */ -#include "dwm.h" -#include - -/* static */ - -static double mwfact = MWFACT; - -/* extern */ - -void -setmwfact(const char *arg) { - double delta; - - if(!isarrange(tile)) - return; - /* arg handling, manipulate mwfact */ - if(arg == NULL) - mwfact = MWFACT; - else if(1 == sscanf(arg, "%lf", &delta)) { - if(arg[0] != '+' && arg[0] != '-') - mwfact = delta; - else - mwfact += delta; - if(mwfact < 0.1) - mwfact = 0.1; - else if(mwfact > 0.9) - mwfact = 0.9; - } - arrange(); -} - -void -tile(void) { - unsigned int i, n, nx, ny, nw, nh, mw, th; - Client *c; - - for(n = 0, c = nexttiled(clients); c; c = nexttiled(c->next)) - n++; - - /* window geoms */ - mw = (n == 1) ? waw : mwfact * waw; - th = (n > 1) ? wah / (n - 1) : 0; - if(n > 1 && th < bh) - th = wah; - - nx = wax; - ny = way; - for(i = 0, c = nexttiled(clients); c; c = nexttiled(c->next), i++) { - c->ismax = False; - if(i == 0) { /* master */ - nw = mw - 2 * c->border; - nh = wah - 2 * c->border; - } - else { /* tile window */ - if(i == 1) { - ny = way; - nx += mw; - } - nw = waw - mw - 2 * c->border; - if(i + 1 == n) /* remainder */ - nh = (way + wah) - ny - 2 * c->border; - else - nh = th - 2 * c->border; - } - resize(c, nx, ny, nw, nh, RESIZEHINTS); - if(n > 1 && th != wah) - ny += nh + 2 * c->border; - } -} - -void -zoom(const char *arg) { - Client *c; - - if(!sel || !isarrange(tile) || sel->isfloating) - return; - if((c = sel) == nexttiled(clients)) - if(!(c = nexttiled(c->next))) - return; - detach(c); - attach(c); - focus(c); - arrange(); -} diff --git a/tile.h b/tile.h deleted file mode 100644 index 38e0c76..0000000 --- a/tile.h +++ /dev/null @@ -1,6 +0,0 @@ -/* See LICENSE file for copyright and license details. */ - -/* tile.c */ -void setmwfact(const char *arg); /* sets master width factor */ -void tile(void); /* arranges all windows tiled */ -void zoom(const char *arg); /* zooms the focused client to master area, arg is ignored */ diff --git a/util.c b/util.c deleted file mode 100644 index d39d73c..0000000 --- a/util.c +++ /dev/null @@ -1,52 +0,0 @@ -/* See LICENSE file for copyright and license details. */ -#include "dwm.h" -#include -#include -#include -#include -#include - -/* extern */ - -void * -emallocz(unsigned int size) { - void *res = calloc(1, size); - - if(!res) - eprint("fatal: could not malloc() %u bytes\n", size); - return res; -} - -void -eprint(const char *errstr, ...) { - va_list ap; - - va_start(ap, errstr); - vfprintf(stderr, errstr, ap); - va_end(ap); - exit(EXIT_FAILURE); -} - -void -spawn(const char *arg) { - static char *shell = NULL; - - if(!shell && !(shell = getenv("SHELL"))) - shell = "/bin/sh"; - if(!arg) - return; - /* The double-fork construct avoids zombie processes and keeps the code - * clean from stupid signal handlers. */ - if(fork() == 0) { - if(fork() == 0) { - if(dpy) - close(ConnectionNumber(dpy)); - setsid(); - execl(shell, shell, "-c", arg, (char *)NULL); - fprintf(stderr, "dwm: execl '%s -c %s'", shell, arg); - perror(" failed"); - } - exit(0); - } - wait(0); -} -- 2.20.1 From 2d7bb8d7c974c1ae5430fa1561c850f0ea4623d3 Mon Sep 17 00:00:00 2001 From: "Anselm R. Garbe" Date: Sat, 15 Sep 2007 22:31:24 +0200 Subject: [PATCH 07/16] removed grabkeys, not necessary --- dwm.c | 47 +++++++++++++++++++++-------------------------- 1 file changed, 21 insertions(+), 26 deletions(-) diff --git a/dwm.c b/dwm.c index d497f4c..cd7c9e5 100644 --- a/dwm.c +++ b/dwm.c @@ -129,7 +129,6 @@ static void mappingnotify(XEvent *e); static void maprequest(XEvent *e); static void propertynotify(XEvent *e); static void unmapnotify(XEvent *e); -static void grabkeys(void); static unsigned int idxoftag(const char *tag); static void floating(void); /* default floating layout */ static void applyrules(Client *c); @@ -1070,9 +1069,26 @@ keypress(XEvent *e) { KEYS unsigned int len = sizeof keys / sizeof keys[0]; unsigned int i; + KeyCode code; KeySym keysym; - XKeyEvent *ev = &e->xkey; - + 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++) if(keysym == keys[i].keysym @@ -1099,7 +1115,7 @@ mappingnotify(XEvent *e) { XRefreshKeyboardMapping(ev); if(ev->request == MappingKeyboard) - grabkeys(); + keypress(NULL); } static void @@ -1152,27 +1168,6 @@ unmapnotify(XEvent *e) { unmanage(c); } -static void -grabkeys(void) { - KEYS - unsigned int len = sizeof keys / sizeof keys[0]; - unsigned int i; - KeyCode code; - - 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); - } -} - static unsigned int idxoftag(const char *tag) { unsigned int i; @@ -1546,7 +1541,7 @@ setup(void) { wa.cursor = cursor[CurNormal]; XChangeWindowAttributes(dpy, root, CWEventMask | CWCursor, &wa); XSelectInput(dpy, root, wa.event_mask); - grabkeys(); + keypress(NULL); /* grabkeys */ compileregs(); for(ntags = 0; tags[ntags]; ntags++); seltags = emallocz(sizeof(Bool) * ntags); -- 2.20.1 From e041ff70b0e4438e741405d994e13f91435ed321 Mon Sep 17 00:00:00 2001 From: "Anselm R. Garbe" Date: Sat, 15 Sep 2007 22:33:46 +0200 Subject: [PATCH 08/16] backporting my intro-comment of old dwm.h --- dwm.c | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/dwm.c b/dwm.c index cd7c9e5..7d2a0de 100644 --- a/dwm.c +++ b/dwm.c @@ -1,4 +1,33 @@ -/* See LICENSE file for copyright and license details. */ +/* See LICENSE file for copyright and license details. + * + * dynamic window manager is designed like any other X client as well. It is + * driven through handling X events. In contrast to other X clients, a window + * manager selects for SubstructureRedirectMask on the root window, to receive + * events about window (dis-)appearance. Only one X connection at a time is + * allowed to select for this event mask. + * + * Calls to fetch an X event from the event queue are blocking. Due reading + * status text from standard input, a select()-driven main loop has been + * implemented which selects for reads on the X connection and STDIN_FILENO to + * handle all data smoothly. The event handlers of dwm are organized in an + * array which is accessed whenever a new event has been fetched. This allows + * event dispatching in O(1) time. + * + * Each child of the root window is called a client, except windows which have + * 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. + * + * Keys and tagging rules are organized as arrays and defined in the config.h + * file. These arrays are kept static in event.o and tag.o respectively, + * because no other part of dwm needs access to them. The current layout is + * represented by the lt pointer. + * + * To understand everything else, start reading main(). + */ #include #include #include -- 2.20.1 From a6df995b5d4efabc243d6f564db356fc406c6601 Mon Sep 17 00:00:00 2001 From: "Anselm R. Garbe" Date: Sat, 15 Sep 2007 22:45:18 +0200 Subject: [PATCH 09/16] ordered function forward definitions --- dwm.c | 113 +++++++++++++++++++++++++++++----------------------------- 1 file changed, 57 insertions(+), 56 deletions(-) diff --git a/dwm.c b/dwm.c index 7d2a0de..2597f4f 100644 --- a/dwm.c +++ b/dwm.c @@ -113,84 +113,85 @@ typedef struct { } Regs; /* functions */ -static void eprint(const char *errstr, ...); -static void *emallocz(unsigned int size); -static void spawn(const char *arg); -static void drawsquare(Bool filled, Bool empty, unsigned long col[ColLast]); -static unsigned long initcolor(const char *colstr); -static void initfont(const char *fontstr); -static Bool isoccupied(unsigned int t); -static unsigned int textnw(const char *text, unsigned int len); -static void drawtext(const char *text, unsigned long col[ColLast]); -static void drawbar(void); -static void initstyle(void); -static void initbar(void); -static unsigned int textw(const char *text); -static void togglebar(const char *arg); -static void updatebarpos(void); + +static void applyrules(Client *c); +static void arrange(void); +static void attach(Client *c); static void attachstack(Client *c); -static void detachstack(Client *c); -static void grabbuttons(Client *c, Bool focused); -static Bool isprotodel(Client *c); -static void setclientstate(Client *c, long state); -static int xerrordummy(Display *dsply, XErrorEvent *ee); static void ban(Client *c); -static void configure(Client *c); -static void killclient(const char *arg); -static void manage(Window w, XWindowAttributes *wa); -static void resize(Client *c, int x, int y, int w, int h, Bool sizehints); -static void unban(Client *c); -static void unmanage(Client *c); -static void updatesizehints(Client *c); -static void updatetitle(Client *c); -static Client *getclient(Window w); -static void movemouse(Client *c); -static void resizemouse(Client *c); static void buttonpress(XEvent *e); -static void configurerequest(XEvent *e); +static void cleanup(void); +static void compileregs(void); +static void configure(Client *c); static void configurenotify(XEvent *e); +static void configurerequest(XEvent *e); static void destroynotify(XEvent *e); +static void detach(Client *c); +static void detachstack(Client *c); +static void drawbar(void); +static void drawsquare(Bool filled, Bool empty, unsigned long col[ColLast]); +static void drawtext(const char *text, unsigned long col[ColLast]); +static void *emallocz(unsigned int size); static void enternotify(XEvent *e); +static void eprint(const char *errstr, ...); static void expose(XEvent *e); -static void keypress(XEvent *e); -static void leavenotify(XEvent *e); -static void mappingnotify(XEvent *e); -static void maprequest(XEvent *e); -static void propertynotify(XEvent *e); -static void unmapnotify(XEvent *e); -static unsigned int idxoftag(const char *tag); static void floating(void); /* default floating layout */ -static void applyrules(Client *c); -static void compileregs(void); +static void focus(Client *c); static void focusnext(const char *arg); static void focusprev(const char *arg); +static Client *getclient(Window w); +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 unsigned int idxoftag(const char *tag); +static void initbar(void); +static unsigned long initcolor(const char *colstr); +static void initfont(const char *fontstr); static void initlayouts(void); +static void initstyle(void); +static Bool isarrange(void (*func)()); static Bool isfloating(void); +static Bool isoccupied(unsigned int t); +static Bool isprotodel(Client *c); static Bool isvisible(Client *c); +static void keypress(XEvent *e); +static void killclient(const char *arg); +static void leavenotify(XEvent *e); +static void manage(Window w, XWindowAttributes *wa); +static void mappingnotify(XEvent *e); +static void maprequest(XEvent *e); +static void movemouse(Client *c); +static Client *nexttiled(Client *c); +static void propertynotify(XEvent *e); +static void quit(const char *arg); +static void resize(Client *c, int x, int y, int w, int h, Bool sizehints); +static void resizemouse(Client *c); static void restack(void); +static void scan(void); +static void setclientstate(Client *c, long state); static void setlayout(const char *arg); +static void setmwfact(const char *arg); +static void setup(void); +static void spawn(const char *arg); static void tag(const char *arg); +static unsigned int textnw(const char *text, unsigned int len); +static unsigned int textw(const char *text); +static void tile(void); +static void togglebar(const char *arg); static void togglefloating(const char *arg); static void togglemax(const char *arg); static void toggletag(const char *arg); static void toggleview(const char *arg); +static void unban(Client *c); +static void unmanage(Client *c); +static void unmapnotify(XEvent *e); +static void updatebarpos(void); +static void updatesizehints(Client *c); +static void updatetitle(Client *c); static void view(const char *arg); -static void cleanup(void); -static long getstate(Window w); -static void scan(void); -static void setup(void); -static int xerrorstart(Display *dsply, XErrorEvent *ee); -static Bool gettextprop(Window w, Atom atom, char *text, unsigned int size); -static void quit(const char *arg); static int xerror(Display *dpy, XErrorEvent *ee); -static void arrange(void); -static void attach(Client *c); -static void detach(Client *c); -static void focus(Client *c); -static Bool isarrange(void (*func)()); -static Client *nexttiled(Client *c); -static void setmwfact(const char *arg); -static void tile(void); +static int xerrordummy(Display *dsply, XErrorEvent *ee); +static int xerrorstart(Display *dsply, XErrorEvent *ee); static void zoom(const char *arg); #include "config.h" -- 2.20.1 From a026617c657bea56d93b586b50a016477d52cc85 Mon Sep 17 00:00:00 2001 From: "Anselm R. Garbe" Date: Sun, 16 Sep 2007 10:23:53 +0200 Subject: [PATCH 10/16] macros which have been defined in config.h can only be used at function level, however you can nest code into config.h now for implementing a different layout (just for example), eg. #include "supertile.c" --- dwm.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/dwm.c b/dwm.c index 2597f4f..3168ee0 100644 --- a/dwm.c +++ b/dwm.c @@ -194,20 +194,16 @@ static int xerrordummy(Display *dsply, XErrorEvent *ee); static int xerrorstart(Display *dsply, XErrorEvent *ee); static void zoom(const char *arg); -#include "config.h" - /* variables */ static char stext[256]; -static double mwfact = MWFACT; +static double mwfact; static int screen, sx, sy, sw, sh, wax, way, waw, wah; static int (*xerrorxlib)(Display *, XErrorEvent *); -static unsigned int bh; +static unsigned int bh, bpos, ntags; static unsigned int blw = 0; -static unsigned int bpos = BARPOS; static unsigned int ltidx = 0; /* default */ static unsigned int nlayouts = 0; static unsigned int nrules = 0; -static unsigned int ntags; static unsigned int numlockmask = 0; static void (*handler[LASTEvent]) (XEvent *) = { [ButtonPress] = buttonpress, @@ -237,6 +233,9 @@ static DC dc = {0}; static Window barwin, root; static Regs *regs = NULL; +/* configuration, allows nested code to work on above variables */ +#include "config.h" + static void eprint(const char *errstr, ...) { va_list ap; @@ -1806,6 +1805,11 @@ main(int argc, char *argv[]) { eprint("dwm-"VERSION", © 2006-2007 A. R. Garbe, S. van Dijk, J. Salmi, P. Hruby, S. Nagy\n"); else if(argc != 1) eprint("usage: dwm [-v]\n"); + + /* macros from config.h can be used beginning within main() */ + mwfact = MWFACT; + bpos = BARPOS; + setlocale(LC_CTYPE, ""); if(!(dpy = XOpenDisplay(0))) eprint("dwm: cannot open display\n"); -- 2.20.1 From 11cfff2dae9fb09aace8f46bf338a051cbd36fb3 Mon Sep 17 00:00:00 2001 From: "Anselm R. Garbe" Date: Sun, 16 Sep 2007 10:24:35 +0200 Subject: [PATCH 11/16] corrected a misleading comment --- dwm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dwm.c b/dwm.c index 3168ee0..e3dd6c8 100644 --- a/dwm.c +++ b/dwm.c @@ -1806,7 +1806,7 @@ main(int argc, char *argv[]) { else if(argc != 1) eprint("usage: dwm [-v]\n"); - /* macros from config.h can be used beginning within main() */ + /* macros from config.h can be used at function level only */ mwfact = MWFACT; bpos = BARPOS; -- 2.20.1 From 49197fe4bf023478108c76c1bed74a7d1ef138de Mon Sep 17 00:00:00 2001 From: "Anselm R. Garbe" Date: Sun, 16 Sep 2007 11:53:14 +0200 Subject: [PATCH 12/16] ordered all functions alphabetically --- dwm.c | 2358 ++++++++++++++++++++++++++++----------------------------- 1 file changed, 1178 insertions(+), 1180 deletions(-) diff --git a/dwm.c b/dwm.c index e3dd6c8..b6659ae 100644 --- a/dwm.c +++ b/dwm.c @@ -233,769 +233,206 @@ static DC dc = {0}; static Window barwin, root; static Regs *regs = NULL; -/* configuration, allows nested code to work on above variables */ +/* configuration, allows nested code to access above variables */ #include "config.h" +/* implementation */ static void -eprint(const char *errstr, ...) { - va_list ap; +applyrules(Client *c) { + static char buf[512]; + unsigned int i, j; + regmatch_t tmp; + Bool matched = False; + XClassHint ch = { 0 }; - va_start(ap, errstr); - vfprintf(stderr, errstr, ap); - va_end(ap); - exit(EXIT_FAILURE); + /* rule matching */ + XGetClassHint(dpy, c->win, &ch); + 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++) + 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++) { + if(!regexec(regs[i].tagregex, tags[j], 1, &tmp, 0)) { + matched = True; + c->tags[j] = True; + } + } + } + if(ch.res_class) + XFree(ch.res_class); + if(ch.res_name) + XFree(ch.res_name); + if(!matched) + for(i = 0; i < ntags; i++) + c->tags[i] = seltags[i]; } -static void * -emallocz(unsigned int size) { - void *res = calloc(1, size); +static void +arrange(void) { + Client *c; - if(!res) - eprint("fatal: could not malloc() %u bytes\n", size); - return res; + for(c = clients; c; c = c->next) + if(isvisible(c)) + unban(c); + else + ban(c); + layouts[ltidx].arrange(); + focus(NULL); + restack(); } static void -spawn(const char *arg) { - static char *shell = NULL; - - if(!shell && !(shell = getenv("SHELL"))) - shell = "/bin/sh"; - if(!arg) - return; - /* The double-fork construct avoids zombie processes and keeps the code - * clean from stupid signal handlers. */ - if(fork() == 0) { - if(fork() == 0) { - if(dpy) - close(ConnectionNumber(dpy)); - setsid(); - execl(shell, shell, "-c", arg, (char *)NULL); - fprintf(stderr, "dwm: execl '%s -c %s'", shell, arg); - perror(" failed"); - } - exit(0); - } - wait(0); +attach(Client *c) { + if(clients) + clients->prev = c; + c->next = clients; + clients = c; } static void -drawsquare(Bool filled, Bool empty, unsigned long col[ColLast]) { - int x; - XGCValues gcv; - XRectangle r = { dc.x, dc.y, dc.w, dc.h }; - - gcv.foreground = col[ColFG]; - XChangeGC(dpy, dc.gc, GCForeground, &gcv); - x = (dc.font.ascent + dc.font.descent + 2) / 4; - r.x = dc.x + 1; - r.y = dc.y + 1; - if(filled) { - r.width = r.height = x + 1; - XFillRectangles(dpy, dc.drawable, dc.gc, &r, 1); - } - else if(empty) { - r.width = r.height = x; - XDrawRectangles(dpy, dc.drawable, dc.gc, &r, 1); - } +attachstack(Client *c) { + c->snext = stack; + stack = c; } -static unsigned long -initcolor(const char *colstr) { - Colormap cmap = DefaultColormap(dpy, screen); - XColor color; - - if(!XAllocNamedColor(dpy, cmap, colstr, &color, &color)) - eprint("error, cannot allocate color '%s'\n", colstr); - return color.pixel; +static void +ban(Client *c) { + if(c->isbanned) + return; + XMoveWindow(dpy, c->win, c->x + 2 * sw, c->y); + c->isbanned = True; } static void -initfont(const char *fontstr) { - char *def, **missing; - int i, n; +buttonpress(XEvent *e) { + unsigned int i, x; + Client *c; + XButtonPressedEvent *ev = &e->xbutton; - missing = NULL; - if(dc.font.set) - XFreeFontSet(dpy, dc.font.set); - 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) { - XFontSetExtents *font_extents; - XFontStruct **xfonts; - char **font_names; - dc.font.ascent = dc.font.descent = 0; - font_extents = XExtentsOfFontSet(dc.font.set); - n = XFontsOfFontSet(dc.font.set, &xfonts, &font_names); - for(i = 0, dc.font.ascent = 0, dc.font.descent = 0; i < n; i++) { - if(dc.font.ascent < (*xfonts)->ascent) - dc.font.ascent = (*xfonts)->ascent; - if(dc.font.descent < (*xfonts)->descent) - dc.font.descent = (*xfonts)->descent; - xfonts++; + if(barwin == ev->window) { + x = 0; + for(i = 0; i < ntags; i++) { + x += textw(tags[i]); + if(ev->x < x) { + if(ev->button == Button1) { + if(ev->state & MODKEY) + tag(tags[i]); + else + view(tags[i]); + } + else if(ev->button == Button3) { + if(ev->state & MODKEY) + toggletag(tags[i]); + else + toggleview(tags[i]); + } + return; + } } + if((ev->x < x + blw) && ev->button == Button1) + setlayout(NULL); } - else { - if(dc.font.xfont) - XFreeFont(dpy, dc.font.xfont); - dc.font.xfont = NULL; - if(!(dc.font.xfont = XLoadQueryFont(dpy, fontstr)) - || !(dc.font.xfont = XLoadQueryFont(dpy, "fixed"))) - eprint("error, cannot load font: '%s'\n", fontstr); - dc.font.ascent = dc.font.xfont->ascent; - dc.font.descent = dc.font.xfont->descent; + else if((c = getclient(ev->window))) { + focus(c); + if(CLEANMASK(ev->state) != MODKEY) + return; + if(ev->button == Button1 && (isfloating() || c->isfloating)) { + restack(); + movemouse(c); + } + else if(ev->button == Button2) + zoom(NULL); + else if(ev->button == Button3 + && (isfloating() || c->isfloating) && !c->isfixed) + { + restack(); + resizemouse(c); + } } - dc.font.height = dc.font.ascent + dc.font.descent; -} - -static Bool -isoccupied(unsigned int t) { - Client *c; - - for(c = clients; c; c = c->next) - if(c->tags[t]) - return True; - return False; } -static unsigned 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; +static void +cleanup(void) { + close(STDIN_FILENO); + while(stack) { + unban(stack); + unmanage(stack); } - return XTextWidth(dc.font.xfont, text, len); + 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); + XDestroyWindow(dpy, barwin); + XFreeCursor(dpy, cursor[CurNormal]); + XFreeCursor(dpy, cursor[CurResize]); + XFreeCursor(dpy, cursor[CurMove]); + XSetInputFocus(dpy, PointerRoot, RevertToPointerRoot, CurrentTime); + XSync(dpy, False); + free(seltags); } static void -drawtext(const char *text, unsigned long col[ColLast]) { - int x, y, w, h; - static char buf[256]; - unsigned int len, olen; - XRectangle r = { dc.x, dc.y, dc.w, dc.h }; +compileregs(void) { + unsigned int i; + regex_t *reg; - XSetForeground(dpy, dc.gc, col[ColBG]); - XFillRectangles(dpy, dc.drawable, dc.gc, &r, 1); - if(!text) + if(regs) return; - w = 0; - olen = len = strlen(text); - if(len >= sizeof buf) - len = sizeof buf - 1; - memcpy(buf, text, len); - buf[len] = 0; - 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 */ - while(len && (w = textnw(buf, len)) > dc.w - h) - buf[--len] = 0; - if(len < olen) { - if(len > 1) - buf[len - 1] = '.'; - if(len > 2) - buf[len - 2] = '.'; - if(len > 3) - buf[len - 3] = '.'; - } - if(w > dc.w) - return; /* too long */ - XSetForeground(dpy, dc.gc, col[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); -} - -static void -drawbar(void) { - int i, x; - - dc.x = dc.y = 0; - for(i = 0; i < ntags; i++) { - dc.w = textw(tags[i]); - if(seltags[i]) { - drawtext(tags[i], dc.sel); - drawsquare(sel && sel->tags[i], isoccupied(i), dc.sel); - } - else { - drawtext(tags[i], dc.norm); - drawsquare(sel && sel->tags[i], isoccupied(i), dc.norm); - } - dc.x += dc.w; - } - dc.w = blw; - drawtext(layouts[ltidx].symbol, dc.norm); - x = dc.x + dc.w; - dc.w = textw(stext); - dc.x = sw - dc.w; - if(dc.x < x) { - dc.x = x; - dc.w = sw - x; - } - drawtext(stext, dc.norm); - if((dc.w = dc.x - x) > bh) { - dc.x = x; - if(sel) { - drawtext(sel->name, dc.sel); - drawsquare(sel->ismax, sel->isfloating, dc.sel); - } - else - drawtext(NULL, dc.norm); - } - XCopyArea(dpy, dc.drawable, barwin, dc.gc, 0, 0, sw, bh, 0, 0); - XSync(dpy, False); -} - -static void -initstyle(void) { - dc.norm[ColBorder] = initcolor(NORMBORDERCOLOR); - dc.norm[ColBG] = initcolor(NORMBGCOLOR); - dc.norm[ColFG] = initcolor(NORMFGCOLOR); - dc.sel[ColBorder] = initcolor(SELBORDERCOLOR); - dc.sel[ColBG] = initcolor(SELBGCOLOR); - dc.sel[ColFG] = initcolor(SELFGCOLOR); - initfont(FONT); - dc.h = bh = dc.font.height + 2; -} - -static void -initbar(void) { - XSetWindowAttributes wa; - - wa.override_redirect = 1; - wa.background_pixmap = ParentRelative; - wa.event_mask = ButtonPressMask | ExposureMask; - barwin = XCreateWindow(dpy, root, sx, sy, sw, bh, 0, - DefaultDepth(dpy, screen), CopyFromParent, DefaultVisual(dpy, screen), - CWOverrideRedirect | CWBackPixmap | CWEventMask, &wa); - XDefineCursor(dpy, barwin, cursor[CurNormal]); - updatebarpos(); - XMapRaised(dpy, barwin); - strcpy(stext, "dwm-"VERSION); - dc.drawable = XCreatePixmap(dpy, root, sw, bh, DefaultDepth(dpy, screen)); - dc.gc = XCreateGC(dpy, root, 0, 0); - XSetLineAttributes(dpy, dc.gc, 1, LineSolid, CapButt, JoinMiter); - if(!dc.font.set) - XSetFont(dpy, dc.gc, dc.font.xfont->fid); -} - -static unsigned int -textw(const char *text) { - return textnw(text, strlen(text)) + dc.font.height; -} - -static void -togglebar(const char *arg) { - if(bpos == BarOff) - bpos = (BARPOS == BarOff) ? BarTop : BARPOS; - else - bpos = BarOff; - updatebarpos(); - arrange(); -} - -static void -updatebarpos(void) { - XEvent ev; - - wax = sx; - way = sy; - wah = sh; - waw = sw; - switch(bpos) { - default: - wah -= bh; - way += bh; - XMoveWindow(dpy, barwin, sx, sy); - break; - case BarBot: - wah -= bh; - XMoveWindow(dpy, barwin, sx, sy + wah); - break; - case BarOff: - XMoveWindow(dpy, barwin, sx, sy - bh); - break; - } - XSync(dpy, False); - while(XCheckMaskEvent(dpy, EnterWindowMask, &ev)); -} - -static void -attachstack(Client *c) { - c->snext = stack; - stack = c; -} - -static void -detachstack(Client *c) { - Client **tc; - - for(tc=&stack; *tc && *tc != c; tc=&(*tc)->snext); - *tc = c->snext; -} - -static void -grabbuttons(Client *c, Bool focused) { - XUngrabButton(dpy, AnyButton, AnyModifier, c->win); - - if(focused) { - XGrabButton(dpy, Button1, MODKEY, c->win, False, BUTTONMASK, - GrabModeAsync, GrabModeSync, None, None); - XGrabButton(dpy, Button1, MODKEY | LockMask, c->win, False, BUTTONMASK, - GrabModeAsync, GrabModeSync, None, None); - XGrabButton(dpy, Button1, MODKEY | numlockmask, c->win, False, BUTTONMASK, - GrabModeAsync, GrabModeSync, None, None); - XGrabButton(dpy, Button1, MODKEY | numlockmask | LockMask, c->win, False, BUTTONMASK, - GrabModeAsync, GrabModeSync, None, None); - - XGrabButton(dpy, Button2, MODKEY, c->win, False, BUTTONMASK, - GrabModeAsync, GrabModeSync, None, None); - XGrabButton(dpy, Button2, MODKEY | LockMask, c->win, False, BUTTONMASK, - GrabModeAsync, GrabModeSync, None, None); - XGrabButton(dpy, Button2, MODKEY | numlockmask, c->win, False, BUTTONMASK, - GrabModeAsync, GrabModeSync, None, None); - XGrabButton(dpy, Button2, MODKEY | numlockmask | LockMask, c->win, False, BUTTONMASK, - GrabModeAsync, GrabModeSync, None, None); - - XGrabButton(dpy, Button3, MODKEY, c->win, False, BUTTONMASK, - GrabModeAsync, GrabModeSync, None, None); - XGrabButton(dpy, Button3, MODKEY | LockMask, c->win, False, BUTTONMASK, - GrabModeAsync, GrabModeSync, None, None); - XGrabButton(dpy, Button3, MODKEY | numlockmask, c->win, False, BUTTONMASK, - GrabModeAsync, GrabModeSync, None, None); - XGrabButton(dpy, Button3, MODKEY | numlockmask | LockMask, c->win, False, BUTTONMASK, - GrabModeAsync, GrabModeSync, None, None); - } - else - XGrabButton(dpy, AnyButton, AnyModifier, c->win, False, BUTTONMASK, - GrabModeAsync, GrabModeSync, None, None); -} - -static Bool -isprotodel(Client *c) { - int i, n; - Atom *protocols; - Bool ret = False; - - if(XGetWMProtocols(dpy, c->win, &protocols, &n)) { - for(i = 0; !ret && i < n; i++) - if(protocols[i] == wmatom[WMDelete]) - ret = True; - XFree(protocols); - } - return ret; -} - -static void -setclientstate(Client *c, long state) { - long data[] = {state, None}; - - XChangeProperty(dpy, c->win, wmatom[WMState], wmatom[WMState], 32, - PropModeReplace, (unsigned char *)data, 2); -} - -static int -xerrordummy(Display *dsply, XErrorEvent *ee) { - return 0; -} - -static void -ban(Client *c) { - if(c->isbanned) - return; - XMoveWindow(dpy, c->win, c->x + 2 * sw, c->y); - c->isbanned = True; -} - -static void -configure(Client *c) { - XConfigureEvent ce; - - ce.type = ConfigureNotify; - ce.display = dpy; - ce.event = c->win; - ce.window = c->win; - ce.x = c->x; - ce.y = c->y; - ce.width = c->w; - ce.height = c->h; - ce.border_width = c->border; - ce.above = None; - ce.override_redirect = False; - XSendEvent(dpy, c->win, False, StructureNotifyMask, (XEvent *)&ce); -} - -static void -killclient(const char *arg) { - XEvent ev; - - if(!sel) - return; - if(isprotodel(sel)) { - ev.type = ClientMessage; - ev.xclient.window = sel->win; - ev.xclient.message_type = wmatom[WMProtocols]; - ev.xclient.format = 32; - ev.xclient.data.l[0] = wmatom[WMDelete]; - ev.xclient.data.l[1] = CurrentTime; - XSendEvent(dpy, sel->win, False, NoEventMask, &ev); - } - else - XKillClient(dpy, sel->win); -} - -static void -manage(Window w, XWindowAttributes *wa) { - unsigned int i; - Client *c, *t = NULL; - Window trans; - Status rettrans; - XWindowChanges wc; - - c = emallocz(sizeof(Client)); - c->tags = emallocz(ntags * sizeof(Bool)); - c->win = w; - c->x = wa->x; - c->y = wa->y; - c->w = wa->width; - c->h = wa->height; - c->oldborder = wa->border_width; - if(c->w == sw && c->h == sh) { - c->x = sx; - c->y = sy; - c->border = wa->border_width; - } - else { - if(c->x + c->w + 2 * c->border > wax + waw) - c->x = wax + waw - c->w - 2 * c->border; - if(c->y + c->h + 2 * c->border > way + wah) - c->y = way + wah - c->h - 2 * c->border; - if(c->x < wax) - c->x = wax; - if(c->y < way) - c->y = way; - c->border = BORDERPX; - } - wc.border_width = c->border; - XConfigureWindow(dpy, w, CWBorderWidth, &wc); - XSetWindowBorder(dpy, w, dc.norm[ColBorder]); - configure(c); /* propagates border_width, if size doesn't change */ - updatesizehints(c); - XSelectInput(dpy, w, - StructureNotifyMask | PropertyChangeMask | EnterWindowMask); - grabbuttons(c, False); - updatetitle(c); - if((rettrans = XGetTransientForHint(dpy, w, &trans) == Success)) - for(t = clients; t && t->win != trans; t = t->next); - if(t) - for(i = 0; i < ntags; i++) - c->tags[i] = t->tags[i]; - applyrules(c); - if(!c->isfloating) - c->isfloating = (rettrans == Success) || c->isfixed; - attach(c); - attachstack(c); - XMoveResizeWindow(dpy, c->win, c->x, c->y, c->w, c->h); /* some windows require this */ - ban(c); - XMapWindow(dpy, c->win); - setclientstate(c, NormalState); - arrange(); -} - -static void -resize(Client *c, int x, int y, int w, int h, Bool sizehints) { - double dx, dy, max, min, ratio; - XWindowChanges wc; - - if(sizehints) { - if(c->minay > 0 && c->maxay > 0 && (h - c->baseh) > 0 && (w - c->basew) > 0) { - dx = (double)(w - c->basew); - dy = (double)(h - c->baseh); - min = (double)(c->minax) / (double)(c->minay); - max = (double)(c->maxax) / (double)(c->maxay); - ratio = dx / dy; - if(max > 0 && min > 0 && ratio > 0) { - if(ratio < min) { - dy = (dx * min + dy) / (min * min + 1); - dx = dy * min; - w = (int)dx + c->basew; - h = (int)dy + c->baseh; - } - else if(ratio > max) { - dy = (dx * min + dy) / (max * max + 1); - dx = dy * min; - w = (int)dx + c->basew; - h = (int)dy + c->baseh; - } - } - } - if(c->minw && w < c->minw) - w = c->minw; - if(c->minh && h < c->minh) - h = c->minh; - if(c->maxw && w > c->maxw) - w = c->maxw; - if(c->maxh && h > c->maxh) - h = c->maxh; - if(c->incw) - w -= (w - c->basew) % c->incw; - if(c->inch) - h -= (h - c->baseh) % c->inch; - } - if(w <= 0 || h <= 0) - return; - /* offscreen appearance fixes */ - if(x > sw) - x = sw - w - 2 * c->border; - if(y > sh) - y = sh - h - 2 * c->border; - if(x + w + 2 * c->border < sx) - x = sx; - if(y + h + 2 * c->border < sy) - y = sy; - if(c->x != x || c->y != y || c->w != w || c->h != h) { - c->x = wc.x = x; - c->y = wc.y = y; - c->w = wc.width = w; - c->h = wc.height = h; - wc.border_width = c->border; - XConfigureWindow(dpy, c->win, CWX | CWY | CWWidth | CWHeight | CWBorderWidth, &wc); - configure(c); - XSync(dpy, False); - } -} - -static void -unban(Client *c) { - if(!c->isbanned) - return; - XMoveWindow(dpy, c->win, c->x, c->y); - c->isbanned = False; -} - -static void -unmanage(Client *c) { - XWindowChanges wc; - - wc.border_width = c->oldborder; - /* The server grab construct avoids race conditions. */ - XGrabServer(dpy); - XSetErrorHandler(xerrordummy); - XConfigureWindow(dpy, c->win, CWBorderWidth, &wc); /* restore border */ - detach(c); - detachstack(c); - if(sel == c) - focus(NULL); - XUngrabButton(dpy, AnyButton, AnyModifier, c->win); - setclientstate(c, WithdrawnState); - free(c->tags); - free(c); - XSync(dpy, False); - XSetErrorHandler(xerror); - XUngrabServer(dpy); - arrange(); -} - -static void -updatesizehints(Client *c) { - long msize; - XSizeHints size; - - if(!XGetWMNormalHints(dpy, c->win, &size, &msize) || !size.flags) - size.flags = PSize; - c->flags = size.flags; - if(c->flags & PBaseSize) { - c->basew = size.base_width; - c->baseh = size.base_height; - } - else if(c->flags & PMinSize) { - c->basew = size.min_width; - c->baseh = size.min_height; - } - else - c->basew = c->baseh = 0; - if(c->flags & PResizeInc) { - c->incw = size.width_inc; - c->inch = size.height_inc; - } - else - c->incw = c->inch = 0; - if(c->flags & PMaxSize) { - c->maxw = size.max_width; - c->maxh = size.max_height; - } - else - c->maxw = c->maxh = 0; - if(c->flags & PMinSize) { - c->minw = size.min_width; - c->minh = size.min_height; - } - else if(c->flags & PBaseSize) { - c->minw = size.base_width; - c->minh = size.base_height; - } - else - c->minw = c->minh = 0; - if(c->flags & PAspect) { - c->minax = size.min_aspect.x; - c->maxax = size.max_aspect.x; - c->minay = size.min_aspect.y; - c->maxay = size.max_aspect.y; - } - else - c->minax = c->maxax = c->minay = c->maxay = 0; - c->isfixed = (c->maxw && c->minw && c->maxh && c->minh - && c->maxw == c->minw && c->maxh == c->minh); -} - -static void -updatetitle(Client *c) { - if(!gettextprop(c->win, netatom[NetWMName], c->name, sizeof c->name)) - gettextprop(c->win, wmatom[WMName], c->name, sizeof c->name); -} - -static Client * -getclient(Window w) { - Client *c; - - for(c = clients; c && c->win != w; c = c->next); - return c; -} - -static void -movemouse(Client *c) { - int x1, y1, ocx, ocy, di, nx, ny; - unsigned int dui; - Window dummy; - XEvent ev; - - ocx = nx = c->x; - ocy = ny = c->y; - if(XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync, - None, cursor[CurMove], CurrentTime) != GrabSuccess) - return; - c->ismax = False; - XQueryPointer(dpy, root, &dummy, &dummy, &x1, &y1, &di, &di, &dui); - for(;;) { - XMaskEvent(dpy, MOUSEMASK | ExposureMask | SubstructureRedirectMask, &ev); - switch (ev.type) { - case ButtonRelease: - XUngrabPointer(dpy, CurrentTime); - return; - case ConfigureRequest: - case Expose: - case MapRequest: - handler[ev.type](&ev); - break; - case MotionNotify: - XSync(dpy, False); - nx = ocx + (ev.xmotion.x - x1); - ny = ocy + (ev.xmotion.y - y1); - if(abs(wax + nx) < SNAP) - nx = wax; - else if(abs((wax + waw) - (nx + c->w + 2 * c->border)) < SNAP) - nx = wax + waw - c->w - 2 * c->border; - if(abs(way - ny) < SNAP) - ny = way; - else if(abs((way + wah) - (ny + c->h + 2 * c->border)) < SNAP) - ny = way + wah - c->h - 2 * c->border; - resize(c, nx, ny, c->w, c->h, False); - break; + nrules = sizeof rules / sizeof rules[0]; + regs = emallocz(nrules * sizeof(Regs)); + for(i = 0; i < nrules; i++) { + if(rules[i].prop) { + reg = emallocz(sizeof(regex_t)); + if(regcomp(reg, rules[i].prop, REG_EXTENDED)) + free(reg); + else + regs[i].propregex = reg; } - } -} - -static void -resizemouse(Client *c) { - int ocx, ocy; - int nw, nh; - XEvent ev; - - ocx = c->x; - ocy = c->y; - if(XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync, - None, cursor[CurResize], CurrentTime) != GrabSuccess) - return; - c->ismax = False; - XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w + c->border - 1, c->h + c->border - 1); - for(;;) { - XMaskEvent(dpy, MOUSEMASK | ExposureMask | SubstructureRedirectMask , &ev); - switch(ev.type) { - case ButtonRelease: - XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, - c->w + c->border - 1, c->h + c->border - 1); - XUngrabPointer(dpy, CurrentTime); - while(XCheckMaskEvent(dpy, EnterWindowMask, &ev)); - return; - case ConfigureRequest: - case Expose: - case MapRequest: - handler[ev.type](&ev); - break; - case MotionNotify: - XSync(dpy, False); - if((nw = ev.xmotion.x - ocx - 2 * c->border + 1) <= 0) - nw = 1; - if((nh = ev.xmotion.y - ocy - 2 * c->border + 1) <= 0) - nh = 1; - resize(c, c->x, c->y, nw, nh, True); - break; + if(rules[i].tags) { + reg = emallocz(sizeof(regex_t)); + if(regcomp(reg, rules[i].tags, REG_EXTENDED)) + free(reg); + else + regs[i].tagregex = reg; } } } static void -buttonpress(XEvent *e) { - unsigned int i, x; - Client *c; - XButtonPressedEvent *ev = &e->xbutton; +configure(Client *c) { + XConfigureEvent ce; - if(barwin == ev->window) { - x = 0; - for(i = 0; i < ntags; i++) { - x += textw(tags[i]); - if(ev->x < x) { - if(ev->button == Button1) { - if(ev->state & MODKEY) - tag(tags[i]); - else - view(tags[i]); - } - else if(ev->button == Button3) { - if(ev->state & MODKEY) - toggletag(tags[i]); - else - toggleview(tags[i]); - } - return; - } - } - if((ev->x < x + blw) && ev->button == Button1) - setlayout(NULL); - } - else if((c = getclient(ev->window))) { - focus(c); - if(CLEANMASK(ev->state) != MODKEY) - return; - if(ev->button == Button1 && (isfloating() || c->isfloating)) { - restack(); - movemouse(c); - } - else if(ev->button == Button2) - zoom(NULL); - else if(ev->button == Button3 - && (isfloating() || c->isfloating) && !c->isfixed) - { - restack(); - resizemouse(c); - } + ce.type = ConfigureNotify; + ce.display = dpy; + ce.event = c->win; + ce.window = c->win; + ce.x = c->x; + ce.y = c->y; + ce.width = c->w; + ce.height = c->h; + ce.border_width = c->border; + ce.above = None; + ce.override_redirect = False; + XSendEvent(dpy, c->win, False, StructureNotifyMask, (XEvent *)&ce); +} + +static void +configurenotify(XEvent *e) { + XConfigureEvent *ev = &e->xconfigure; + + if (ev->window == root && (ev->width != sw || ev->height != sh)) { + sw = ev->width; + sh = ev->height; + XFreePixmap(dpy, dc.drawable); + dc.drawable = XCreatePixmap(dpy, root, sw, bh, DefaultDepth(dpy, screen)); + XResizeWindow(dpy, barwin, sw, bh); + updatebarpos(); + arrange(); } } @@ -1044,21 +481,6 @@ configurerequest(XEvent *e) { XSync(dpy, False); } -static void -configurenotify(XEvent *e) { - XConfigureEvent *ev = &e->xconfigure; - - if (ev->window == root && (ev->width != sw || ev->height != sh)) { - sw = ev->width; - sh = ev->height; - XFreePixmap(dpy, dc.drawable); - dc.drawable = XCreatePixmap(dpy, root, sw, bh, DefaultDepth(dpy, screen)); - XResizeWindow(dpy, barwin, sw, bh); - updatebarpos(); - arrange(); - } -} - static void destroynotify(XEvent *e) { Client *c; @@ -1069,242 +491,406 @@ destroynotify(XEvent *e) { } static void -enternotify(XEvent *e) { - Client *c; - XCrossingEvent *ev = &e->xcrossing; +detach(Client *c) { + if(c->prev) + c->prev->next = c->next; + if(c->next) + c->next->prev = c->prev; + if(c == clients) + clients = c->next; + c->next = c->prev = NULL; +} - if(ev->mode != NotifyNormal || ev->detail == NotifyInferior) - return; - if((c = getclient(ev->window))) - focus(c); - else if(ev->window == root) { - selscreen = True; - focus(NULL); +static void +detachstack(Client *c) { + Client **tc; + + for(tc=&stack; *tc && *tc != c; tc=&(*tc)->snext); + *tc = c->snext; +} + +static void +drawbar(void) { + int i, x; + + dc.x = dc.y = 0; + for(i = 0; i < ntags; i++) { + dc.w = textw(tags[i]); + if(seltags[i]) { + drawtext(tags[i], dc.sel); + drawsquare(sel && sel->tags[i], isoccupied(i), dc.sel); + } + else { + drawtext(tags[i], dc.norm); + drawsquare(sel && sel->tags[i], isoccupied(i), dc.norm); + } + dc.x += dc.w; + } + dc.w = blw; + drawtext(layouts[ltidx].symbol, dc.norm); + x = dc.x + dc.w; + dc.w = textw(stext); + dc.x = sw - dc.w; + if(dc.x < x) { + dc.x = x; + dc.w = sw - x; + } + drawtext(stext, dc.norm); + if((dc.w = dc.x - x) > bh) { + dc.x = x; + if(sel) { + drawtext(sel->name, dc.sel); + drawsquare(sel->ismax, sel->isfloating, dc.sel); + } + else + drawtext(NULL, dc.norm); } + XCopyArea(dpy, dc.drawable, barwin, dc.gc, 0, 0, sw, bh, 0, 0); + XSync(dpy, False); } static void -expose(XEvent *e) { - XExposeEvent *ev = &e->xexpose; +drawsquare(Bool filled, Bool empty, unsigned long col[ColLast]) { + int x; + XGCValues gcv; + XRectangle r = { dc.x, dc.y, dc.w, dc.h }; - if(ev->count == 0) { - if(barwin == ev->window) - drawbar(); + gcv.foreground = col[ColFG]; + XChangeGC(dpy, dc.gc, GCForeground, &gcv); + x = (dc.font.ascent + dc.font.descent + 2) / 4; + r.x = dc.x + 1; + r.y = dc.y + 1; + if(filled) { + r.width = r.height = x + 1; + XFillRectangles(dpy, dc.drawable, dc.gc, &r, 1); + } + else if(empty) { + r.width = r.height = x; + XDrawRectangles(dpy, dc.drawable, dc.gc, &r, 1); } } static void -keypress(XEvent *e) { - KEYS - unsigned int len = sizeof keys / sizeof keys[0]; - unsigned int i; - KeyCode code; - KeySym keysym; - XKeyEvent *ev; +drawtext(const char *text, unsigned long col[ColLast]) { + int x, y, w, h; + static char buf[256]; + unsigned int len, olen; + XRectangle r = { dc.x, dc.y, dc.w, dc.h }; - 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); - } + XSetForeground(dpy, dc.gc, col[ColBG]); + XFillRectangles(dpy, dc.drawable, dc.gc, &r, 1); + if(!text) return; + w = 0; + olen = len = strlen(text); + if(len >= sizeof buf) + len = sizeof buf - 1; + memcpy(buf, text, len); + buf[len] = 0; + 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 */ + while(len && (w = textnw(buf, len)) > dc.w - h) + buf[--len] = 0; + if(len < olen) { + if(len > 1) + buf[len - 1] = '.'; + if(len > 2) + buf[len - 2] = '.'; + if(len > 3) + buf[len - 3] = '.'; + } + if(w > dc.w) + return; /* too long */ + XSetForeground(dpy, dc.gc, col[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); +} + +static void * +emallocz(unsigned int size) { + void *res = calloc(1, size); + + if(!res) + eprint("fatal: could not malloc() %u bytes\n", size); + return res; +} + +static void +enternotify(XEvent *e) { + Client *c; + XCrossingEvent *ev = &e->xcrossing; + + if(ev->mode != NotifyNormal || ev->detail == NotifyInferior) + return; + if((c = getclient(ev->window))) + focus(c); + else if(ev->window == root) { + selscreen = True; + focus(NULL); } - ev = &e->xkey; - keysym = XKeycodeToKeysym(dpy, (KeyCode)ev->keycode, 0); - for(i = 0; i < len; i++) - if(keysym == keys[i].keysym - && CLEANMASK(keys[i].mod) == CLEANMASK(ev->state)) - { - if(keys[i].func) - keys[i].func(keys[i].arg); - } } static void -leavenotify(XEvent *e) { - XCrossingEvent *ev = &e->xcrossing; +eprint(const char *errstr, ...) { + va_list ap; - if((ev->window == root) && !ev->same_screen) { - selscreen = False; - focus(NULL); - } + va_start(ap, errstr); + vfprintf(stderr, errstr, ap); + va_end(ap); + exit(EXIT_FAILURE); } static void -mappingnotify(XEvent *e) { - XMappingEvent *ev = &e->xmapping; +expose(XEvent *e) { + XExposeEvent *ev = &e->xexpose; - XRefreshKeyboardMapping(ev); - if(ev->request == MappingKeyboard) - keypress(NULL); + if(ev->count == 0) { + if(barwin == ev->window) + drawbar(); + } } static void -maprequest(XEvent *e) { - static XWindowAttributes wa; - XMapRequestEvent *ev = &e->xmaprequest; +floating(void) { /* default floating layout */ + Client *c; - if(!XGetWindowAttributes(dpy, ev->window, &wa)) - return; - if(wa.override_redirect) + for(c = clients; c; c = c->next) + if(isvisible(c)) + resize(c, c->x, c->y, c->w, c->h, True); +} + +static void +focus(Client *c) { + if((!c && selscreen) || (c && !isvisible(c))) + for(c = stack; c && !isvisible(c); c = c->snext); + if(sel && sel != c) { + grabbuttons(sel, False); + XSetWindowBorder(dpy, sel->win, dc.norm[ColBorder]); + } + if(c) { + detachstack(c); + attachstack(c); + grabbuttons(c, True); + } + sel = c; + drawbar(); + if(!selscreen) return; - if(!getclient(ev->window)) - manage(ev->window, &wa); + if(c) { + XSetWindowBorder(dpy, c->win, dc.sel[ColBorder]); + XSetInputFocus(dpy, c->win, RevertToPointerRoot, CurrentTime); + } + else + XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime); } static void -propertynotify(XEvent *e) { +focusnext(const char *arg) { Client *c; - Window trans; - XPropertyEvent *ev = &e->xproperty; - if(ev->state == PropertyDelete) - return; /* ignore */ - if((c = getclient(ev->window))) { - switch (ev->atom) { - default: break; - case XA_WM_TRANSIENT_FOR: - XGetTransientForHint(dpy, c->win, &trans); - if(!c->isfloating && (c->isfloating = (getclient(trans) != NULL))) - arrange(); - break; - case XA_WM_NORMAL_HINTS: - updatesizehints(c); - break; - } - if(ev->atom == XA_WM_NAME || ev->atom == netatom[NetWMName]) { - updatetitle(c); - if(c == sel) - drawbar(); - } + if(!sel) + return; + for(c = sel->next; c && !isvisible(c); c = c->next); + if(!c) + for(c = clients; c && !isvisible(c); c = c->next); + if(c) { + focus(c); + restack(); } } static void -unmapnotify(XEvent *e) { +focusprev(const char *arg) { Client *c; - XUnmapEvent *ev = &e->xunmap; - if((c = getclient(ev->window))) - unmanage(c); + if(!sel) + return; + for(c = sel->prev; c && !isvisible(c); c = c->prev); + if(!c) { + for(c = clients; c && c->next; c = c->next); + for(; c && !isvisible(c); c = c->prev); + } + if(c) { + focus(c); + restack(); + } } -static unsigned int -idxoftag(const char *tag) { - unsigned int i; +static Client * +getclient(Window w) { + Client *c; - for(i = 0; i < ntags; i++) - if(tags[i] == tag) - return i; - return 0; + for(c = clients; c && c->win != w; c = c->next); + return c; } -static void -floating(void) { /* default floating layout */ - Client *c; +static long +getstate(Window w) { + int format, status; + long result = -1; + unsigned char *p = NULL; + unsigned long n, extra; + Atom real; - for(c = clients; c; c = c->next) - if(isvisible(c)) - resize(c, c->x, c->y, c->w, c->h, True); + status = XGetWindowProperty(dpy, w, wmatom[WMState], 0L, 2L, False, wmatom[WMState], + &real, &format, &n, &extra, (unsigned char **)&p); + if(status != Success) + return -1; + if(n != 0) + result = *p; + XFree(p); + return result; } -static void -applyrules(Client *c) { - static char buf[512]; - unsigned int i, j; - regmatch_t tmp; - Bool matched = False; - XClassHint ch = { 0 }; +static Bool +gettextprop(Window w, Atom atom, char *text, unsigned int size) { + char **list = NULL; + int n; + XTextProperty name; - /* rule matching */ - XGetClassHint(dpy, c->win, &ch); - 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++) - 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++) { - if(!regexec(regs[i].tagregex, tags[j], 1, &tmp, 0)) { - matched = True; - c->tags[j] = True; - } - } + if(!text || size == 0) + return False; + text[0] = '\0'; + XGetTextProperty(dpy, w, &name, atom); + if(!name.nitems) + return False; + if(name.encoding == XA_STRING) + strncpy(text, (char *)name.value, size - 1); + else { + if(XmbTextPropertyToTextList(dpy, &name, &list, &n) >= Success + && n > 0 && *list) + { + strncpy(text, *list, size - 1); + XFreeStringList(list); } - if(ch.res_class) - XFree(ch.res_class); - if(ch.res_name) - XFree(ch.res_name); - if(!matched) - for(i = 0; i < ntags; i++) - c->tags[i] = seltags[i]; + } + text[size - 1] = '\0'; + XFree(name.value); + return True; } static void -compileregs(void) { - unsigned int i; - regex_t *reg; +grabbuttons(Client *c, Bool focused) { + XUngrabButton(dpy, AnyButton, AnyModifier, c->win); - if(regs) - return; - nrules = sizeof rules / sizeof rules[0]; - regs = emallocz(nrules * sizeof(Regs)); - for(i = 0; i < nrules; i++) { - if(rules[i].prop) { - reg = emallocz(sizeof(regex_t)); - if(regcomp(reg, rules[i].prop, REG_EXTENDED)) - free(reg); - else - regs[i].propregex = reg; - } - if(rules[i].tags) { - reg = emallocz(sizeof(regex_t)); - if(regcomp(reg, rules[i].tags, REG_EXTENDED)) - free(reg); - else - regs[i].tagregex = reg; - } + if(focused) { + XGrabButton(dpy, Button1, MODKEY, c->win, False, BUTTONMASK, + GrabModeAsync, GrabModeSync, None, None); + XGrabButton(dpy, Button1, MODKEY | LockMask, c->win, False, BUTTONMASK, + GrabModeAsync, GrabModeSync, None, None); + XGrabButton(dpy, Button1, MODKEY | numlockmask, c->win, False, BUTTONMASK, + GrabModeAsync, GrabModeSync, None, None); + XGrabButton(dpy, Button1, MODKEY | numlockmask | LockMask, c->win, False, BUTTONMASK, + GrabModeAsync, GrabModeSync, None, None); + + XGrabButton(dpy, Button2, MODKEY, c->win, False, BUTTONMASK, + GrabModeAsync, GrabModeSync, None, None); + XGrabButton(dpy, Button2, MODKEY | LockMask, c->win, False, BUTTONMASK, + GrabModeAsync, GrabModeSync, None, None); + XGrabButton(dpy, Button2, MODKEY | numlockmask, c->win, False, BUTTONMASK, + GrabModeAsync, GrabModeSync, None, None); + XGrabButton(dpy, Button2, MODKEY | numlockmask | LockMask, c->win, False, BUTTONMASK, + GrabModeAsync, GrabModeSync, None, None); + + XGrabButton(dpy, Button3, MODKEY, c->win, False, BUTTONMASK, + GrabModeAsync, GrabModeSync, None, None); + XGrabButton(dpy, Button3, MODKEY | LockMask, c->win, False, BUTTONMASK, + GrabModeAsync, GrabModeSync, None, None); + XGrabButton(dpy, Button3, MODKEY | numlockmask, c->win, False, BUTTONMASK, + GrabModeAsync, GrabModeSync, None, None); + XGrabButton(dpy, Button3, MODKEY | numlockmask | LockMask, c->win, False, BUTTONMASK, + GrabModeAsync, GrabModeSync, None, None); } + else + XGrabButton(dpy, AnyButton, AnyModifier, c->win, False, BUTTONMASK, + GrabModeAsync, GrabModeSync, None, None); +} + +static unsigned int +idxoftag(const char *tag) { + unsigned int i; + + for(i = 0; i < ntags; i++) + if(tags[i] == tag) + return i; + return 0; } static void -focusnext(const char *arg) { - Client *c; +initbar(void) { + XSetWindowAttributes wa; - if(!sel) - return; - for(c = sel->next; c && !isvisible(c); c = c->next); - if(!c) - for(c = clients; c && !isvisible(c); c = c->next); - if(c) { - focus(c); - restack(); - } + wa.override_redirect = 1; + wa.background_pixmap = ParentRelative; + wa.event_mask = ButtonPressMask | ExposureMask; + barwin = XCreateWindow(dpy, root, sx, sy, sw, bh, 0, + DefaultDepth(dpy, screen), CopyFromParent, DefaultVisual(dpy, screen), + CWOverrideRedirect | CWBackPixmap | CWEventMask, &wa); + XDefineCursor(dpy, barwin, cursor[CurNormal]); + updatebarpos(); + XMapRaised(dpy, barwin); + strcpy(stext, "dwm-"VERSION); + dc.drawable = XCreatePixmap(dpy, root, sw, bh, DefaultDepth(dpy, screen)); + dc.gc = XCreateGC(dpy, root, 0, 0); + XSetLineAttributes(dpy, dc.gc, 1, LineSolid, CapButt, JoinMiter); + if(!dc.font.set) + XSetFont(dpy, dc.gc, dc.font.xfont->fid); +} + +static unsigned long +initcolor(const char *colstr) { + Colormap cmap = DefaultColormap(dpy, screen); + XColor color; + + if(!XAllocNamedColor(dpy, cmap, colstr, &color, &color)) + eprint("error, cannot allocate color '%s'\n", colstr); + return color.pixel; } static void -focusprev(const char *arg) { - Client *c; +initfont(const char *fontstr) { + char *def, **missing; + int i, n; - if(!sel) - return; - for(c = sel->prev; c && !isvisible(c); c = c->prev); - if(!c) { - for(c = clients; c && c->next; c = c->next); - for(; c && !isvisible(c); c = c->prev); + missing = NULL; + if(dc.font.set) + XFreeFontSet(dpy, dc.font.set); + 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(c) { - focus(c); - restack(); + if(dc.font.set) { + XFontSetExtents *font_extents; + XFontStruct **xfonts; + char **font_names; + dc.font.ascent = dc.font.descent = 0; + font_extents = XExtentsOfFontSet(dc.font.set); + n = XFontsOfFontSet(dc.font.set, &xfonts, &font_names); + for(i = 0, dc.font.ascent = 0, dc.font.descent = 0; i < n; i++) { + if(dc.font.ascent < (*xfonts)->ascent) + dc.font.ascent = (*xfonts)->ascent; + if(dc.font.descent < (*xfonts)->descent) + dc.font.descent = (*xfonts)->descent; + xfonts++; + } + } + else { + if(dc.font.xfont) + XFreeFont(dpy, dc.font.xfont); + dc.font.xfont = NULL; + if(!(dc.font.xfont = XLoadQueryFont(dpy, fontstr)) + || !(dc.font.xfont = XLoadQueryFont(dpy, "fixed"))) + eprint("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; } static void @@ -1319,11 +905,54 @@ initlayouts(void) { } } +static void +initstyle(void) { + dc.norm[ColBorder] = initcolor(NORMBORDERCOLOR); + dc.norm[ColBG] = initcolor(NORMBGCOLOR); + dc.norm[ColFG] = initcolor(NORMFGCOLOR); + dc.sel[ColBorder] = initcolor(SELBORDERCOLOR); + dc.sel[ColBG] = initcolor(SELBGCOLOR); + dc.sel[ColFG] = initcolor(SELFGCOLOR); + initfont(FONT); + dc.h = bh = dc.font.height + 2; +} + +static Bool +isarrange(void (*func)()) +{ + return func == layouts[ltidx].arrange; +} + static Bool isfloating(void) { return layouts[ltidx].arrange == floating; } +static Bool +isoccupied(unsigned int t) { + Client *c; + + for(c = clients; c; c = c->next) + if(c->tags[t]) + return True; + return False; +} + +static Bool +isprotodel(Client *c) { + int i, n; + Atom *protocols; + Bool ret = False; + + if(XGetWMProtocols(dpy, c->win, &protocols, &n)) { + for(i = 0; !ret && i < n; i++) + if(protocols[i] == wmatom[WMDelete]) + ret = True; + XFree(protocols); + } + return ret; +} + static Bool isvisible(Client *c) { unsigned int i; @@ -1335,176 +964,360 @@ isvisible(Client *c) { } static void -restack(void) { - Client *c; +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++) + if(keysym == keys[i].keysym + && CLEANMASK(keys[i].mod) == CLEANMASK(ev->state)) + { + if(keys[i].func) + keys[i].func(keys[i].arg); + } +} + +static void +killclient(const char *arg) { XEvent ev; - XWindowChanges wc; - drawbar(); if(!sel) return; - if(sel->isfloating || isfloating()) - XRaiseWindow(dpy, sel->win); - if(!isfloating()) { - wc.stack_mode = Below; - wc.sibling = barwin; - if(!sel->isfloating) { - XConfigureWindow(dpy, sel->win, CWSibling | CWStackMode, &wc); - wc.sibling = sel->win; - } - for(c = nexttiled(clients); c; c = nexttiled(c->next)) { - if(c == sel) - continue; - XConfigureWindow(dpy, c->win, CWSibling | CWStackMode, &wc); - wc.sibling = c->win; - } + if(isprotodel(sel)) { + ev.type = ClientMessage; + ev.xclient.window = sel->win; + ev.xclient.message_type = wmatom[WMProtocols]; + ev.xclient.format = 32; + ev.xclient.data.l[0] = wmatom[WMDelete]; + ev.xclient.data.l[1] = CurrentTime; + XSendEvent(dpy, sel->win, False, NoEventMask, &ev); } - XSync(dpy, False); - while(XCheckMaskEvent(dpy, EnterWindowMask, &ev)); + else + XKillClient(dpy, sel->win); } static void -setlayout(const char *arg) { +leavenotify(XEvent *e) { + XCrossingEvent *ev = &e->xcrossing; + + if((ev->window == root) && !ev->same_screen) { + selscreen = False; + focus(NULL); + } +} + +static void +manage(Window w, XWindowAttributes *wa) { unsigned int i; + Client *c, *t = NULL; + Window trans; + Status rettrans; + XWindowChanges wc; - if(!arg) { - if(++ltidx == nlayouts) - ltidx = 0;; + c = emallocz(sizeof(Client)); + c->tags = emallocz(ntags * sizeof(Bool)); + c->win = w; + c->x = wa->x; + c->y = wa->y; + c->w = wa->width; + c->h = wa->height; + c->oldborder = wa->border_width; + if(c->w == sw && c->h == sh) { + c->x = sx; + c->y = sy; + c->border = wa->border_width; } else { - for(i = 0; i < nlayouts; i++) - if(!strcmp(arg, layouts[i].symbol)) - break; - if(i == nlayouts) - return; - ltidx = i; + if(c->x + c->w + 2 * c->border > wax + waw) + c->x = wax + waw - c->w - 2 * c->border; + if(c->y + c->h + 2 * c->border > way + wah) + c->y = way + wah - c->h - 2 * c->border; + if(c->x < wax) + c->x = wax; + if(c->y < way) + c->y = way; + c->border = BORDERPX; } - if(sel) - arrange(); - else - drawbar(); + wc.border_width = c->border; + XConfigureWindow(dpy, w, CWBorderWidth, &wc); + XSetWindowBorder(dpy, w, dc.norm[ColBorder]); + configure(c); /* propagates border_width, if size doesn't change */ + updatesizehints(c); + XSelectInput(dpy, w, + StructureNotifyMask | PropertyChangeMask | EnterWindowMask); + grabbuttons(c, False); + updatetitle(c); + if((rettrans = XGetTransientForHint(dpy, w, &trans) == Success)) + for(t = clients; t && t->win != trans; t = t->next); + if(t) + for(i = 0; i < ntags; i++) + c->tags[i] = t->tags[i]; + applyrules(c); + if(!c->isfloating) + c->isfloating = (rettrans == Success) || c->isfixed; + attach(c); + attachstack(c); + XMoveResizeWindow(dpy, c->win, c->x, c->y, c->w, c->h); /* some windows require this */ + ban(c); + XMapWindow(dpy, c->win); + setclientstate(c, NormalState); + arrange(); } static void -tag(const char *arg) { - unsigned int i; +mappingnotify(XEvent *e) { + XMappingEvent *ev = &e->xmapping; - if(!sel) - return; - for(i = 0; i < ntags; i++) - sel->tags[i] = arg == NULL; - i = idxoftag(arg); - if(i >= 0 && i < ntags) - sel->tags[i] = True; - arrange(); + XRefreshKeyboardMapping(ev); + if(ev->request == MappingKeyboard) + keypress(NULL); } static void -togglefloating(const char *arg) { - if(!sel) +maprequest(XEvent *e) { + static XWindowAttributes wa; + XMapRequestEvent *ev = &e->xmaprequest; + + if(!XGetWindowAttributes(dpy, ev->window, &wa)) return; - sel->isfloating = !sel->isfloating; - if(sel->isfloating) - resize(sel, sel->x, sel->y, sel->w, sel->h, True); - arrange(); + if(wa.override_redirect) + return; + if(!getclient(ev->window)) + manage(ev->window, &wa); } static void -togglemax(const char *arg) { +movemouse(Client *c) { + int x1, y1, ocx, ocy, di, nx, ny; + unsigned int dui; + Window dummy; XEvent ev; - if(!sel || (!isfloating() && !sel->isfloating) || sel->isfixed) + ocx = nx = c->x; + ocy = ny = c->y; + if(XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync, + None, cursor[CurMove], CurrentTime) != GrabSuccess) return; - if((sel->ismax = !sel->ismax)) { - sel->rx = sel->x; - sel->ry = sel->y; - sel->rw = sel->w; - sel->rh = sel->h; - resize(sel, wax, way, waw - 2 * sel->border, wah - 2 * sel->border, True); + c->ismax = False; + XQueryPointer(dpy, root, &dummy, &dummy, &x1, &y1, &di, &di, &dui); + for(;;) { + XMaskEvent(dpy, MOUSEMASK | ExposureMask | SubstructureRedirectMask, &ev); + switch (ev.type) { + case ButtonRelease: + XUngrabPointer(dpy, CurrentTime); + return; + case ConfigureRequest: + case Expose: + case MapRequest: + handler[ev.type](&ev); + break; + case MotionNotify: + XSync(dpy, False); + nx = ocx + (ev.xmotion.x - x1); + ny = ocy + (ev.xmotion.y - y1); + if(abs(wax + nx) < SNAP) + nx = wax; + else if(abs((wax + waw) - (nx + c->w + 2 * c->border)) < SNAP) + nx = wax + waw - c->w - 2 * c->border; + if(abs(way - ny) < SNAP) + ny = way; + else if(abs((way + wah) - (ny + c->h + 2 * c->border)) < SNAP) + ny = way + wah - c->h - 2 * c->border; + resize(c, nx, ny, c->w, c->h, False); + break; + } } - else - resize(sel, sel->rx, sel->ry, sel->rw, sel->rh, True); - drawbar(); - while(XCheckMaskEvent(dpy, EnterWindowMask, &ev)); +} + +static Client * +nexttiled(Client *c) { + for(; c && (c->isfloating || !isvisible(c)); c = c->next); + return c; } static void -toggletag(const char *arg) { - unsigned int i, j; +propertynotify(XEvent *e) { + Client *c; + Window trans; + XPropertyEvent *ev = &e->xproperty; - if(!sel) - return; - i = idxoftag(arg); - sel->tags[i] = !sel->tags[i]; - for(j = 0; j < ntags && !sel->tags[j]; j++); - if(j == ntags) - sel->tags[i] = True; - arrange(); + if(ev->state == PropertyDelete) + return; /* ignore */ + if((c = getclient(ev->window))) { + switch (ev->atom) { + default: break; + case XA_WM_TRANSIENT_FOR: + XGetTransientForHint(dpy, c->win, &trans); + if(!c->isfloating && (c->isfloating = (getclient(trans) != NULL))) + arrange(); + break; + case XA_WM_NORMAL_HINTS: + updatesizehints(c); + break; + } + if(ev->atom == XA_WM_NAME || ev->atom == netatom[NetWMName]) { + updatetitle(c); + if(c == sel) + drawbar(); + } + } } static void -toggleview(const char *arg) { - unsigned int i, j; - - i = idxoftag(arg); - seltags[i] = !seltags[i]; - for(j = 0; j < ntags && !seltags[j]; j++); - if(j == ntags) - seltags[i] = True; /* cannot toggle last view */ - arrange(); +quit(const char *arg) { + readin = running = False; } static void -view(const char *arg) { - unsigned int i; +resize(Client *c, int x, int y, int w, int h, Bool sizehints) { + double dx, dy, max, min, ratio; + XWindowChanges wc; - for(i = 0; i < ntags; i++) - seltags[i] = arg == NULL; - i = idxoftag(arg); - if(i >= 0 && i < ntags) - seltags[i] = True; - arrange(); + if(sizehints) { + if(c->minay > 0 && c->maxay > 0 && (h - c->baseh) > 0 && (w - c->basew) > 0) { + dx = (double)(w - c->basew); + dy = (double)(h - c->baseh); + min = (double)(c->minax) / (double)(c->minay); + max = (double)(c->maxax) / (double)(c->maxay); + ratio = dx / dy; + if(max > 0 && min > 0 && ratio > 0) { + if(ratio < min) { + dy = (dx * min + dy) / (min * min + 1); + dx = dy * min; + w = (int)dx + c->basew; + h = (int)dy + c->baseh; + } + else if(ratio > max) { + dy = (dx * min + dy) / (max * max + 1); + dx = dy * min; + w = (int)dx + c->basew; + h = (int)dy + c->baseh; + } + } + } + if(c->minw && w < c->minw) + w = c->minw; + if(c->minh && h < c->minh) + h = c->minh; + if(c->maxw && w > c->maxw) + w = c->maxw; + if(c->maxh && h > c->maxh) + h = c->maxh; + if(c->incw) + w -= (w - c->basew) % c->incw; + if(c->inch) + h -= (h - c->baseh) % c->inch; + } + if(w <= 0 || h <= 0) + return; + /* offscreen appearance fixes */ + if(x > sw) + x = sw - w - 2 * c->border; + if(y > sh) + y = sh - h - 2 * c->border; + if(x + w + 2 * c->border < sx) + x = sx; + if(y + h + 2 * c->border < sy) + y = sy; + if(c->x != x || c->y != y || c->w != w || c->h != h) { + c->x = wc.x = x; + c->y = wc.y = y; + c->w = wc.width = w; + c->h = wc.height = h; + wc.border_width = c->border; + XConfigureWindow(dpy, c->win, CWX | CWY | CWWidth | CWHeight | CWBorderWidth, &wc); + configure(c); + XSync(dpy, False); + } } static void -cleanup(void) { - close(STDIN_FILENO); - while(stack) { - unban(stack); - unmanage(stack); - } - 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); - XDestroyWindow(dpy, barwin); - XFreeCursor(dpy, cursor[CurNormal]); - XFreeCursor(dpy, cursor[CurResize]); - XFreeCursor(dpy, cursor[CurMove]); - XSetInputFocus(dpy, PointerRoot, RevertToPointerRoot, CurrentTime); - XSync(dpy, False); - free(seltags); +resizemouse(Client *c) { + int ocx, ocy; + int nw, nh; + XEvent ev; + + ocx = c->x; + ocy = c->y; + if(XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync, + None, cursor[CurResize], CurrentTime) != GrabSuccess) + return; + c->ismax = False; + XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w + c->border - 1, c->h + c->border - 1); + for(;;) { + XMaskEvent(dpy, MOUSEMASK | ExposureMask | SubstructureRedirectMask , &ev); + switch(ev.type) { + case ButtonRelease: + XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, + c->w + c->border - 1, c->h + c->border - 1); + XUngrabPointer(dpy, CurrentTime); + while(XCheckMaskEvent(dpy, EnterWindowMask, &ev)); + return; + case ConfigureRequest: + case Expose: + case MapRequest: + handler[ev.type](&ev); + break; + case MotionNotify: + XSync(dpy, False); + if((nw = ev.xmotion.x - ocx - 2 * c->border + 1) <= 0) + nw = 1; + if((nh = ev.xmotion.y - ocy - 2 * c->border + 1) <= 0) + nh = 1; + resize(c, c->x, c->y, nw, nh, True); + break; + } + } } -static long -getstate(Window w) { - int format, status; - long result = -1; - unsigned char *p = NULL; - unsigned long n, extra; - Atom real; +static void +restack(void) { + Client *c; + XEvent ev; + XWindowChanges wc; - status = XGetWindowProperty(dpy, w, wmatom[WMState], 0L, 2L, False, wmatom[WMState], - &real, &format, &n, &extra, (unsigned char **)&p); - if(status != Success) - return -1; - if(n != 0) - result = *p; - XFree(p); - return result; + drawbar(); + if(!sel) + return; + if(sel->isfloating || isfloating()) + XRaiseWindow(dpy, sel->win); + if(!isfloating()) { + wc.stack_mode = Below; + wc.sibling = barwin; + if(!sel->isfloating) { + XConfigureWindow(dpy, sel->win, CWSibling | CWStackMode, &wc); + wc.sibling = sel->win; + } + for(c = nexttiled(clients); c; c = nexttiled(c->next)) { + if(c == sel) + continue; + XConfigureWindow(dpy, c->win, CWSibling | CWStackMode, &wc); + wc.sibling = c->win; + } + } + XSync(dpy, False); + while(XCheckMaskEvent(dpy, EnterWindowMask, &ev)); } static void @@ -1534,6 +1347,58 @@ scan(void) { XFree(wins); } +static void +setclientstate(Client *c, long state) { + long data[] = {state, None}; + + XChangeProperty(dpy, c->win, wmatom[WMState], wmatom[WMState], 32, + PropModeReplace, (unsigned char *)data, 2); +} + +static void +setlayout(const char *arg) { + unsigned int i; + + if(!arg) { + if(++ltidx == nlayouts) + ltidx = 0;; + } + else { + for(i = 0; i < nlayouts; i++) + if(!strcmp(arg, layouts[i].symbol)) + break; + if(i == nlayouts) + return; + ltidx = i; + } + if(sel) + arrange(); + else + drawbar(); +} + +static void +setmwfact(const char *arg) { + double delta; + + if(!isarrange(tile)) + return; + /* arg handling, manipulate mwfact */ + if(arg == NULL) + mwfact = MWFACT; + else if(1 == sscanf(arg, "%lf", &delta)) { + if(arg[0] != '+' && arg[0] != '-') + mwfact = delta; + else + mwfact += delta; + if(mwfact < 0.1) + mwfact = 0.1; + else if(mwfact > 0.9) + mwfact = 0.9; + } + arrange(); +} + static void setup(void) { int i, j; @@ -1586,158 +1451,58 @@ setup(void) { selscreen = XQueryPointer(dpy, root, &w, &w, &i, &i, &i, &i, &mask); } -/* - * Startup Error handler to check if another window manager - * is already running. - */ -static int -xerrorstart(Display *dsply, XErrorEvent *ee) { - otherwm = True; - return -1; -} - -static Bool -gettextprop(Window w, Atom atom, char *text, unsigned int size) { - char **list = NULL; - int n; - XTextProperty name; +static void +spawn(const char *arg) { + static char *shell = NULL; - if(!text || size == 0) - return False; - text[0] = '\0'; - XGetTextProperty(dpy, w, &name, atom); - if(!name.nitems) - return False; - if(name.encoding == XA_STRING) - strncpy(text, (char *)name.value, size - 1); - else { - if(XmbTextPropertyToTextList(dpy, &name, &list, &n) >= Success - && n > 0 && *list) - { - strncpy(text, *list, size - 1); - XFreeStringList(list); + if(!shell && !(shell = getenv("SHELL"))) + shell = "/bin/sh"; + if(!arg) + return; + /* The double-fork construct avoids zombie processes and keeps the code + * clean from stupid signal handlers. */ + if(fork() == 0) { + if(fork() == 0) { + if(dpy) + close(ConnectionNumber(dpy)); + setsid(); + execl(shell, shell, "-c", arg, (char *)NULL); + fprintf(stderr, "dwm: execl '%s -c %s'", shell, arg); + perror(" failed"); } + exit(0); } - text[size - 1] = '\0'; - XFree(name.value); - return True; -} - -static void -quit(const char *arg) { - readin = running = False; -} - -/* There's no way to check accesses to destroyed windows, thus those cases are - * ignored (especially on UnmapNotify's). Other types of errors call Xlibs - * default error handler, which may call exit. - */ -static int -xerror(Display *dpy, XErrorEvent *ee) { - if(ee->error_code == BadWindow - || (ee->request_code == X_SetInputFocus && ee->error_code == BadMatch) - || (ee->request_code == X_PolyText8 && ee->error_code == BadDrawable) - || (ee->request_code == X_PolyFillRectangle && ee->error_code == BadDrawable) - || (ee->request_code == X_PolySegment && ee->error_code == BadDrawable) - || (ee->request_code == X_ConfigureWindow && ee->error_code == BadMatch) - || (ee->request_code == X_GrabKey && ee->error_code == BadAccess) - || (ee->request_code == X_CopyArea && ee->error_code == BadDrawable)) - return 0; - fprintf(stderr, "dwm: fatal error: request code=%d, error code=%d\n", - ee->request_code, ee->error_code); - return xerrorxlib(dpy, ee); /* may call exit */ -} - -static void -arrange(void) { - Client *c; - - for(c = clients; c; c = c->next) - if(isvisible(c)) - unban(c); - else - ban(c); - layouts[ltidx].arrange(); - focus(NULL); - restack(); -} - -static void -attach(Client *c) { - if(clients) - clients->prev = c; - c->next = clients; - clients = c; + wait(0); } static void -detach(Client *c) { - if(c->prev) - c->prev->next = c->next; - if(c->next) - c->next->prev = c->prev; - if(c == clients) - clients = c->next; - c->next = c->prev = NULL; -} +tag(const char *arg) { + unsigned int i; -static void -focus(Client *c) { - if((!c && selscreen) || (c && !isvisible(c))) - for(c = stack; c && !isvisible(c); c = c->snext); - if(sel && sel != c) { - grabbuttons(sel, False); - XSetWindowBorder(dpy, sel->win, dc.norm[ColBorder]); - } - if(c) { - detachstack(c); - attachstack(c); - grabbuttons(c, True); - } - sel = c; - drawbar(); - if(!selscreen) + if(!sel) return; - if(c) { - XSetWindowBorder(dpy, c->win, dc.sel[ColBorder]); - XSetInputFocus(dpy, c->win, RevertToPointerRoot, CurrentTime); - } - else - XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime); -} - -static Bool -isarrange(void (*func)()) -{ - return func == layouts[ltidx].arrange; -} - -static Client * -nexttiled(Client *c) { - for(; c && (c->isfloating || !isvisible(c)); c = c->next); - return c; + for(i = 0; i < ntags; i++) + sel->tags[i] = arg == NULL; + i = idxoftag(arg); + if(i >= 0 && i < ntags) + sel->tags[i] = True; + arrange(); } -static void -setmwfact(const char *arg) { - double delta; +static unsigned int +textnw(const char *text, unsigned int len) { + XRectangle r; - if(!isarrange(tile)) - return; - /* arg handling, manipulate mwfact */ - if(arg == NULL) - mwfact = MWFACT; - else if(1 == sscanf(arg, "%lf", &delta)) { - if(arg[0] != '+' && arg[0] != '-') - mwfact = delta; - else - mwfact += delta; - if(mwfact < 0.1) - mwfact = 0.1; - else if(mwfact > 0.9) - mwfact = 0.9; + if(dc.font.set) { + XmbTextExtents(dc.font.set, text, len, NULL, &r); + return r.width; } - arrange(); + return XTextWidth(dc.font.xfont, text, len); +} + +static unsigned int +textw(const char *text) { + return textnw(text, strlen(text)) + dc.font.height; } static void @@ -1779,6 +1544,239 @@ tile(void) { } } +static void +togglebar(const char *arg) { + if(bpos == BarOff) + bpos = (BARPOS == BarOff) ? BarTop : BARPOS; + else + bpos = BarOff; + updatebarpos(); + arrange(); +} + +static void +togglefloating(const char *arg) { + if(!sel) + return; + sel->isfloating = !sel->isfloating; + if(sel->isfloating) + resize(sel, sel->x, sel->y, sel->w, sel->h, True); + arrange(); +} + +static void +togglemax(const char *arg) { + XEvent ev; + + if(!sel || (!isfloating() && !sel->isfloating) || sel->isfixed) + return; + if((sel->ismax = !sel->ismax)) { + sel->rx = sel->x; + sel->ry = sel->y; + sel->rw = sel->w; + sel->rh = sel->h; + resize(sel, wax, way, waw - 2 * sel->border, wah - 2 * sel->border, True); + } + else + resize(sel, sel->rx, sel->ry, sel->rw, sel->rh, True); + drawbar(); + while(XCheckMaskEvent(dpy, EnterWindowMask, &ev)); +} + +static void +toggletag(const char *arg) { + unsigned int i, j; + + if(!sel) + return; + i = idxoftag(arg); + sel->tags[i] = !sel->tags[i]; + for(j = 0; j < ntags && !sel->tags[j]; j++); + if(j == ntags) + sel->tags[i] = True; + arrange(); +} + +static void +toggleview(const char *arg) { + unsigned int i, j; + + i = idxoftag(arg); + seltags[i] = !seltags[i]; + for(j = 0; j < ntags && !seltags[j]; j++); + if(j == ntags) + seltags[i] = True; /* cannot toggle last view */ + arrange(); +} + +static void +unban(Client *c) { + if(!c->isbanned) + return; + XMoveWindow(dpy, c->win, c->x, c->y); + c->isbanned = False; +} + +static void +unmanage(Client *c) { + XWindowChanges wc; + + wc.border_width = c->oldborder; + /* The server grab construct avoids race conditions. */ + XGrabServer(dpy); + XSetErrorHandler(xerrordummy); + XConfigureWindow(dpy, c->win, CWBorderWidth, &wc); /* restore border */ + detach(c); + detachstack(c); + if(sel == c) + focus(NULL); + XUngrabButton(dpy, AnyButton, AnyModifier, c->win); + setclientstate(c, WithdrawnState); + free(c->tags); + free(c); + XSync(dpy, False); + XSetErrorHandler(xerror); + XUngrabServer(dpy); + arrange(); +} + +static void +unmapnotify(XEvent *e) { + Client *c; + XUnmapEvent *ev = &e->xunmap; + + if((c = getclient(ev->window))) + unmanage(c); +} + +static void +updatebarpos(void) { + XEvent ev; + + wax = sx; + way = sy; + wah = sh; + waw = sw; + switch(bpos) { + default: + wah -= bh; + way += bh; + XMoveWindow(dpy, barwin, sx, sy); + break; + case BarBot: + wah -= bh; + XMoveWindow(dpy, barwin, sx, sy + wah); + break; + case BarOff: + XMoveWindow(dpy, barwin, sx, sy - bh); + break; + } + XSync(dpy, False); + while(XCheckMaskEvent(dpy, EnterWindowMask, &ev)); +} + +static void +updatesizehints(Client *c) { + long msize; + XSizeHints size; + + if(!XGetWMNormalHints(dpy, c->win, &size, &msize) || !size.flags) + size.flags = PSize; + c->flags = size.flags; + if(c->flags & PBaseSize) { + c->basew = size.base_width; + c->baseh = size.base_height; + } + else if(c->flags & PMinSize) { + c->basew = size.min_width; + c->baseh = size.min_height; + } + else + c->basew = c->baseh = 0; + if(c->flags & PResizeInc) { + c->incw = size.width_inc; + c->inch = size.height_inc; + } + else + c->incw = c->inch = 0; + if(c->flags & PMaxSize) { + c->maxw = size.max_width; + c->maxh = size.max_height; + } + else + c->maxw = c->maxh = 0; + if(c->flags & PMinSize) { + c->minw = size.min_width; + c->minh = size.min_height; + } + else if(c->flags & PBaseSize) { + c->minw = size.base_width; + c->minh = size.base_height; + } + else + c->minw = c->minh = 0; + if(c->flags & PAspect) { + c->minax = size.min_aspect.x; + c->maxax = size.max_aspect.x; + c->minay = size.min_aspect.y; + c->maxay = size.max_aspect.y; + } + else + c->minax = c->maxax = c->minay = c->maxay = 0; + c->isfixed = (c->maxw && c->minw && c->maxh && c->minh + && c->maxw == c->minw && c->maxh == c->minh); +} + +static void +updatetitle(Client *c) { + if(!gettextprop(c->win, netatom[NetWMName], c->name, sizeof c->name)) + gettextprop(c->win, wmatom[WMName], c->name, sizeof c->name); +} + +/* There's no way to check accesses to destroyed windows, thus those cases are + * ignored (especially on UnmapNotify's). Other types of errors call Xlibs + * default error handler, which may call exit. */ +static int +xerror(Display *dpy, XErrorEvent *ee) { + if(ee->error_code == BadWindow + || (ee->request_code == X_SetInputFocus && ee->error_code == BadMatch) + || (ee->request_code == X_PolyText8 && ee->error_code == BadDrawable) + || (ee->request_code == X_PolyFillRectangle && ee->error_code == BadDrawable) + || (ee->request_code == X_PolySegment && ee->error_code == BadDrawable) + || (ee->request_code == X_ConfigureWindow && ee->error_code == BadMatch) + || (ee->request_code == X_GrabKey && ee->error_code == BadAccess) + || (ee->request_code == X_CopyArea && ee->error_code == BadDrawable)) + return 0; + fprintf(stderr, "dwm: fatal error: request code=%d, error code=%d\n", + ee->request_code, ee->error_code); + return xerrorxlib(dpy, ee); /* may call exit */ +} + +static int +xerrordummy(Display *dsply, XErrorEvent *ee) { + return 0; +} + +/* Startup Error handler to check if another window manager + * is already running. */ +static int +xerrorstart(Display *dsply, XErrorEvent *ee) { + otherwm = True; + return -1; +} + +static void +view(const char *arg) { + unsigned int i; + + for(i = 0; i < ntags; i++) + seltags[i] = arg == NULL; + i = idxoftag(arg); + if(i >= 0 && i < ntags) + seltags[i] = True; + arrange(); +} + static void zoom(const char *arg) { Client *c; -- 2.20.1 From 9449ea3e002990372383835b85ed18ceaf75e400 Mon Sep 17 00:00:00 2001 From: "Anselm R. Garbe" Date: Sun, 16 Sep 2007 12:34:08 +0200 Subject: [PATCH 13/16] some more rearrangements --- dwm.c | 290 +++++++++++++++++++++++++++++----------------------------- 1 file changed, 143 insertions(+), 147 deletions(-) diff --git a/dwm.c b/dwm.c index b6659ae..9932974 100644 --- a/dwm.c +++ b/dwm.c @@ -112,14 +112,14 @@ typedef struct { regex_t *tagregex; } Regs; -/* functions */ - +/* forward declarations */ static void applyrules(Client *c); static void arrange(void); static void attach(Client *c); static void attachstack(Client *c); static void ban(Client *c); static void buttonpress(XEvent *e); +static void checkotherwm(void); static void cleanup(void); static void compileregs(void); static void configure(Client *c); @@ -140,17 +140,13 @@ static void focus(Client *c); static void focusnext(const char *arg); static void focusprev(const char *arg); static Client *getclient(Window w); +static unsigned long getcolor(const char *colstr); 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 unsigned int idxoftag(const char *tag); -static void initbar(void); -static unsigned long initcolor(const char *colstr); static void initfont(const char *fontstr); -static void initlayouts(void); -static void initstyle(void); static Bool isarrange(void (*func)()); -static Bool isfloating(void); static Bool isoccupied(unsigned int t); static Bool isprotodel(Client *c); static Bool isvisible(Client *c); @@ -167,6 +163,7 @@ static void quit(const char *arg); static void resize(Client *c, int x, int y, int w, int h, Bool sizehints); static void resizemouse(Client *c); static void restack(void); +static void run(void); static void scan(void); static void setclientstate(Client *c, long state); static void setlayout(const char *arg); @@ -236,7 +233,7 @@ static Regs *regs = NULL; /* configuration, allows nested code to access above variables */ #include "config.h" -/* implementation */ +/* functions*/ static void applyrules(Client *c) { static char buf[512]; @@ -338,14 +335,14 @@ buttonpress(XEvent *e) { focus(c); if(CLEANMASK(ev->state) != MODKEY) return; - if(ev->button == Button1 && (isfloating() || c->isfloating)) { + if(ev->button == Button1 && (isarrange(floating) || c->isfloating)) { restack(); movemouse(c); } else if(ev->button == Button2) zoom(NULL); else if(ev->button == Button3 - && (isfloating() || c->isfloating) && !c->isfixed) + && (isarrange(floating) || c->isfloating) && !c->isfixed) { restack(); resizemouse(c); @@ -353,6 +350,22 @@ buttonpress(XEvent *e) { } } +static void +checkotherwm(void) { + otherwm = False; + XSetErrorHandler(xerrorstart); + + /* this causes an error if some other window manager is running */ + XSelectInput(dpy, root, SubstructureRedirectMask); + XSync(dpy, False); + if(otherwm) + eprint("dwm: another window manager is already running\n"); + XSync(dpy, False); + XSetErrorHandler(NULL); + xerrorxlib = XSetErrorHandler(xerror); + XSync(dpy, False); +} + static void cleanup(void) { close(STDIN_FILENO); @@ -446,7 +459,7 @@ configurerequest(XEvent *e) { c->ismax = False; if(ev->value_mask & CWBorderWidth) c->border = ev->border_width; - if(c->isfixed || c->isfloating || isfloating()) { + if(c->isfixed || c->isfloating || isarrange(floating)) { if(ev->value_mask & CWX) c->x = ev->x; if(ev->value_mask & CWY) @@ -728,6 +741,16 @@ getclient(Window w) { return c; } +static unsigned long +getcolor(const char *colstr) { + Colormap cmap = DefaultColormap(dpy, screen); + XColor color; + + if(!XAllocNamedColor(dpy, cmap, colstr, &color, &color)) + eprint("error, cannot allocate color '%s'\n", colstr); + return color.pixel; +} + static long getstate(Window w) { int format, status; @@ -820,37 +843,6 @@ idxoftag(const char *tag) { return 0; } -static void -initbar(void) { - XSetWindowAttributes wa; - - wa.override_redirect = 1; - wa.background_pixmap = ParentRelative; - wa.event_mask = ButtonPressMask | ExposureMask; - barwin = XCreateWindow(dpy, root, sx, sy, sw, bh, 0, - DefaultDepth(dpy, screen), CopyFromParent, DefaultVisual(dpy, screen), - CWOverrideRedirect | CWBackPixmap | CWEventMask, &wa); - XDefineCursor(dpy, barwin, cursor[CurNormal]); - updatebarpos(); - XMapRaised(dpy, barwin); - strcpy(stext, "dwm-"VERSION); - dc.drawable = XCreatePixmap(dpy, root, sw, bh, DefaultDepth(dpy, screen)); - dc.gc = XCreateGC(dpy, root, 0, 0); - XSetLineAttributes(dpy, dc.gc, 1, LineSolid, CapButt, JoinMiter); - if(!dc.font.set) - XSetFont(dpy, dc.gc, dc.font.xfont->fid); -} - -static unsigned long -initcolor(const char *colstr) { - Colormap cmap = DefaultColormap(dpy, screen); - XColor color; - - if(!XAllocNamedColor(dpy, cmap, colstr, &color, &color)) - eprint("error, cannot allocate color '%s'\n", colstr); - return color.pixel; -} - static void initfont(const char *fontstr) { char *def, **missing; @@ -893,41 +885,12 @@ initfont(const char *fontstr) { dc.font.height = dc.font.ascent + dc.font.descent; } -static void -initlayouts(void) { - unsigned int i, w; - - nlayouts = sizeof layouts / sizeof layouts[0]; - for(blw = i = 0; i < nlayouts; i++) { - w = textw(layouts[i].symbol); - if(w > blw) - blw = w; - } -} - -static void -initstyle(void) { - dc.norm[ColBorder] = initcolor(NORMBORDERCOLOR); - dc.norm[ColBG] = initcolor(NORMBGCOLOR); - dc.norm[ColFG] = initcolor(NORMFGCOLOR); - dc.sel[ColBorder] = initcolor(SELBORDERCOLOR); - dc.sel[ColBG] = initcolor(SELBGCOLOR); - dc.sel[ColFG] = initcolor(SELFGCOLOR); - initfont(FONT); - dc.h = bh = dc.font.height + 2; -} - static Bool isarrange(void (*func)()) { return func == layouts[ltidx].arrange; } -static Bool -isfloating(void) { - return layouts[ltidx].arrange == floating; -} - static Bool isoccupied(unsigned int t) { Client *c; @@ -1300,9 +1263,9 @@ restack(void) { drawbar(); if(!sel) return; - if(sel->isfloating || isfloating()) + if(sel->isfloating || isarrange(floating)) XRaiseWindow(dpy, sel->win); - if(!isfloating()) { + if(!isarrange(floating)) { wc.stack_mode = Below; wc.sibling = barwin; if(!sel->isfloating) { @@ -1320,6 +1283,54 @@ restack(void) { while(XCheckMaskEvent(dpy, EnterWindowMask, &ev)); } +static void +run(void) { + char *p; + int r, xfd; + fd_set rd; + XEvent ev; + + /* main event loop, also reads status text from stdin */ + XSync(dpy, False); + xfd = ConnectionNumber(dpy); + readin = True; + while(running) { + FD_ZERO(&rd); + if(readin) + FD_SET(STDIN_FILENO, &rd); + FD_SET(xfd, &rd); + if(select(xfd + 1, &rd, NULL, NULL, NULL) == -1) { + if(errno == EINTR) + continue; + eprint("select failed\n"); + } + if(FD_ISSET(STDIN_FILENO, &rd)) { + switch(r = read(STDIN_FILENO, stext, sizeof stext - 1)) { + case -1: + strncpy(stext, strerror(errno), sizeof stext - 1); + stext[sizeof stext - 1] = '\0'; + readin = False; + break; + case 0: + strncpy(stext, "EOF", 4); + 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); + } + drawbar(); + } + while(XPending(dpy)) { + XNextEvent(dpy, &ev); + if(handler[ev.type]) + (handler[ev.type])(&ev); /* call handler */ + } + } +} + static void scan(void) { unsigned int i, num; @@ -1401,8 +1412,7 @@ setmwfact(const char *arg) { static void setup(void) { - int i, j; - unsigned int mask; + unsigned int i, j, mask; Window w; XModifierKeymap *modmap; XSetWindowAttributes wa; @@ -1416,37 +1426,80 @@ setup(void) { netatom[NetWMName] = XInternAtom(dpy, "_NET_WM_NAME", False); XChangeProperty(dpy, root, netatom[NetSupported], XA_ATOM, 32, PropModeReplace, (unsigned char *) netatom, NetLast); + /* init cursors */ cursor[CurNormal] = XCreateFontCursor(dpy, XC_left_ptr); cursor[CurResize] = XCreateFontCursor(dpy, XC_sizing); cursor[CurMove] = XCreateFontCursor(dpy, XC_fleur); + + /* init geometry */ + sx = sy = 0; + sw = DisplayWidth(dpy, screen); + sh = DisplayHeight(dpy, screen); + /* init modifier map */ modmap = XGetModifierMapping(dpy); - for (i = 0; i < 8; i++) - for (j = 0; j < modmap->max_keypermod; j++) { + for(i = 0; i < 8; i++) + for(j = 0; j < modmap->max_keypermod; j++) { if(modmap->modifiermap[i * modmap->max_keypermod + j] - == XKeysymToKeycode(dpy, XK_Num_Lock)) + == XKeysymToKeycode(dpy, XK_Num_Lock)) numlockmask = (1 << i); } XFreeModifiermap(modmap); + /* select for events */ wa.event_mask = SubstructureRedirectMask | SubstructureNotifyMask | EnterWindowMask | LeaveWindowMask | StructureNotifyMask; wa.cursor = cursor[CurNormal]; XChangeWindowAttributes(dpy, root, CWEventMask | CWCursor, &wa); XSelectInput(dpy, root, wa.event_mask); - keypress(NULL); /* grabkeys */ + + /* grab keys */ + keypress(NULL); + + /* init tags */ compileregs(); for(ntags = 0; tags[ntags]; ntags++); seltags = emallocz(sizeof(Bool) * ntags); seltags[0] = True; - /* geometry */ - sx = sy = 0; - sw = DisplayWidth(dpy, screen); - sh = DisplayHeight(dpy, screen); - initstyle(); - initlayouts(); - initbar(); + + /* 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); + initfont(FONT); + dc.h = bh = dc.font.height + 2; + + /* init layouts */ + mwfact = MWFACT; + nlayouts = sizeof layouts / sizeof layouts[0]; + for(blw = i = 0; i < nlayouts; i++) { + j = textw(layouts[i].symbol); + if(j > blw) + blw = j; + } + + /* init bar */ + bpos = BARPOS; + wa.override_redirect = 1; + wa.background_pixmap = ParentRelative; + wa.event_mask = ButtonPressMask | ExposureMask; + barwin = XCreateWindow(dpy, root, sx, sy, sw, bh, 0, + DefaultDepth(dpy, screen), CopyFromParent, DefaultVisual(dpy, screen), + CWOverrideRedirect | CWBackPixmap | CWEventMask, &wa); + XDefineCursor(dpy, barwin, cursor[CurNormal]); + updatebarpos(); + XMapRaised(dpy, barwin); + strcpy(stext, "dwm-"VERSION); + dc.drawable = XCreatePixmap(dpy, root, sw, bh, DefaultDepth(dpy, screen)); + dc.gc = XCreateGC(dpy, root, 0, 0); + XSetLineAttributes(dpy, dc.gc, 1, LineSolid, CapButt, JoinMiter); + if(!dc.font.set) + XSetFont(dpy, dc.gc, dc.font.xfont->fid); + /* multihead support */ selscreen = XQueryPointer(dpy, root, &w, &w, &i, &i, &i, &i, &mask); } @@ -1568,7 +1621,7 @@ static void togglemax(const char *arg) { XEvent ev; - if(!sel || (!isfloating() && !sel->isfloating) || sel->isfixed) + if(!sel || (!isarrange(floating) && !sel->isfloating) || sel->isfixed) return; if((sel->ismax = !sel->ismax)) { sel->rx = sel->x; @@ -1794,81 +1847,24 @@ zoom(const char *arg) { int main(int argc, char *argv[]) { - char *p; - int r, xfd; - fd_set rd; - XEvent ev; - 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"); else if(argc != 1) eprint("usage: dwm [-v]\n"); - /* macros from config.h can be used at function level only */ - mwfact = MWFACT; - bpos = BARPOS; - setlocale(LC_CTYPE, ""); if(!(dpy = XOpenDisplay(0))) eprint("dwm: cannot open display\n"); - xfd = ConnectionNumber(dpy); screen = DefaultScreen(dpy); root = RootWindow(dpy, screen); - otherwm = False; - XSetErrorHandler(xerrorstart); - /* this causes an error if some other window manager is running */ - XSelectInput(dpy, root, SubstructureRedirectMask); - XSync(dpy, False); - if(otherwm) - eprint("dwm: another window manager is already running\n"); - XSync(dpy, False); - XSetErrorHandler(NULL); - xerrorxlib = XSetErrorHandler(xerror); - XSync(dpy, False); + checkotherwm(); setup(); drawbar(); scan(); - - /* main event loop, also reads status text from stdin */ - XSync(dpy, False); - readin = True; - while(running) { - FD_ZERO(&rd); - if(readin) - FD_SET(STDIN_FILENO, &rd); - FD_SET(xfd, &rd); - if(select(xfd + 1, &rd, NULL, NULL, NULL) == -1) { - if(errno == EINTR) - continue; - eprint("select failed\n"); - } - if(FD_ISSET(STDIN_FILENO, &rd)) { - switch(r = read(STDIN_FILENO, stext, sizeof stext - 1)) { - case -1: - strncpy(stext, strerror(errno), sizeof stext - 1); - stext[sizeof stext - 1] = '\0'; - readin = False; - break; - case 0: - strncpy(stext, "EOF", 4); - 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); - } - drawbar(); - } - while(XPending(dpy)) { - XNextEvent(dpy, &ev); - if(handler[ev.type]) - (handler[ev.type])(&ev); /* call handler */ - } - } + run(); cleanup(); + XCloseDisplay(dpy); return 0; } -- 2.20.1 From d1ce3eac33a636e03a1f5a887897ae8046065ff7 Mon Sep 17 00:00:00 2001 From: "Anselm R. Garbe" Date: Sun, 16 Sep 2007 13:27:33 +0200 Subject: [PATCH 14/16] now tiled windows can be resized/moved, their floating state will be toggled implicitely --- dwm.1 | 4 ++-- dwm.c | 16 ++++++++++------ 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/dwm.1 b/dwm.1 index cf197f5..d82c387 100644 --- a/dwm.1 +++ b/dwm.1 @@ -117,13 +117,13 @@ Quit dwm. .SS Mouse commands .TP .B Mod1\-Button1 -Move current window while dragging (floating layout only). +Move current window while dragging. Tiled windows will be toggled to the floating state. .TP .B Mod1\-Button2 Zooms/cycles current window to/from master area (tiled layout only). .TP .B Mod1\-Button3 -Resize current window while dragging (floating layout only). +Resize current window while dragging. Tiled windows will be toggled to the floating state. .SH CUSTOMIZATION dwm is customized by creating a custom config.h and (re)compiling the source code. This keeps it fast, secure and simple. diff --git a/dwm.c b/dwm.c index 9932974..164b5ef 100644 --- a/dwm.c +++ b/dwm.c @@ -335,16 +335,20 @@ buttonpress(XEvent *e) { focus(c); if(CLEANMASK(ev->state) != MODKEY) return; - if(ev->button == Button1 && (isarrange(floating) || c->isfloating)) { - restack(); + if(ev->button == Button1) { + if(!isarrange(floating) && !c->isfloating) + togglefloating(NULL); + else + restack(); movemouse(c); } else if(ev->button == Button2) zoom(NULL); - else if(ev->button == Button3 - && (isarrange(floating) || c->isfloating) && !c->isfixed) - { - restack(); + else if(ev->button == Button3 && !c->isfixed) { + if(!isarrange(floating) && !c->isfloating) + togglefloating(NULL); + else + restack(); resizemouse(c); } } -- 2.20.1 From b2f276b0f9f15131b0f4a03b46c8bedefbc89eea Mon Sep 17 00:00:00 2001 From: "Anselm R. Garbe" Date: Sun, 16 Sep 2007 13:42:37 +0200 Subject: [PATCH 15/16] Mod1-Button2 on a floating but not-fixed client will make it tiled again --- dwm.1 | 2 +- dwm.c | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/dwm.1 b/dwm.1 index d82c387..5e42e28 100644 --- a/dwm.1 +++ b/dwm.1 @@ -120,7 +120,7 @@ Quit dwm. Move current window while dragging. Tiled windows will be toggled to the floating state. .TP .B Mod1\-Button2 -Zooms/cycles current window to/from master area (tiled layout only). +Zooms/cycles current window to/from master area. If it is floating (but not fixed) it will be toggled to the tiled state instead. .TP .B Mod1\-Button3 Resize current window while dragging. Tiled windows will be toggled to the floating state. diff --git a/dwm.c b/dwm.c index 164b5ef..0428ba1 100644 --- a/dwm.c +++ b/dwm.c @@ -342,8 +342,12 @@ buttonpress(XEvent *e) { restack(); movemouse(c); } - else if(ev->button == Button2) - zoom(NULL); + else if(ev->button == Button2) { + if(isarrange(tile) && !c->isfixed && c->isfloating) + togglefloating(NULL); + else + zoom(NULL); + } else if(ev->button == Button3 && !c->isfixed) { if(!isarrange(floating) && !c->isfloating) togglefloating(NULL); -- 2.20.1 From 01022b95d65612462972bdd009896ba6fdd3063a Mon Sep 17 00:00:00 2001 From: "Anselm R. Garbe" Date: Sun, 16 Sep 2007 20:02:42 +0200 Subject: [PATCH 16/16] there might be envs which need Xlib.h --- dwm.c | 1 + 1 file changed, 1 insertion(+) diff --git a/dwm.c b/dwm.c index 0428ba1..2602ed2 100644 --- a/dwm.c +++ b/dwm.c @@ -41,6 +41,7 @@ #include #include #include +#include #include #include -- 2.20.1