tordered all functions alphabetically - dwm - [fork] customized build of dwm, t… | |
git clone git://src.adamsgaard.dk/dwm | |
Log | |
Files | |
Refs | |
README | |
LICENSE | |
--- | |
commit 49197fe4bf023478108c76c1bed74a7d1ef138de | |
parent 11cfff2dae9fb09aace8f46bf338a051cbd36fb3 | |
Author: Anselm R. Garbe <[email protected]> | |
Date: Sun, 16 Sep 2007 11:53:14 +0200 | |
ordered all functions alphabetically | |
Diffstat: | |
M dwm.c | 2358 +++++++++++++++--------------… | |
1 file changed, 1178 insertions(+), 1180 deletions(-) | |
--- | |
diff --git a/dwm.c b/dwm.c | |
t@@ -233,769 +233,206 @@ static DC dc = {0}; | |
static Window barwin, root; | |
static Regs *regs = NULL; | |
-/* configuration, allows nested code to work on above variables */ | |
+/* configuration, allows nested code to access above variables */ | |
#include "config.h" | |
+/* implementation */ | |
static void | |
-eprint(const char *errstr, ...) { | |
- va_list ap; | |
+applyrules(Client *c) { | |
+ static char buf[512]; | |
+ unsigned int i, j; | |
+ regmatch_t tmp; | |
+ Bool matched = False; | |
+ XClassHint ch = { 0 }; | |
- va_start(ap, errstr); | |
- vfprintf(stderr, errstr, ap); | |
- va_end(ap); | |
- exit(EXIT_FAILURE); | |
+ /* rule matching */ | |
+ XGetClassHint(dpy, c->win, &ch); | |
+ snprintf(buf, sizeof buf, "%s:%s:%s", | |
+ ch.res_class ? ch.res_class : "", | |
+ ch.res_name ? ch.res_name : "", c->name); | |
+ for(i = 0; i < nrules; i++) | |
+ if(regs[i].propregex && !regexec(regs[i].propregex, buf, 1, &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 * | |
-emallocz(unsigned int size) { | |
- void *res = calloc(1, size); | |
+static void | |
+arrange(void) { | |
+ Client *c; | |
- if(!res) | |
- eprint("fatal: could not malloc() %u bytes\n", size); | |
- return res; | |
+ for(c = clients; c; c = c->next) | |
+ if(isvisible(c)) | |
+ unban(c); | |
+ else | |
+ ban(c); | |
+ layouts[ltidx].arrange(); | |
+ focus(NULL); | |
+ restack(); | |
} | |
static void | |
-spawn(const char *arg) { | |
- static char *shell = NULL; | |
- | |
- if(!shell && !(shell = getenv("SHELL"))) | |
- shell = "/bin/sh"; | |
- if(!arg) | |
- return; | |
- /* The double-fork construct avoids zombie processes and keeps the code | |
- * clean from stupid signal handlers. */ | |
- if(fork() == 0) { | |
- if(fork() == 0) { | |
- if(dpy) | |
- close(ConnectionNumber(dpy)); | |
- setsid(); | |
- execl(shell, shell, "-c", arg, (char *)NULL); | |
- fprintf(stderr, "dwm: execl '%s -c %s'", shell, arg); | |
- perror(" failed"); | |
- } | |
- exit(0); | |
- } | |
- wait(0); | |
+attach(Client *c) { | |
+ if(clients) | |
+ clients->prev = c; | |
+ c->next = clients; | |
+ clients = c; | |
} | |
static void | |
-drawsquare(Bool filled, Bool empty, unsigned long col[ColLast]) { | |
- int x; | |
- XGCValues gcv; | |
- XRectangle r = { dc.x, dc.y, dc.w, dc.h }; | |
- | |
- gcv.foreground = col[ColFG]; | |
- XChangeGC(dpy, dc.gc, GCForeground, &gcv); | |
- x = (dc.font.ascent + dc.font.descent + 2) / 4; | |
- r.x = dc.x + 1; | |
- r.y = dc.y + 1; | |
- if(filled) { | |
- r.width = r.height = x + 1; | |
- XFillRectangles(dpy, dc.drawable, dc.gc, &r, 1); | |
- } | |
- else if(empty) { | |
- r.width = r.height = x; | |
- XDrawRectangles(dpy, dc.drawable, dc.gc, &r, 1); | |
- } | |
+attachstack(Client *c) { | |
+ c->snext = stack; | |
+ stack = c; | |
} | |
-static unsigned long | |
-initcolor(const char *colstr) { | |
- Colormap cmap = DefaultColormap(dpy, screen); | |
- XColor color; | |
- | |
- if(!XAllocNamedColor(dpy, cmap, colstr, &color, &color)) | |
- eprint("error, cannot allocate color '%s'\n", colstr); | |
- return color.pixel; | |
+static void | |
+ban(Client *c) { | |
+ if(c->isbanned) | |
+ return; | |
+ XMoveWindow(dpy, c->win, c->x + 2 * sw, c->y); | |
+ c->isbanned = True; | |
} | |
static void | |
-initfont(const char *fontstr) { | |
- char *def, **missing; | |
- int i, n; | |
+buttonpress(XEvent *e) { | |
+ unsigned int i, x; | |
+ Client *c; | |
+ XButtonPressedEvent *ev = &e->xbutton; | |
- missing = NULL; | |
- if(dc.font.set) | |
- XFreeFontSet(dpy, dc.font.set); | |
- dc.font.set = XCreateFontSet(dpy, fontstr, &missing, &n, &def); | |
- if(missing) { | |
- while(n--) | |
- fprintf(stderr, "dwm: missing fontset: %s\n", missing[… | |
- XFreeStringList(missing); | |
- } | |
- if(dc.font.set) { | |
- XFontSetExtents *font_extents; | |
- XFontStruct **xfonts; | |
- char **font_names; | |
- dc.font.ascent = dc.font.descent = 0; | |
- font_extents = XExtentsOfFontSet(dc.font.set); | |
- n = XFontsOfFontSet(dc.font.set, &xfonts, &font_names); | |
- for(i = 0, dc.font.ascent = 0, dc.font.descent = 0; i < n; i++… | |
- if(dc.font.ascent < (*xfonts)->ascent) | |
- dc.font.ascent = (*xfonts)->ascent; | |
- if(dc.font.descent < (*xfonts)->descent) | |
- dc.font.descent = (*xfonts)->descent; | |
- xfonts++; | |
+ if(barwin == ev->window) { | |
+ x = 0; | |
+ for(i = 0; i < ntags; i++) { | |
+ x += textw(tags[i]); | |
+ if(ev->x < x) { | |
+ if(ev->button == Button1) { | |
+ if(ev->state & MODKEY) | |
+ tag(tags[i]); | |
+ else | |
+ view(tags[i]); | |
+ } | |
+ else if(ev->button == Button3) { | |
+ if(ev->state & MODKEY) | |
+ toggletag(tags[i]); | |
+ else | |
+ toggleview(tags[i]); | |
+ } | |
+ return; | |
+ } | |
} | |
+ if((ev->x < x + blw) && ev->button == Button1) | |
+ setlayout(NULL); | |
} | |
- else { | |
- if(dc.font.xfont) | |
- XFreeFont(dpy, dc.font.xfont); | |
- dc.font.xfont = NULL; | |
- if(!(dc.font.xfont = XLoadQueryFont(dpy, fontstr)) | |
- || !(dc.font.xfont = XLoadQueryFont(dpy, "fixed"))) | |
- eprint("error, cannot load font: '%s'\n", fontstr); | |
- dc.font.ascent = dc.font.xfont->ascent; | |
- dc.font.descent = dc.font.xfont->descent; | |
+ else if((c = getclient(ev->window))) { | |
+ focus(c); | |
+ if(CLEANMASK(ev->state) != MODKEY) | |
+ return; | |
+ if(ev->button == Button1 && (isfloating() || c->isfloating)) { | |
+ restack(); | |
+ movemouse(c); | |
+ } | |
+ else if(ev->button == Button2) | |
+ zoom(NULL); | |
+ else if(ev->button == Button3 | |
+ && (isfloating() || c->isfloating) && !c->isfixed) | |
+ { | |
+ restack(); | |
+ resizemouse(c); | |
+ } | |
} | |
- dc.font.height = dc.font.ascent + dc.font.descent; | |
-} | |
- | |
-static Bool | |
-isoccupied(unsigned int t) { | |
- Client *c; | |
- | |
- for(c = clients; c; c = c->next) | |
- if(c->tags[t]) | |
- return True; | |
- return False; | |
} | |
-static unsigned int | |
-textnw(const char *text, unsigned int len) { | |
- XRectangle r; | |
- | |
- if(dc.font.set) { | |
- XmbTextExtents(dc.font.set, text, len, NULL, &r); | |
- return r.width; | |
+static void | |
+cleanup(void) { | |
+ close(STDIN_FILENO); | |
+ while(stack) { | |
+ unban(stack); | |
+ unmanage(stack); | |
} | |
- return XTextWidth(dc.font.xfont, text, len); | |
+ if(dc.font.set) | |
+ XFreeFontSet(dpy, dc.font.set); | |
+ else | |
+ XFreeFont(dpy, dc.font.xfont); | |
+ XUngrabKey(dpy, AnyKey, AnyModifier, root); | |
+ XFreePixmap(dpy, dc.drawable); | |
+ XFreeGC(dpy, dc.gc); | |
+ XDestroyWindow(dpy, barwin); | |
+ XFreeCursor(dpy, cursor[CurNormal]); | |
+ XFreeCursor(dpy, cursor[CurResize]); | |
+ XFreeCursor(dpy, cursor[CurMove]); | |
+ XSetInputFocus(dpy, PointerRoot, RevertToPointerRoot, CurrentTime); | |
+ XSync(dpy, False); | |
+ free(seltags); | |
} | |
static void | |
-drawtext(const char *text, unsigned long col[ColLast]) { | |
- int x, y, w, h; | |
- static char buf[256]; | |
- unsigned int len, olen; | |
- XRectangle r = { dc.x, dc.y, dc.w, dc.h }; | |
+compileregs(void) { | |
+ unsigned int i; | |
+ regex_t *reg; | |
- XSetForeground(dpy, dc.gc, col[ColBG]); | |
- XFillRectangles(dpy, dc.drawable, dc.gc, &r, 1); | |
- if(!text) | |
+ if(regs) | |
return; | |
- w = 0; | |
- olen = len = strlen(text); | |
- if(len >= sizeof buf) | |
- len = sizeof buf - 1; | |
- memcpy(buf, text, len); | |
- buf[len] = 0; | |
- h = dc.font.ascent + dc.font.descent; | |
- y = dc.y + (dc.h / 2) - (h / 2) + dc.font.ascent; | |
- x = dc.x + (h / 2); | |
- /* shorten text if necessary */ | |
- while(len && (w = textnw(buf, len)) > dc.w - h) | |
- buf[--len] = 0; | |
- if(len < olen) { | |
- if(len > 1) | |
- buf[len - 1] = '.'; | |
- if(len > 2) | |
- buf[len - 2] = '.'; | |
- if(len > 3) | |
- buf[len - 3] = '.'; | |
- } | |
- if(w > dc.w) | |
- return; /* too long */ | |
- XSetForeground(dpy, dc.gc, col[ColFG]); | |
- if(dc.font.set) | |
- XmbDrawString(dpy, dc.drawable, dc.font.set, dc.gc, x, y, buf,… | |
- 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; | |
+ nrules = sizeof rules / sizeof rules[0]; | |
+ regs = emallocz(nrules * sizeof(Regs)); | |
+ for(i = 0; i < nrules; i++) { | |
+ if(rules[i].prop) { | |
+ reg = emallocz(sizeof(regex_t)); | |
+ if(regcomp(reg, rules[i].prop, REG_EXTENDED)) | |
+ free(reg); | |
+ else | |
+ regs[i].propregex = reg; | |
} | |
- } | |
-} | |
- | |
-static void | |
-resizemouse(Client *c) { | |
- int ocx, ocy; | |
- int nw, nh; | |
- XEvent ev; | |
- | |
- ocx = c->x; | |
- ocy = c->y; | |
- if(XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, 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; | |
+ if(rules[i].tags) { | |
+ reg = emallocz(sizeof(regex_t)); | |
+ if(regcomp(reg, rules[i].tags, REG_EXTENDED)) | |
+ free(reg); | |
+ else | |
+ regs[i].tagregex = reg; | |
} | |
} | |
} | |
static void | |
-buttonpress(XEvent *e) { | |
- unsigned int i, x; | |
- Client *c; | |
- XButtonPressedEvent *ev = &e->xbutton; | |
+configure(Client *c) { | |
+ XConfigureEvent ce; | |
- if(barwin == ev->window) { | |
- x = 0; | |
- for(i = 0; i < ntags; i++) { | |
- x += textw(tags[i]); | |
- if(ev->x < x) { | |
- if(ev->button == Button1) { | |
- if(ev->state & MODKEY) | |
- tag(tags[i]); | |
- else | |
- view(tags[i]); | |
- } | |
- else if(ev->button == Button3) { | |
- if(ev->state & MODKEY) | |
- toggletag(tags[i]); | |
- else | |
- toggleview(tags[i]); | |
- } | |
- return; | |
- } | |
- } | |
- if((ev->x < x + blw) && ev->button == Button1) | |
- setlayout(NULL); | |
- } | |
- else if((c = getclient(ev->window))) { | |
- focus(c); | |
- if(CLEANMASK(ev->state) != MODKEY) | |
- return; | |
- if(ev->button == Button1 && (isfloating() || c->isfloating)) { | |
- restack(); | |
- movemouse(c); | |
- } | |
- else if(ev->button == Button2) | |
- zoom(NULL); | |
- else if(ev->button == Button3 | |
- && (isfloating() || c->isfloating) && !c->isfixed) | |
- { | |
- restack(); | |
- resizemouse(c); | |
- } | |
+ ce.type = ConfigureNotify; | |
+ ce.display = dpy; | |
+ ce.event = c->win; | |
+ ce.window = c->win; | |
+ ce.x = c->x; | |
+ ce.y = c->y; | |
+ ce.width = c->w; | |
+ ce.height = c->h; | |
+ ce.border_width = c->border; | |
+ ce.above = None; | |
+ ce.override_redirect = False; | |
+ XSendEvent(dpy, c->win, False, StructureNotifyMask, (XEvent *)&ce); | |
+} | |
+ | |
+static void | |
+configurenotify(XEvent *e) { | |
+ XConfigureEvent *ev = &e->xconfigure; | |
+ | |
+ if (ev->window == root && (ev->width != sw || ev->height != sh)) { | |
+ sw = ev->width; | |
+ sh = ev->height; | |
+ XFreePixmap(dpy, dc.drawable); | |
+ dc.drawable = XCreatePixmap(dpy, root, sw, bh, DefaultDepth(dp… | |
+ XResizeWindow(dpy, barwin, sw, bh); | |
+ updatebarpos(); | |
+ arrange(); | |
} | |
} | |
t@@ -1045,21 +482,6 @@ configurerequest(XEvent *e) { | |
} | |
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; | |
t@@ -1069,242 +491,406 @@ destroynotify(XEvent *e) { | |
} | |
static void | |
-enternotify(XEvent *e) { | |
- Client *c; | |
- XCrossingEvent *ev = &e->xcrossing; | |
+detach(Client *c) { | |
+ if(c->prev) | |
+ c->prev->next = c->next; | |
+ if(c->next) | |
+ c->next->prev = c->prev; | |
+ if(c == clients) | |
+ clients = c->next; | |
+ c->next = c->prev = NULL; | |
+} | |
- if(ev->mode != NotifyNormal || ev->detail == NotifyInferior) | |
- return; | |
- if((c = getclient(ev->window))) | |
- focus(c); | |
- else if(ev->window == root) { | |
- selscreen = True; | |
- focus(NULL); | |
+static void | |
+detachstack(Client *c) { | |
+ Client **tc; | |
+ | |
+ for(tc=&stack; *tc && *tc != c; tc=&(*tc)->snext); | |
+ *tc = c->snext; | |
+} | |
+ | |
+static void | |
+drawbar(void) { | |
+ int i, x; | |
+ | |
+ dc.x = dc.y = 0; | |
+ for(i = 0; i < ntags; i++) { | |
+ dc.w = textw(tags[i]); | |
+ if(seltags[i]) { | |
+ drawtext(tags[i], dc.sel); | |
+ drawsquare(sel && sel->tags[i], isoccupied(i), dc.sel); | |
+ } | |
+ else { | |
+ drawtext(tags[i], dc.norm); | |
+ drawsquare(sel && sel->tags[i], isoccupied(i), dc.norm… | |
+ } | |
+ dc.x += dc.w; | |
+ } | |
+ dc.w = blw; | |
+ drawtext(layouts[ltidx].symbol, dc.norm); | |
+ x = dc.x + dc.w; | |
+ dc.w = textw(stext); | |
+ dc.x = sw - dc.w; | |
+ if(dc.x < x) { | |
+ dc.x = x; | |
+ dc.w = sw - x; | |
+ } | |
+ drawtext(stext, dc.norm); | |
+ if((dc.w = dc.x - x) > bh) { | |
+ dc.x = x; | |
+ if(sel) { | |
+ drawtext(sel->name, dc.sel); | |
+ drawsquare(sel->ismax, sel->isfloating, dc.sel); | |
+ } | |
+ else | |
+ drawtext(NULL, dc.norm); | |
} | |
+ XCopyArea(dpy, dc.drawable, barwin, dc.gc, 0, 0, sw, bh, 0, 0); | |
+ XSync(dpy, False); | |
} | |
static void | |
-expose(XEvent *e) { | |
- XExposeEvent *ev = &e->xexpose; | |
+drawsquare(Bool filled, Bool empty, unsigned long col[ColLast]) { | |
+ int x; | |
+ XGCValues gcv; | |
+ XRectangle r = { dc.x, dc.y, dc.w, dc.h }; | |
- if(ev->count == 0) { | |
- if(barwin == ev->window) | |
- drawbar(); | |
+ gcv.foreground = col[ColFG]; | |
+ XChangeGC(dpy, dc.gc, GCForeground, &gcv); | |
+ x = (dc.font.ascent + dc.font.descent + 2) / 4; | |
+ r.x = dc.x + 1; | |
+ r.y = dc.y + 1; | |
+ if(filled) { | |
+ r.width = r.height = x + 1; | |
+ XFillRectangles(dpy, dc.drawable, dc.gc, &r, 1); | |
+ } | |
+ else if(empty) { | |
+ r.width = r.height = x; | |
+ XDrawRectangles(dpy, dc.drawable, dc.gc, &r, 1); | |
} | |
} | |
static void | |
-keypress(XEvent *e) { | |
- KEYS | |
- unsigned int len = sizeof keys / sizeof keys[0]; | |
- unsigned int i; | |
- KeyCode code; | |
- KeySym keysym; | |
- XKeyEvent *ev; | |
+drawtext(const char *text, unsigned long col[ColLast]) { | |
+ int x, y, w, h; | |
+ static char buf[256]; | |
+ unsigned int len, olen; | |
+ XRectangle r = { dc.x, dc.y, dc.w, dc.h }; | |
- if(!e) { /* grabkeys */ | |
- XUngrabKey(dpy, AnyKey, AnyModifier, root); | |
- for(i = 0; i < len; i++) { | |
- code = XKeysymToKeycode(dpy, keys[i].keysym); | |
- XGrabKey(dpy, code, keys[i].mod, root, True, | |
- GrabModeAsync, GrabModeAsync); | |
- XGrabKey(dpy, code, keys[i].mod | LockMask, root, True, | |
- GrabModeAsync, GrabModeAsync); | |
- XGrabKey(dpy, code, keys[i].mod | numlockmask, root, T… | |
- GrabModeAsync, GrabModeAsync); | |
- XGrabKey(dpy, code, keys[i].mod | numlockmask | LockMa… | |
- GrabModeAsync, GrabModeAsync); | |
- } | |
+ XSetForeground(dpy, dc.gc, col[ColBG]); | |
+ XFillRectangles(dpy, dc.drawable, dc.gc, &r, 1); | |
+ if(!text) | |
return; | |
+ w = 0; | |
+ olen = len = strlen(text); | |
+ if(len >= sizeof buf) | |
+ len = sizeof buf - 1; | |
+ memcpy(buf, text, len); | |
+ buf[len] = 0; | |
+ h = dc.font.ascent + dc.font.descent; | |
+ y = dc.y + (dc.h / 2) - (h / 2) + dc.font.ascent; | |
+ x = dc.x + (h / 2); | |
+ /* shorten text if necessary */ | |
+ while(len && (w = textnw(buf, len)) > dc.w - h) | |
+ buf[--len] = 0; | |
+ if(len < olen) { | |
+ if(len > 1) | |
+ buf[len - 1] = '.'; | |
+ if(len > 2) | |
+ buf[len - 2] = '.'; | |
+ if(len > 3) | |
+ buf[len - 3] = '.'; | |
+ } | |
+ if(w > dc.w) | |
+ return; /* too long */ | |
+ XSetForeground(dpy, dc.gc, col[ColFG]); | |
+ if(dc.font.set) | |
+ XmbDrawString(dpy, dc.drawable, dc.font.set, dc.gc, x, y, buf,… | |
+ else | |
+ XDrawString(dpy, dc.drawable, dc.gc, x, y, buf, len); | |
+} | |
+ | |
+static void * | |
+emallocz(unsigned int size) { | |
+ void *res = calloc(1, size); | |
+ | |
+ if(!res) | |
+ eprint("fatal: could not malloc() %u bytes\n", size); | |
+ return res; | |
+} | |
+ | |
+static void | |
+enternotify(XEvent *e) { | |
+ Client *c; | |
+ XCrossingEvent *ev = &e->xcrossing; | |
+ | |
+ if(ev->mode != NotifyNormal || ev->detail == NotifyInferior) | |
+ return; | |
+ if((c = getclient(ev->window))) | |
+ focus(c); | |
+ else if(ev->window == root) { | |
+ selscreen = True; | |
+ focus(NULL); | |
} | |
- ev = &e->xkey; | |
- keysym = XKeycodeToKeysym(dpy, (KeyCode)ev->keycode, 0); | |
- for(i = 0; i < len; i++) | |
- if(keysym == keys[i].keysym | |
- && CLEANMASK(keys[i].mod) == CLEANMASK(ev->state)) | |
- { | |
- if(keys[i].func) | |
- keys[i].func(keys[i].arg); | |
- } | |
} | |
static void | |
-leavenotify(XEvent *e) { | |
- XCrossingEvent *ev = &e->xcrossing; | |
+eprint(const char *errstr, ...) { | |
+ va_list ap; | |
- if((ev->window == root) && !ev->same_screen) { | |
- selscreen = False; | |
- focus(NULL); | |
- } | |
+ va_start(ap, errstr); | |
+ vfprintf(stderr, errstr, ap); | |
+ va_end(ap); | |
+ exit(EXIT_FAILURE); | |
} | |
static void | |
-mappingnotify(XEvent *e) { | |
- XMappingEvent *ev = &e->xmapping; | |
+expose(XEvent *e) { | |
+ XExposeEvent *ev = &e->xexpose; | |
- XRefreshKeyboardMapping(ev); | |
- if(ev->request == MappingKeyboard) | |
- keypress(NULL); | |
+ if(ev->count == 0) { | |
+ if(barwin == ev->window) | |
+ drawbar(); | |
+ } | |
} | |
static void | |
-maprequest(XEvent *e) { | |
- static XWindowAttributes wa; | |
- XMapRequestEvent *ev = &e->xmaprequest; | |
+floating(void) { /* default floating layout */ | |
+ Client *c; | |
- if(!XGetWindowAttributes(dpy, ev->window, &wa)) | |
- return; | |
- if(wa.override_redirect) | |
+ for(c = clients; c; c = c->next) | |
+ if(isvisible(c)) | |
+ resize(c, c->x, c->y, c->w, c->h, True); | |
+} | |
+ | |
+static void | |
+focus(Client *c) { | |
+ if((!c && selscreen) || (c && !isvisible(c))) | |
+ for(c = stack; c && !isvisible(c); c = c->snext); | |
+ if(sel && sel != c) { | |
+ grabbuttons(sel, False); | |
+ XSetWindowBorder(dpy, sel->win, dc.norm[ColBorder]); | |
+ } | |
+ if(c) { | |
+ detachstack(c); | |
+ attachstack(c); | |
+ grabbuttons(c, True); | |
+ } | |
+ sel = c; | |
+ drawbar(); | |
+ if(!selscreen) | |
return; | |
- if(!getclient(ev->window)) | |
- manage(ev->window, &wa); | |
+ if(c) { | |
+ XSetWindowBorder(dpy, c->win, dc.sel[ColBorder]); | |
+ XSetInputFocus(dpy, c->win, RevertToPointerRoot, CurrentTime); | |
+ } | |
+ else | |
+ XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime); | |
} | |
static void | |
-propertynotify(XEvent *e) { | |
+focusnext(const char *arg) { | |
Client *c; | |
- Window trans; | |
- XPropertyEvent *ev = &e->xproperty; | |
- if(ev->state == PropertyDelete) | |
- return; /* ignore */ | |
- if((c = getclient(ev->window))) { | |
- switch (ev->atom) { | |
- default: break; | |
- case XA_WM_TRANSIENT_FOR: | |
- XGetTransientForHint(dpy, c->win, &trans); | |
- if(!c->isfloating && (c->isfloating = (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(); | |
- } | |
+ if(!sel) | |
+ return; | |
+ for(c = sel->next; c && !isvisible(c); c = c->next); | |
+ if(!c) | |
+ for(c = clients; c && !isvisible(c); c = c->next); | |
+ if(c) { | |
+ focus(c); | |
+ restack(); | |
} | |
} | |
static void | |
-unmapnotify(XEvent *e) { | |
+focusprev(const char *arg) { | |
Client *c; | |
- XUnmapEvent *ev = &e->xunmap; | |
- if((c = getclient(ev->window))) | |
- unmanage(c); | |
+ if(!sel) | |
+ return; | |
+ for(c = sel->prev; c && !isvisible(c); c = c->prev); | |
+ if(!c) { | |
+ for(c = clients; c && c->next; c = c->next); | |
+ for(; c && !isvisible(c); c = c->prev); | |
+ } | |
+ if(c) { | |
+ focus(c); | |
+ restack(); | |
+ } | |
} | |
-static unsigned int | |
-idxoftag(const char *tag) { | |
- unsigned int i; | |
+static Client * | |
+getclient(Window w) { | |
+ Client *c; | |
- for(i = 0; i < ntags; i++) | |
- if(tags[i] == tag) | |
- return i; | |
- return 0; | |
+ for(c = clients; c && c->win != w; c = c->next); | |
+ return c; | |
} | |
-static void | |
-floating(void) { /* default floating layout */ | |
- Client *c; | |
+static long | |
+getstate(Window w) { | |
+ int format, status; | |
+ long result = -1; | |
+ unsigned char *p = NULL; | |
+ unsigned long n, extra; | |
+ Atom real; | |
- for(c = clients; c; c = c->next) | |
- if(isvisible(c)) | |
- resize(c, c->x, c->y, c->w, c->h, True); | |
+ status = XGetWindowProperty(dpy, w, wmatom[WMState], 0L, 2L, False, wm… | |
+ &real, &format, &n, &extra, (unsigned char **)&p); | |
+ if(status != Success) | |
+ return -1; | |
+ if(n != 0) | |
+ result = *p; | |
+ XFree(p); | |
+ return result; | |
} | |
-static void | |
-applyrules(Client *c) { | |
- static char buf[512]; | |
- unsigned int i, j; | |
- regmatch_t tmp; | |
- Bool matched = False; | |
- XClassHint ch = { 0 }; | |
+static Bool | |
+gettextprop(Window w, Atom atom, char *text, unsigned int size) { | |
+ char **list = NULL; | |
+ int n; | |
+ XTextProperty name; | |
- /* rule matching */ | |
- XGetClassHint(dpy, c->win, &ch); | |
- snprintf(buf, sizeof buf, "%s:%s:%s", | |
- ch.res_class ? ch.res_class : "", | |
- ch.res_name ? ch.res_name : "", c->name); | |
- for(i = 0; i < nrules; i++) | |
- if(regs[i].propregex && !regexec(regs[i].propregex, buf, 1, &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(!text || size == 0) | |
+ return False; | |
+ text[0] = '\0'; | |
+ XGetTextProperty(dpy, w, &name, atom); | |
+ if(!name.nitems) | |
+ return False; | |
+ if(name.encoding == XA_STRING) | |
+ strncpy(text, (char *)name.value, size - 1); | |
+ else { | |
+ if(XmbTextPropertyToTextList(dpy, &name, &list, &n) >= Success | |
+ && n > 0 && *list) | |
+ { | |
+ strncpy(text, *list, size - 1); | |
+ XFreeStringList(list); | |
} | |
- if(ch.res_class) | |
- XFree(ch.res_class); | |
- if(ch.res_name) | |
- XFree(ch.res_name); | |
- if(!matched) | |
- for(i = 0; i < ntags; i++) | |
- c->tags[i] = seltags[i]; | |
+ } | |
+ text[size - 1] = '\0'; | |
+ XFree(name.value); | |
+ return True; | |
} | |
static void | |
-compileregs(void) { | |
- unsigned int i; | |
- regex_t *reg; | |
+grabbuttons(Client *c, Bool focused) { | |
+ XUngrabButton(dpy, AnyButton, AnyModifier, c->win); | |
- if(regs) | |
- return; | |
- nrules = sizeof rules / sizeof rules[0]; | |
- regs = emallocz(nrules * sizeof(Regs)); | |
- for(i = 0; i < nrules; i++) { | |
- if(rules[i].prop) { | |
- reg = emallocz(sizeof(regex_t)); | |
- if(regcomp(reg, rules[i].prop, REG_EXTENDED)) | |
- free(reg); | |
- else | |
- regs[i].propregex = reg; | |
- } | |
- if(rules[i].tags) { | |
- reg = emallocz(sizeof(regex_t)); | |
- if(regcomp(reg, rules[i].tags, REG_EXTENDED)) | |
- free(reg); | |
- else | |
- regs[i].tagregex = reg; | |
- } | |
+ if(focused) { | |
+ XGrabButton(dpy, Button1, MODKEY, c->win, False, BUTTONMASK, | |
+ GrabModeAsync, GrabModeSync, None, None); | |
+ XGrabButton(dpy, Button1, MODKEY | LockMask, c->win, False, 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 unsigned int | |
+idxoftag(const char *tag) { | |
+ unsigned int i; | |
+ | |
+ for(i = 0; i < ntags; i++) | |
+ if(tags[i] == tag) | |
+ return i; | |
+ return 0; | |
} | |
static void | |
-focusnext(const char *arg) { | |
- Client *c; | |
+initbar(void) { | |
+ XSetWindowAttributes wa; | |
- if(!sel) | |
- return; | |
- for(c = sel->next; c && !isvisible(c); c = c->next); | |
- if(!c) | |
- for(c = clients; c && !isvisible(c); c = c->next); | |
- if(c) { | |
- focus(c); | |
- restack(); | |
- } | |
+ wa.override_redirect = 1; | |
+ wa.background_pixmap = ParentRelative; | |
+ wa.event_mask = ButtonPressMask | ExposureMask; | |
+ barwin = XCreateWindow(dpy, root, sx, sy, sw, bh, 0, | |
+ DefaultDepth(dpy, screen), CopyFromParent, 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 long | |
+initcolor(const char *colstr) { | |
+ Colormap cmap = DefaultColormap(dpy, screen); | |
+ XColor color; | |
+ | |
+ if(!XAllocNamedColor(dpy, cmap, colstr, &color, &color)) | |
+ eprint("error, cannot allocate color '%s'\n", colstr); | |
+ return color.pixel; | |
} | |
static void | |
-focusprev(const char *arg) { | |
- Client *c; | |
+initfont(const char *fontstr) { | |
+ char *def, **missing; | |
+ int i, n; | |
- if(!sel) | |
- return; | |
- for(c = sel->prev; c && !isvisible(c); c = c->prev); | |
- if(!c) { | |
- for(c = clients; c && c->next; c = c->next); | |
- for(; c && !isvisible(c); c = c->prev); | |
+ missing = NULL; | |
+ if(dc.font.set) | |
+ XFreeFontSet(dpy, dc.font.set); | |
+ dc.font.set = XCreateFontSet(dpy, fontstr, &missing, &n, &def); | |
+ if(missing) { | |
+ while(n--) | |
+ fprintf(stderr, "dwm: missing fontset: %s\n", missing[… | |
+ XFreeStringList(missing); | |
} | |
- if(c) { | |
- focus(c); | |
- restack(); | |
+ if(dc.font.set) { | |
+ XFontSetExtents *font_extents; | |
+ XFontStruct **xfonts; | |
+ char **font_names; | |
+ dc.font.ascent = dc.font.descent = 0; | |
+ font_extents = XExtentsOfFontSet(dc.font.set); | |
+ n = XFontsOfFontSet(dc.font.set, &xfonts, &font_names); | |
+ for(i = 0, dc.font.ascent = 0, dc.font.descent = 0; i < n; i++… | |
+ if(dc.font.ascent < (*xfonts)->ascent) | |
+ dc.font.ascent = (*xfonts)->ascent; | |
+ if(dc.font.descent < (*xfonts)->descent) | |
+ dc.font.descent = (*xfonts)->descent; | |
+ xfonts++; | |
+ } | |
+ } | |
+ else { | |
+ if(dc.font.xfont) | |
+ XFreeFont(dpy, dc.font.xfont); | |
+ dc.font.xfont = NULL; | |
+ if(!(dc.font.xfont = XLoadQueryFont(dpy, fontstr)) | |
+ || !(dc.font.xfont = XLoadQueryFont(dpy, "fixed"))) | |
+ eprint("error, cannot load font: '%s'\n", fontstr); | |
+ dc.font.ascent = dc.font.xfont->ascent; | |
+ dc.font.descent = dc.font.xfont->descent; | |
} | |
+ dc.font.height = dc.font.ascent + dc.font.descent; | |
} | |
static void | |
t@@ -1319,12 +905,55 @@ initlayouts(void) { | |
} | |
} | |
+static void | |
+initstyle(void) { | |
+ dc.norm[ColBorder] = initcolor(NORMBORDERCOLOR); | |
+ dc.norm[ColBG] = initcolor(NORMBGCOLOR); | |
+ dc.norm[ColFG] = initcolor(NORMFGCOLOR); | |
+ dc.sel[ColBorder] = initcolor(SELBORDERCOLOR); | |
+ dc.sel[ColBG] = initcolor(SELBGCOLOR); | |
+ dc.sel[ColFG] = initcolor(SELFGCOLOR); | |
+ initfont(FONT); | |
+ dc.h = bh = dc.font.height + 2; | |
+} | |
+ | |
+static Bool | |
+isarrange(void (*func)()) | |
+{ | |
+ return func == layouts[ltidx].arrange; | |
+} | |
+ | |
static Bool | |
isfloating(void) { | |
return layouts[ltidx].arrange == floating; | |
} | |
static Bool | |
+isoccupied(unsigned int t) { | |
+ Client *c; | |
+ | |
+ for(c = clients; c; c = c->next) | |
+ if(c->tags[t]) | |
+ return True; | |
+ return False; | |
+} | |
+ | |
+static Bool | |
+isprotodel(Client *c) { | |
+ int i, n; | |
+ Atom *protocols; | |
+ Bool ret = False; | |
+ | |
+ if(XGetWMProtocols(dpy, c->win, &protocols, &n)) { | |
+ for(i = 0; !ret && i < n; i++) | |
+ if(protocols[i] == wmatom[WMDelete]) | |
+ ret = True; | |
+ XFree(protocols); | |
+ } | |
+ return ret; | |
+} | |
+ | |
+static Bool | |
isvisible(Client *c) { | |
unsigned int i; | |
t@@ -1335,176 +964,360 @@ isvisible(Client *c) { | |
} | |
static void | |
-restack(void) { | |
- Client *c; | |
+keypress(XEvent *e) { | |
+ KEYS | |
+ unsigned int len = sizeof keys / sizeof keys[0]; | |
+ unsigned int i; | |
+ KeyCode code; | |
+ KeySym keysym; | |
+ XKeyEvent *ev; | |
+ | |
+ if(!e) { /* grabkeys */ | |
+ XUngrabKey(dpy, AnyKey, AnyModifier, root); | |
+ for(i = 0; i < len; i++) { | |
+ code = XKeysymToKeycode(dpy, keys[i].keysym); | |
+ XGrabKey(dpy, code, keys[i].mod, root, True, | |
+ GrabModeAsync, GrabModeAsync); | |
+ XGrabKey(dpy, code, keys[i].mod | LockMask, root, True, | |
+ GrabModeAsync, GrabModeAsync); | |
+ XGrabKey(dpy, code, keys[i].mod | numlockmask, root, T… | |
+ GrabModeAsync, GrabModeAsync); | |
+ XGrabKey(dpy, code, keys[i].mod | numlockmask | LockMa… | |
+ GrabModeAsync, GrabModeAsync); | |
+ } | |
+ return; | |
+ } | |
+ ev = &e->xkey; | |
+ keysym = XKeycodeToKeysym(dpy, (KeyCode)ev->keycode, 0); | |
+ for(i = 0; i < len; i++) | |
+ if(keysym == keys[i].keysym | |
+ && CLEANMASK(keys[i].mod) == CLEANMASK(ev->state)) | |
+ { | |
+ if(keys[i].func) | |
+ keys[i].func(keys[i].arg); | |
+ } | |
+} | |
+ | |
+static void | |
+killclient(const char *arg) { | |
XEvent ev; | |
- XWindowChanges wc; | |
- drawbar(); | |
if(!sel) | |
return; | |
- if(sel->isfloating || isfloating()) | |
- XRaiseWindow(dpy, sel->win); | |
- if(!isfloating()) { | |
- wc.stack_mode = Below; | |
- wc.sibling = barwin; | |
- if(!sel->isfloating) { | |
- XConfigureWindow(dpy, sel->win, CWSibling | 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; | |
- } | |
+ if(isprotodel(sel)) { | |
+ ev.type = ClientMessage; | |
+ ev.xclient.window = sel->win; | |
+ ev.xclient.message_type = wmatom[WMProtocols]; | |
+ ev.xclient.format = 32; | |
+ ev.xclient.data.l[0] = wmatom[WMDelete]; | |
+ ev.xclient.data.l[1] = CurrentTime; | |
+ XSendEvent(dpy, sel->win, False, NoEventMask, &ev); | |
} | |
- XSync(dpy, False); | |
- while(XCheckMaskEvent(dpy, EnterWindowMask, &ev)); | |
+ else | |
+ XKillClient(dpy, sel->win); | |
} | |
static void | |
-setlayout(const char *arg) { | |
+leavenotify(XEvent *e) { | |
+ XCrossingEvent *ev = &e->xcrossing; | |
+ | |
+ if((ev->window == root) && !ev->same_screen) { | |
+ selscreen = False; | |
+ focus(NULL); | |
+ } | |
+} | |
+ | |
+static void | |
+manage(Window w, XWindowAttributes *wa) { | |
unsigned int i; | |
+ Client *c, *t = NULL; | |
+ Window trans; | |
+ Status rettrans; | |
+ XWindowChanges wc; | |
- if(!arg) { | |
- if(++ltidx == nlayouts) | |
- ltidx = 0;; | |
+ c = emallocz(sizeof(Client)); | |
+ c->tags = emallocz(ntags * sizeof(Bool)); | |
+ c->win = w; | |
+ c->x = wa->x; | |
+ c->y = wa->y; | |
+ c->w = wa->width; | |
+ c->h = wa->height; | |
+ c->oldborder = wa->border_width; | |
+ if(c->w == sw && c->h == sh) { | |
+ c->x = sx; | |
+ c->y = sy; | |
+ c->border = wa->border_width; | |
} | |
else { | |
- for(i = 0; i < nlayouts; i++) | |
- if(!strcmp(arg, layouts[i].symbol)) | |
- break; | |
- if(i == nlayouts) | |
- return; | |
- ltidx = i; | |
+ if(c->x + c->w + 2 * c->border > wax + waw) | |
+ c->x = wax + waw - c->w - 2 * c->border; | |
+ if(c->y + c->h + 2 * c->border > way + wah) | |
+ c->y = way + wah - c->h - 2 * c->border; | |
+ if(c->x < wax) | |
+ c->x = wax; | |
+ if(c->y < way) | |
+ c->y = way; | |
+ c->border = BORDERPX; | |
} | |
- if(sel) | |
- arrange(); | |
- else | |
- drawbar(); | |
+ wc.border_width = c->border; | |
+ XConfigureWindow(dpy, w, CWBorderWidth, &wc); | |
+ XSetWindowBorder(dpy, w, dc.norm[ColBorder]); | |
+ configure(c); /* propagates border_width, if size doesn't change */ | |
+ updatesizehints(c); | |
+ XSelectInput(dpy, w, | |
+ StructureNotifyMask | PropertyChangeMask | EnterWindowMask); | |
+ grabbuttons(c, False); | |
+ updatetitle(c); | |
+ if((rettrans = XGetTransientForHint(dpy, w, &trans) == Success)) | |
+ for(t = clients; t && t->win != trans; t = t->next); | |
+ if(t) | |
+ for(i = 0; i < ntags; i++) | |
+ c->tags[i] = t->tags[i]; | |
+ applyrules(c); | |
+ if(!c->isfloating) | |
+ c->isfloating = (rettrans == Success) || c->isfixed; | |
+ attach(c); | |
+ attachstack(c); | |
+ XMoveResizeWindow(dpy, c->win, c->x, c->y, c->w, c->h); /* some window… | |
+ ban(c); | |
+ XMapWindow(dpy, c->win); | |
+ setclientstate(c, NormalState); | |
+ arrange(); | |
} | |
static void | |
-tag(const char *arg) { | |
- unsigned int i; | |
+mappingnotify(XEvent *e) { | |
+ XMappingEvent *ev = &e->xmapping; | |
- if(!sel) | |
- return; | |
- for(i = 0; i < ntags; i++) | |
- sel->tags[i] = arg == NULL; | |
- i = idxoftag(arg); | |
- if(i >= 0 && i < ntags) | |
- sel->tags[i] = True; | |
- arrange(); | |
+ XRefreshKeyboardMapping(ev); | |
+ if(ev->request == MappingKeyboard) | |
+ keypress(NULL); | |
} | |
static void | |
-togglefloating(const char *arg) { | |
- if(!sel) | |
+maprequest(XEvent *e) { | |
+ static XWindowAttributes wa; | |
+ XMapRequestEvent *ev = &e->xmaprequest; | |
+ | |
+ if(!XGetWindowAttributes(dpy, ev->window, &wa)) | |
return; | |
- sel->isfloating = !sel->isfloating; | |
- if(sel->isfloating) | |
- resize(sel, sel->x, sel->y, sel->w, sel->h, True); | |
- arrange(); | |
+ if(wa.override_redirect) | |
+ return; | |
+ if(!getclient(ev->window)) | |
+ manage(ev->window, &wa); | |
} | |
static void | |
-togglemax(const char *arg) { | |
+movemouse(Client *c) { | |
+ int x1, y1, ocx, ocy, di, nx, ny; | |
+ unsigned int dui; | |
+ Window dummy; | |
XEvent ev; | |
- if(!sel || (!isfloating() && !sel->isfloating) || sel->isfixed) | |
+ ocx = nx = c->x; | |
+ ocy = ny = c->y; | |
+ if(XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAs… | |
+ None, cursor[CurMove], CurrentTime) != GrabSuccess) | |
return; | |
- if((sel->ismax = !sel->ismax)) { | |
- sel->rx = sel->x; | |
- sel->ry = sel->y; | |
- sel->rw = sel->w; | |
- sel->rh = sel->h; | |
- resize(sel, wax, way, waw - 2 * sel->border, wah - 2 * sel->bo… | |
+ 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; | |
+ } | |
} | |
- else | |
- resize(sel, sel->rx, sel->ry, sel->rw, sel->rh, True); | |
- drawbar(); | |
- while(XCheckMaskEvent(dpy, EnterWindowMask, &ev)); | |
+} | |
+ | |
+static Client * | |
+nexttiled(Client *c) { | |
+ for(; c && (c->isfloating || !isvisible(c)); c = c->next); | |
+ return c; | |
} | |
static void | |
-toggletag(const char *arg) { | |
- unsigned int i, j; | |
+propertynotify(XEvent *e) { | |
+ Client *c; | |
+ Window trans; | |
+ XPropertyEvent *ev = &e->xproperty; | |
- if(!sel) | |
- return; | |
- i = idxoftag(arg); | |
- sel->tags[i] = !sel->tags[i]; | |
- for(j = 0; j < ntags && !sel->tags[j]; j++); | |
- if(j == ntags) | |
- sel->tags[i] = True; | |
- arrange(); | |
+ if(ev->state == PropertyDelete) | |
+ return; /* ignore */ | |
+ if((c = getclient(ev->window))) { | |
+ switch (ev->atom) { | |
+ default: break; | |
+ case XA_WM_TRANSIENT_FOR: | |
+ XGetTransientForHint(dpy, c->win, &trans); | |
+ if(!c->isfloating && (c->isfloating = (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 | |
-toggleview(const char *arg) { | |
- unsigned int i, j; | |
- | |
- i = idxoftag(arg); | |
- seltags[i] = !seltags[i]; | |
- for(j = 0; j < ntags && !seltags[j]; j++); | |
- if(j == ntags) | |
- seltags[i] = True; /* cannot toggle last view */ | |
- arrange(); | |
+quit(const char *arg) { | |
+ readin = running = False; | |
} | |
static void | |
-view(const char *arg) { | |
- unsigned int i; | |
+resize(Client *c, int x, int y, int w, int h, Bool sizehints) { | |
+ double dx, dy, max, min, ratio; | |
+ XWindowChanges wc; | |
- for(i = 0; i < ntags; i++) | |
- seltags[i] = arg == NULL; | |
- i = idxoftag(arg); | |
- if(i >= 0 && i < ntags) | |
- seltags[i] = True; | |
- arrange(); | |
+ if(sizehints) { | |
+ if(c->minay > 0 && c->maxay > 0 && (h - c->baseh) > 0 && (w - … | |
+ 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 | |
-cleanup(void) { | |
- close(STDIN_FILENO); | |
- while(stack) { | |
- unban(stack); | |
- unmanage(stack); | |
- } | |
- if(dc.font.set) | |
- XFreeFontSet(dpy, dc.font.set); | |
- else | |
- XFreeFont(dpy, dc.font.xfont); | |
- XUngrabKey(dpy, AnyKey, AnyModifier, root); | |
- XFreePixmap(dpy, dc.drawable); | |
- XFreeGC(dpy, dc.gc); | |
- XDestroyWindow(dpy, barwin); | |
- XFreeCursor(dpy, cursor[CurNormal]); | |
- XFreeCursor(dpy, cursor[CurResize]); | |
- XFreeCursor(dpy, cursor[CurMove]); | |
- XSetInputFocus(dpy, PointerRoot, RevertToPointerRoot, CurrentTime); | |
- XSync(dpy, False); | |
- free(seltags); | |
+resizemouse(Client *c) { | |
+ int ocx, ocy; | |
+ int nw, nh; | |
+ XEvent ev; | |
+ | |
+ ocx = c->x; | |
+ ocy = c->y; | |
+ if(XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, 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 long | |
-getstate(Window w) { | |
- int format, status; | |
- long result = -1; | |
- unsigned char *p = NULL; | |
- unsigned long n, extra; | |
- Atom real; | |
+static void | |
+restack(void) { | |
+ Client *c; | |
+ XEvent ev; | |
+ XWindowChanges wc; | |
- status = XGetWindowProperty(dpy, w, wmatom[WMState], 0L, 2L, False, wm… | |
- &real, &format, &n, &extra, (unsigned char **)&p); | |
- if(status != Success) | |
- return -1; | |
- if(n != 0) | |
- result = *p; | |
- XFree(p); | |
- return result; | |
+ drawbar(); | |
+ if(!sel) | |
+ return; | |
+ if(sel->isfloating || isfloating()) | |
+ XRaiseWindow(dpy, sel->win); | |
+ if(!isfloating()) { | |
+ wc.stack_mode = Below; | |
+ wc.sibling = barwin; | |
+ if(!sel->isfloating) { | |
+ XConfigureWindow(dpy, sel->win, CWSibling | 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 | |
t@@ -1535,6 +1348,58 @@ scan(void) { | |
} | |
static void | |
+setclientstate(Client *c, long state) { | |
+ long data[] = {state, None}; | |
+ | |
+ XChangeProperty(dpy, c->win, wmatom[WMState], wmatom[WMState], 32, | |
+ PropModeReplace, (unsigned char *)data, 2); | |
+} | |
+ | |
+static void | |
+setlayout(const char *arg) { | |
+ unsigned int i; | |
+ | |
+ if(!arg) { | |
+ if(++ltidx == nlayouts) | |
+ ltidx = 0;; | |
+ } | |
+ else { | |
+ for(i = 0; i < nlayouts; i++) | |
+ if(!strcmp(arg, layouts[i].symbol)) | |
+ break; | |
+ if(i == nlayouts) | |
+ return; | |
+ ltidx = i; | |
+ } | |
+ if(sel) | |
+ arrange(); | |
+ else | |
+ drawbar(); | |
+} | |
+ | |
+static void | |
+setmwfact(const char *arg) { | |
+ double delta; | |
+ | |
+ if(!isarrange(tile)) | |
+ return; | |
+ /* arg handling, manipulate mwfact */ | |
+ if(arg == NULL) | |
+ mwfact = MWFACT; | |
+ else if(1 == sscanf(arg, "%lf", &delta)) { | |
+ if(arg[0] != '+' && arg[0] != '-') | |
+ mwfact = delta; | |
+ else | |
+ mwfact += delta; | |
+ if(mwfact < 0.1) | |
+ mwfact = 0.1; | |
+ else if(mwfact > 0.9) | |
+ mwfact = 0.9; | |
+ } | |
+ arrange(); | |
+} | |
+ | |
+static void | |
setup(void) { | |
int i, j; | |
unsigned int mask; | |
t@@ -1586,158 +1451,58 @@ setup(void) { | |
selscreen = XQueryPointer(dpy, root, &w, &w, &i, &i, &i, &i, &mask); | |
} | |
-/* | |
- * Startup Error handler to check if another window manager | |
- * is already running. | |
- */ | |
-static int | |
-xerrorstart(Display *dsply, XErrorEvent *ee) { | |
- otherwm = True; | |
- return -1; | |
-} | |
- | |
-static Bool | |
-gettextprop(Window w, Atom atom, char *text, unsigned int size) { | |
- char **list = NULL; | |
- int n; | |
- XTextProperty name; | |
+static void | |
+spawn(const char *arg) { | |
+ static char *shell = NULL; | |
- if(!text || size == 0) | |
- return False; | |
- text[0] = '\0'; | |
- XGetTextProperty(dpy, w, &name, atom); | |
- if(!name.nitems) | |
- return False; | |
- if(name.encoding == XA_STRING) | |
- strncpy(text, (char *)name.value, size - 1); | |
- else { | |
- if(XmbTextPropertyToTextList(dpy, &name, &list, &n) >= Success | |
- && n > 0 && *list) | |
- { | |
- strncpy(text, *list, size - 1); | |
- XFreeStringList(list); | |
+ if(!shell && !(shell = getenv("SHELL"))) | |
+ shell = "/bin/sh"; | |
+ if(!arg) | |
+ return; | |
+ /* The double-fork construct avoids zombie processes and keeps the code | |
+ * clean from stupid signal handlers. */ | |
+ if(fork() == 0) { | |
+ if(fork() == 0) { | |
+ if(dpy) | |
+ close(ConnectionNumber(dpy)); | |
+ setsid(); | |
+ execl(shell, shell, "-c", arg, (char *)NULL); | |
+ fprintf(stderr, "dwm: execl '%s -c %s'", shell, arg); | |
+ perror(" failed"); | |
} | |
+ exit(0); | |
} | |
- text[size - 1] = '\0'; | |
- XFree(name.value); | |
- return True; | |
-} | |
- | |
-static void | |
-quit(const char *arg) { | |
- readin = running = False; | |
-} | |
- | |
-/* There's no way to check accesses to destroyed windows, thus those cases are | |
- * ignored (especially on UnmapNotify's). Other types of errors call Xlibs | |
- * default error handler, which may call exit. | |
- */ | |
-static int | |
-xerror(Display *dpy, XErrorEvent *ee) { | |
- if(ee->error_code == BadWindow | |
- || (ee->request_code == X_SetInputFocus && ee->error_code == BadMatch) | |
- || (ee->request_code == X_PolyText8 && ee->error_code == BadDrawable) | |
- || (ee->request_code == X_PolyFillRectangle && ee->error_code == 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; | |
+ wait(0); | |
} | |
static void | |
-detach(Client *c) { | |
- if(c->prev) | |
- c->prev->next = c->next; | |
- if(c->next) | |
- c->next->prev = c->prev; | |
- if(c == clients) | |
- clients = c->next; | |
- c->next = c->prev = NULL; | |
-} | |
+tag(const char *arg) { | |
+ unsigned int i; | |
-static void | |
-focus(Client *c) { | |
- if((!c && selscreen) || (c && !isvisible(c))) | |
- for(c = stack; c && !isvisible(c); c = c->snext); | |
- if(sel && sel != c) { | |
- grabbuttons(sel, False); | |
- XSetWindowBorder(dpy, sel->win, dc.norm[ColBorder]); | |
- } | |
- if(c) { | |
- detachstack(c); | |
- attachstack(c); | |
- grabbuttons(c, True); | |
- } | |
- sel = c; | |
- drawbar(); | |
- if(!selscreen) | |
+ if(!sel) | |
return; | |
- if(c) { | |
- XSetWindowBorder(dpy, c->win, dc.sel[ColBorder]); | |
- XSetInputFocus(dpy, c->win, RevertToPointerRoot, CurrentTime); | |
- } | |
- else | |
- XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime); | |
-} | |
- | |
-static Bool | |
-isarrange(void (*func)()) | |
-{ | |
- return func == layouts[ltidx].arrange; | |
-} | |
- | |
-static Client * | |
-nexttiled(Client *c) { | |
- for(; c && (c->isfloating || !isvisible(c)); c = c->next); | |
- return c; | |
+ for(i = 0; i < ntags; i++) | |
+ sel->tags[i] = arg == NULL; | |
+ i = idxoftag(arg); | |
+ if(i >= 0 && i < ntags) | |
+ sel->tags[i] = True; | |
+ arrange(); | |
} | |
-static void | |
-setmwfact(const char *arg) { | |
- double delta; | |
+static unsigned int | |
+textnw(const char *text, unsigned int len) { | |
+ XRectangle r; | |
- if(!isarrange(tile)) | |
- return; | |
- /* arg handling, manipulate mwfact */ | |
- if(arg == NULL) | |
- mwfact = MWFACT; | |
- else if(1 == sscanf(arg, "%lf", &delta)) { | |
- if(arg[0] != '+' && arg[0] != '-') | |
- mwfact = delta; | |
- else | |
- mwfact += delta; | |
- if(mwfact < 0.1) | |
- mwfact = 0.1; | |
- else if(mwfact > 0.9) | |
- mwfact = 0.9; | |
+ if(dc.font.set) { | |
+ XmbTextExtents(dc.font.set, text, len, NULL, &r); | |
+ return r.width; | |
} | |
- arrange(); | |
+ return XTextWidth(dc.font.xfont, text, len); | |
+} | |
+ | |
+static unsigned int | |
+textw(const char *text) { | |
+ return textnw(text, strlen(text)) + dc.font.height; | |
} | |
static void | |
t@@ -1780,6 +1545,239 @@ tile(void) { | |
} | |
static void | |
+togglebar(const char *arg) { | |
+ if(bpos == BarOff) | |
+ bpos = (BARPOS == BarOff) ? BarTop : BARPOS; | |
+ else | |
+ bpos = BarOff; | |
+ updatebarpos(); | |
+ arrange(); | |
+} | |
+ | |
+static void | |
+togglefloating(const char *arg) { | |
+ if(!sel) | |
+ return; | |
+ sel->isfloating = !sel->isfloating; | |
+ if(sel->isfloating) | |
+ resize(sel, sel->x, sel->y, sel->w, sel->h, True); | |
+ arrange(); | |
+} | |
+ | |
+static void | |
+togglemax(const char *arg) { | |
+ XEvent ev; | |
+ | |
+ if(!sel || (!isfloating() && !sel->isfloating) || sel->isfixed) | |
+ return; | |
+ if((sel->ismax = !sel->ismax)) { | |
+ sel->rx = sel->x; | |
+ sel->ry = sel->y; | |
+ sel->rw = sel->w; | |
+ sel->rh = sel->h; | |
+ resize(sel, wax, way, waw - 2 * sel->border, wah - 2 * sel->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 | |
+unban(Client *c) { | |
+ if(!c->isbanned) | |
+ return; | |
+ XMoveWindow(dpy, c->win, c->x, c->y); | |
+ c->isbanned = False; | |
+} | |
+ | |
+static void | |
+unmanage(Client *c) { | |
+ XWindowChanges wc; | |
+ | |
+ wc.border_width = c->oldborder; | |
+ /* The server grab construct avoids race conditions. */ | |
+ XGrabServer(dpy); | |
+ XSetErrorHandler(xerrordummy); | |
+ XConfigureWindow(dpy, c->win, CWBorderWidth, &wc); /* restore border */ | |
+ detach(c); | |
+ detachstack(c); | |
+ if(sel == c) | |
+ focus(NULL); | |
+ XUngrabButton(dpy, AnyButton, AnyModifier, c->win); | |
+ setclientstate(c, WithdrawnState); | |
+ free(c->tags); | |
+ free(c); | |
+ XSync(dpy, False); | |
+ XSetErrorHandler(xerror); | |
+ XUngrabServer(dpy); | |
+ arrange(); | |
+} | |
+ | |
+static void | |
+unmapnotify(XEvent *e) { | |
+ Client *c; | |
+ XUnmapEvent *ev = &e->xunmap; | |
+ | |
+ if((c = getclient(ev->window))) | |
+ unmanage(c); | |
+} | |
+ | |
+static void | |
+updatebarpos(void) { | |
+ XEvent ev; | |
+ | |
+ wax = sx; | |
+ way = sy; | |
+ wah = sh; | |
+ waw = sw; | |
+ switch(bpos) { | |
+ default: | |
+ wah -= bh; | |
+ way += bh; | |
+ XMoveWindow(dpy, barwin, sx, sy); | |
+ break; | |
+ case BarBot: | |
+ wah -= bh; | |
+ XMoveWindow(dpy, barwin, sx, sy + wah); | |
+ break; | |
+ case BarOff: | |
+ XMoveWindow(dpy, barwin, sx, sy - bh); | |
+ break; | |
+ } | |
+ XSync(dpy, False); | |
+ while(XCheckMaskEvent(dpy, EnterWindowMask, &ev)); | |
+} | |
+ | |
+static void | |
+updatesizehints(Client *c) { | |
+ long msize; | |
+ XSizeHints size; | |
+ | |
+ if(!XGetWMNormalHints(dpy, c->win, &size, &msize) || !size.flags) | |
+ size.flags = PSize; | |
+ c->flags = size.flags; | |
+ if(c->flags & PBaseSize) { | |
+ c->basew = size.base_width; | |
+ c->baseh = size.base_height; | |
+ } | |
+ else if(c->flags & PMinSize) { | |
+ c->basew = size.min_width; | |
+ c->baseh = size.min_height; | |
+ } | |
+ else | |
+ c->basew = c->baseh = 0; | |
+ if(c->flags & PResizeInc) { | |
+ c->incw = size.width_inc; | |
+ c->inch = size.height_inc; | |
+ } | |
+ else | |
+ c->incw = c->inch = 0; | |
+ if(c->flags & PMaxSize) { | |
+ c->maxw = size.max_width; | |
+ c->maxh = size.max_height; | |
+ } | |
+ else | |
+ c->maxw = c->maxh = 0; | |
+ if(c->flags & PMinSize) { | |
+ c->minw = size.min_width; | |
+ c->minh = size.min_height; | |
+ } | |
+ else if(c->flags & PBaseSize) { | |
+ c->minw = size.base_width; | |
+ c->minh = size.base_height; | |
+ } | |
+ else | |
+ c->minw = c->minh = 0; | |
+ if(c->flags & PAspect) { | |
+ c->minax = size.min_aspect.x; | |
+ c->maxax = size.max_aspect.x; | |
+ c->minay = size.min_aspect.y; | |
+ c->maxay = size.max_aspect.y; | |
+ } | |
+ else | |
+ c->minax = c->maxax = c->minay = c->maxay = 0; | |
+ c->isfixed = (c->maxw && c->minw && c->maxh && c->minh | |
+ && c->maxw == c->minw && c->maxh == c->minh); | |
+} | |
+ | |
+static void | |
+updatetitle(Client *c) { | |
+ if(!gettextprop(c->win, netatom[NetWMName], c->name, sizeof c->name)) | |
+ gettextprop(c->win, wmatom[WMName], c->name, sizeof c->name); | |
+} | |
+ | |
+/* There's no way to check accesses to destroyed windows, thus those cases are | |
+ * ignored (especially on UnmapNotify's). Other types of errors call Xlibs | |
+ * default error handler, which may call exit. */ | |
+static int | |
+xerror(Display *dpy, XErrorEvent *ee) { | |
+ if(ee->error_code == BadWindow | |
+ || (ee->request_code == X_SetInputFocus && ee->error_code == BadMatch) | |
+ || (ee->request_code == X_PolyText8 && ee->error_code == BadDrawable) | |
+ || (ee->request_code == X_PolyFillRectangle && ee->error_code == 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 int | |
+xerrordummy(Display *dsply, XErrorEvent *ee) { | |
+ return 0; | |
+} | |
+ | |
+/* Startup Error handler to check if another window manager | |
+ * is already running. */ | |
+static int | |
+xerrorstart(Display *dsply, XErrorEvent *ee) { | |
+ otherwm = True; | |
+ return -1; | |
+} | |
+ | |
+static void | |
+view(const char *arg) { | |
+ unsigned int i; | |
+ | |
+ for(i = 0; i < ntags; i++) | |
+ seltags[i] = arg == NULL; | |
+ i = idxoftag(arg); | |
+ if(i >= 0 && i < ntags) | |
+ seltags[i] = True; | |
+ arrange(); | |
+} | |
+ | |
+static void | |
zoom(const char *arg) { | |
Client *c; | |