tmicromizing dwm step 1 - dwm - [fork] customized build of dwm, the dynamic win… | |
git clone git://src.adamsgaard.dk/dwm | |
Log | |
Files | |
Refs | |
README | |
LICENSE | |
--- | |
commit 0235a84ef286db5c97239615ae525ce973641941 | |
parent 2091200c957783deed032380d56c4199a23c6b81 | |
Author: Anselm R. Garbe <[email protected]> | |
Date: Sat, 15 Sep 2007 22:25:27 +0200 | |
micromizing dwm step 1 | |
Diffstat: | |
M Makefile | 6 +++--- | |
D bar.c | 263 -----------------------------… | |
D client.c | 386 -----------------------------… | |
M config.h | 26 +++++++++++--------------- | |
M config.mk | 3 --- | |
A dwm.c | 1847 +++++++++++++++++++++++++++++… | |
D dwm.h | 147 -----------------------------… | |
D event.c | 375 -----------------------------… | |
D main.c | 296 -----------------------------… | |
D screen.c | 340 -----------------------------… | |
D tile.c | 85 -----------------------------… | |
D tile.h | 6 ------ | |
D util.c | 52 -----------------------------… | |
13 files changed, 1861 insertions(+), 1971 deletions(-) | |
--- | |
diff --git a/Makefile b/Makefile | |
t@@ -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 | |
t@@ -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 $@ | |
t@@ -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 | |
t@@ -1,263 +0,0 @@ | |
-/* See LICENSE file for copyright and license details. */ | |
-#include "dwm.h" | |
-#include <string.h> | |
-#include <stdio.h> | |
- | |
-/* 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[… | |
- 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,… | |
- 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, DefaultVisu… | |
- 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, scree… | |
- 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 | |
t@@ -1,386 +0,0 @@ | |
-/* See LICENSE file for copyright and license details. */ | |
-#include "dwm.h" | |
-#include <stdlib.h> | |
-#include <X11/Xutil.h> | |
- | |
-/* 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, BU… | |
- GrabModeAsync, GrabModeSync, None, None); | |
- XGrabButton(dpy, Button1, MODKEY | numlockmask, c->win, False,… | |
- GrabModeAsync, GrabModeSync, None, None); | |
- XGrabButton(dpy, Button1, MODKEY | numlockmask | LockMask, c->… | |
- GrabModeAsync, GrabModeSync, None, None); | |
- | |
- XGrabButton(dpy, Button2, MODKEY, c->win, False, BUTTONMASK, | |
- GrabModeAsync, GrabModeSync, None, None); | |
- XGrabButton(dpy, Button2, MODKEY | LockMask, c->win, False, BU… | |
- GrabModeAsync, GrabModeSync, None, None); | |
- XGrabButton(dpy, Button2, MODKEY | numlockmask, c->win, False,… | |
- GrabModeAsync, GrabModeSync, None, None); | |
- XGrabButton(dpy, Button2, MODKEY | numlockmask | LockMask, c->… | |
- GrabModeAsync, GrabModeSync, None, None); | |
- | |
- XGrabButton(dpy, Button3, MODKEY, c->win, False, BUTTONMASK, | |
- GrabModeAsync, GrabModeSync, None, None); | |
- XGrabButton(dpy, Button3, MODKEY | LockMask, c->win, False, BU… | |
- GrabModeAsync, GrabModeSync, None, None); | |
- XGrabButton(dpy, Button3, MODKEY | numlockmask, c->win, False,… | |
- GrabModeAsync, GrabModeSync, None, None); | |
- XGrabButton(dpy, Button3, MODKEY | numlockmask | LockMask, c->… | |
- GrabModeAsync, GrabModeSync, None, None); | |
- } | |
- else | |
- XGrabButton(dpy, AnyButton, AnyModifier, c->win, False, BUTTON… | |
- 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 window… | |
- 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 - … | |
- 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 |… | |
- 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 | |
t@@ -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", NUL… | |
+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 i… | |
#define MWFACT 0.6 /* master width factor [0.1 .… | |
diff --git a/config.mk b/config.mk | |
t@@ -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 | |
t@@ -0,0 +1,1847 @@ | |
+/* See LICENSE file for copyright and license details. */ | |
+#include <errno.h> | |
+#include <locale.h> | |
+#include <regex.h> | |
+#include <stdarg.h> | |
+#include <stdio.h> | |
+#include <stdlib.h> | |
+#include <string.h> | |
+#include <unistd.h> | |
+#include <sys/select.h> | |
+#include <sys/wait.h> | |
+#include <X11/cursorfont.h> | |
+#include <X11/keysym.h> | |
+#include <X11/Xatom.h> | |
+#include <X11/Xproto.h> | |
+#include <X11/Xutil.h> | |
+ | |
+/* 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[… | |
+ 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,… | |
+ 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, DefaultVisu… | |
+ 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, scree… | |
+ 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, BU… | |
+ GrabModeAsync, GrabModeSync, None, None); | |
+ XGrabButton(dpy, Button1, MODKEY | numlockmask, c->win, False,… | |
+ GrabModeAsync, GrabModeSync, None, None); | |
+ XGrabButton(dpy, Button1, MODKEY | numlockmask | LockMask, c->… | |
+ GrabModeAsync, GrabModeSync, None, None); | |
+ | |
+ XGrabButton(dpy, Button2, MODKEY, c->win, False, BUTTONMASK, | |
+ GrabModeAsync, GrabModeSync, None, None); | |
+ XGrabButton(dpy, Button2, MODKEY | LockMask, c->win, False, BU… | |
+ GrabModeAsync, GrabModeSync, None, None); | |
+ XGrabButton(dpy, Button2, MODKEY | numlockmask, c->win, False,… | |
+ GrabModeAsync, GrabModeSync, None, None); | |
+ XGrabButton(dpy, Button2, MODKEY | numlockmask | LockMask, c->… | |
+ GrabModeAsync, GrabModeSync, None, None); | |
+ | |
+ XGrabButton(dpy, Button3, MODKEY, c->win, False, BUTTONMASK, | |
+ GrabModeAsync, GrabModeSync, None, None); | |
+ XGrabButton(dpy, Button3, MODKEY | LockMask, c->win, False, BU… | |
+ GrabModeAsync, GrabModeSync, None, None); | |
+ XGrabButton(dpy, Button3, MODKEY | numlockmask, c->win, False,… | |
+ GrabModeAsync, GrabModeSync, None, None); | |
+ XGrabButton(dpy, Button3, MODKEY | numlockmask | LockMask, c->… | |
+ GrabModeAsync, GrabModeSync, None, None); | |
+ } | |
+ else | |
+ XGrabButton(dpy, AnyButton, AnyModifier, c->win, False, BUTTON… | |
+ 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 window… | |
+ 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 - … | |
+ 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 |… | |
+ 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, GrabModeAs… | |
+ None, cursor[CurMove], CurrentTime) != GrabSuccess) | |
+ return; | |
+ c->ismax = False; | |
+ XQueryPointer(dpy, root, &dummy, &dummy, &x1, &y1, &di, &di, &dui); | |
+ for(;;) { | |
+ XMaskEvent(dpy, MOUSEMASK | ExposureMask | SubstructureRedirec… | |
+ 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))… | |
+ 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))… | |
+ 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, GrabModeAs… | |
+ 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… | |
+ for(;;) { | |
+ XMaskEvent(dpy, MOUSEMASK | ExposureMask | SubstructureRedirec… | |
+ switch(ev.type) { | |
+ case ButtonRelease: | |
+ XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, | |
+ c->w + c->border - 1, c->h + c->border… | |
+ 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 direc… | |
+ if((c->y + c->h) > sh && c->isfloating) | |
+ c->y = sh / 2 - c->h / 2; /* center in y direc… | |
+ 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->… | |
+ } | |
+ 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(dp… | |
+ 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 = (getclie… | |
+ 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… | |
+ 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, &t… | |
+ c->isfloating = rules[i].isfloating; | |
+ for(j = 0; regs[i].tagregex && j < ntags; j++) { | |
+ if(!regexec(regs[i].tagregex, tags[j], 1, &tmp… | |
+ 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 | CWStackMod… | |
+ wc.sibling = sel->win; | |
+ } | |
+ for(c = nexttiled(clients); c; c = nexttiled(c->next)) { | |
+ if(c == sel) | |
+ continue; | |
+ XConfigureWindow(dpy, c->win, CWSibling | CWStackMode,… | |
+ 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->bo… | |
+ } | |
+ 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, wm… | |
+ &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, w… | |
+ continue; | |
+ if(wa.map_state == IsViewable || getstate(wins[i]) == … | |
+ 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]) ==… | |
+ 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 == BadDr… | |
+ || (ee->request_code == X_PolySegment && ee->error_code == BadDrawable) | |
+ || (ee->request_code == X_ConfigureWindow && ee->error_code == BadMatc… | |
+ || (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, … | |
+ 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 -… | |
+ 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)… | |
+ 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 | |
t@@ -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 <X11/Xlib.h> | |
- | |
-/* 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 la… | |
-extern unsigned int ntags, numlockmask; /* number of tags, numl… | |
-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… | |
-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 … | |
-void ban(Client *c); /* bans c */ | |
-void configure(Client *c); /* send synthetic configure event */ | |
-void detach(Client *c); /* detaches c from global clien… | |
-void focus(Client *c); /* focus c if visible && !NULL, … | |
-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 coo… | |
-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… | |
- | |
-/* main.c */ | |
-Bool gettextprop(Window w, Atom atom, | |
- char *text, unsigned int size); /* return text property, UTF-8… | |
-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 o… | |
-void compileregs(void); /* initialize regexps of rules … | |
-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 layou… | |
-void initlayouts(void); /* initialize layout array */ | |
-Bool isarrange(void (*func)()); /* returns True if func is the … | |
-Bool isfloating(void); /* returns True if floating layo… | |
-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/t… | |
-void togglemax(const char *arg); /* toggles maximization of floating cl… | |
-void toggletag(const char *arg); /* toggles sel tags with arg's index */ | |
-void toggleview(const char *arg); /* toggles the tag with arg's index (… | |
-void view(const char *arg); /* views the tag with arg's index */ | |
- | |
-/* util.c */ | |
-void *emallocz(unsigned int size); /* allocates zero-initialized memory… | |
-void eprint(const char *errstr, ...); /* prints errstr and exits with 1… | |
-void spawn(const char *arg); /* forks a new subprocess with arg… | |
diff --git a/event.c b/event.c | |
t@@ -1,375 +0,0 @@ | |
-/* See LICENSE file for copyright and license details. */ | |
-#include "dwm.h" | |
-#include <stdlib.h> | |
-#include <X11/keysym.h> | |
-#include <X11/Xatom.h> | |
-#include <X11/Xutil.h> | |
- | |
-/* 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, GrabModeAs… | |
- None, cursor[CurMove], CurrentTime) != GrabSuccess) | |
- return; | |
- c->ismax = False; | |
- XQueryPointer(dpy, root, &dummy, &dummy, &x1, &y1, &di, &di, &dui); | |
- for(;;) { | |
- XMaskEvent(dpy, MOUSEMASK | ExposureMask | SubstructureRedirec… | |
- 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))… | |
- 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))… | |
- 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, GrabModeAs… | |
- 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… | |
- for(;;) { | |
- XMaskEvent(dpy, MOUSEMASK | ExposureMask | SubstructureRedirec… | |
- switch(ev.type) { | |
- case ButtonRelease: | |
- XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, | |
- c->w + c->border - 1, c->h + c->border… | |
- 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 direc… | |
- if((c->y + c->h) > sh && c->isfloating) | |
- c->y = sh / 2 - c->h / 2; /* center in y direc… | |
- 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->… | |
- } | |
- 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(dp… | |
- 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 = (getclie… | |
- 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… | |
- GrabModeAsync, GrabModeAsync); | |
- } | |
-} | |
diff --git a/main.c b/main.c | |
t@@ -1,296 +0,0 @@ | |
-/* See LICENSE file for copyright and license details. */ | |
-#include "dwm.h" | |
-#include <errno.h> | |
-#include <locale.h> | |
-#include <stdio.h> | |
-#include <stdlib.h> | |
-#include <string.h> | |
-#include <unistd.h> | |
-#include <sys/select.h> | |
-#include <X11/cursorfont.h> | |
-#include <X11/keysym.h> | |
-#include <X11/Xatom.h> | |
-#include <X11/Xproto.h> | |
-#include <X11/Xutil.h> | |
- | |
-/* 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, wm… | |
- &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, w… | |
- continue; | |
- if(wa.map_state == IsViewable || getstate(wins[i]) == … | |
- 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]) ==… | |
- 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 == BadDr… | |
- || (ee->request_code == X_PolySegment && ee->error_code == BadDrawable) | |
- || (ee->request_code == X_ConfigureWindow && ee->error_code == BadMatc… | |
- || (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, … | |
- 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 -… | |
- 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)… | |
- 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 | |
t@@ -1,340 +0,0 @@ | |
-/* See LICENSE file for copyright and license details. */ | |
-#include "dwm.h" | |
-#include <regex.h> | |
-#include <stdio.h> | |
-#include <stdlib.h> | |
-#include <string.h> | |
-#include <X11/Xutil.h> | |
- | |
-/* 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, &t… | |
- c->isfloating = rules[i].isfloating; | |
- for(j = 0; regs[i].tagregex && j < ntags; j++) { | |
- if(!regexec(regs[i].tagregex, tags[j], 1, &tmp… | |
- 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 | CWStackMod… | |
- wc.sibling = sel->win; | |
- } | |
- for(c = nexttiled(clients); c; c = nexttiled(c->next)) { | |
- if(c == sel) | |
- continue; | |
- XConfigureWindow(dpy, c->win, CWSibling | CWStackMode,… | |
- 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->bo… | |
- } | |
- 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 | |
t@@ -1,85 +0,0 @@ | |
-/* See LICENSE file for copyright and license details. */ | |
-#include "dwm.h" | |
-#include <stdio.h> | |
- | |
-/* 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 | |
t@@ -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 mast… | |
diff --git a/util.c b/util.c | |
t@@ -1,52 +0,0 @@ | |
-/* See LICENSE file for copyright and license details. */ | |
-#include "dwm.h" | |
-#include <stdarg.h> | |
-#include <stdio.h> | |
-#include <stdlib.h> | |
-#include <sys/wait.h> | |
-#include <unistd.h> | |
- | |
-/* 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); | |
-} |