ordered all functions alphabetically 49197fe4
Anselm R. Garbe · 2007-09-16 11:53 1 file(s) · +1023 −1025
dwm.c +1023 −1025
233 233
static Window barwin, root;
234 234
static Regs *regs = NULL;
235 235
236 -
/* configuration, allows nested code to work on above variables */
236 +
/* configuration, allows nested code to access above variables */
237 237
#include "config.h"
238 238
239 +
/* implementation */
239 240
static void
240 -
eprint(const char *errstr, ...) {
241 -
	va_list ap;
241 +
applyrules(Client *c) {
242 +
	static char buf[512];
243 +
	unsigned int i, j;
244 +
	regmatch_t tmp;
245 +
	Bool matched = False;
246 +
	XClassHint ch = { 0 };
242 247
243 -
	va_start(ap, errstr);
244 -
	vfprintf(stderr, errstr, ap);
245 -
	va_end(ap);
246 -
	exit(EXIT_FAILURE);
248 +
	/* rule matching */
249 +
	XGetClassHint(dpy, c->win, &ch);
250 +
	snprintf(buf, sizeof buf, "%s:%s:%s",
251 +
			ch.res_class ? ch.res_class : "",
252 +
			ch.res_name ? ch.res_name : "", c->name);
253 +
	for(i = 0; i < nrules; i++)
254 +
		if(regs[i].propregex && !regexec(regs[i].propregex, buf, 1, &tmp, 0)) {
255 +
			c->isfloating = rules[i].isfloating;
256 +
			for(j = 0; regs[i].tagregex && j < ntags; j++) {
257 +
				if(!regexec(regs[i].tagregex, tags[j], 1, &tmp, 0)) {
258 +
					matched = True;
259 +
					c->tags[j] = True;
260 +
				}
261 +
			}
262 +
		}
263 +
	if(ch.res_class)
264 +
		XFree(ch.res_class);
265 +
	if(ch.res_name)
266 +
		XFree(ch.res_name);
267 +
	if(!matched)
268 +
		for(i = 0; i < ntags; i++)
269 +
			c->tags[i] = seltags[i];
247 270
}
248 271
249 -
static void *
250 -
emallocz(unsigned int size) {
251 -
	void *res = calloc(1, size);
272 +
static void
273 +
arrange(void) {
274 +
	Client *c;
252 275
253 -
	if(!res)
254 -
		eprint("fatal: could not malloc() %u bytes\n", size);
255 -
	return res;
276 +
	for(c = clients; c; c = c->next)
277 +
		if(isvisible(c))
278 +
			unban(c);
279 +
		else
280 +
			ban(c);
281 +
	layouts[ltidx].arrange();
282 +
	focus(NULL);
283 +
	restack();
256 284
}
257 285
258 286
static void
259 -
spawn(const char *arg) {
260 -
	static char *shell = NULL;
287 +
attach(Client *c) {
288 +
	if(clients)
289 +
		clients->prev = c;
290 +
	c->next = clients;
291 +
	clients = c;
292 +
}
261 293
262 -
	if(!shell && !(shell = getenv("SHELL")))
263 -
		shell = "/bin/sh";
264 -
	if(!arg)
294 +
static void
295 +
attachstack(Client *c) {
296 +
	c->snext = stack;
297 +
	stack = c;
298 +
}
299 +
300 +
static void
301 +
ban(Client *c) {
302 +
	if(c->isbanned)
265 303
		return;
266 -
	/* The double-fork construct avoids zombie processes and keeps the code
267 -
	 * clean from stupid signal handlers. */
268 -
	if(fork() == 0) {
269 -
		if(fork() == 0) {
270 -
			if(dpy)
271 -
				close(ConnectionNumber(dpy));
272 -
			setsid();
273 -
			execl(shell, shell, "-c", arg, (char *)NULL);
274 -
			fprintf(stderr, "dwm: execl '%s -c %s'", shell, arg);
275 -
			perror(" failed");
304 +
	XMoveWindow(dpy, c->win, c->x + 2 * sw, c->y);
305 +
	c->isbanned = True;
306 +
}
307 +
308 +
static void
309 +
buttonpress(XEvent *e) {
310 +
	unsigned int i, x;
311 +
	Client *c;
312 +
	XButtonPressedEvent *ev = &e->xbutton;
313 +
314 +
	if(barwin == ev->window) {
315 +
		x = 0;
316 +
		for(i = 0; i < ntags; i++) {
317 +
			x += textw(tags[i]);
318 +
			if(ev->x < x) {
319 +
				if(ev->button == Button1) {
320 +
					if(ev->state & MODKEY)
321 +
						tag(tags[i]);
322 +
					else
323 +
						view(tags[i]);
324 +
				}
325 +
				else if(ev->button == Button3) {
326 +
					if(ev->state & MODKEY)
327 +
						toggletag(tags[i]);
328 +
					else
329 +
						toggleview(tags[i]);
330 +
				}
331 +
				return;
332 +
			}
276 333
		}
277 -
		exit(0);
334 +
		if((ev->x < x + blw) && ev->button == Button1)
335 +
			setlayout(NULL);
278 336
	}
279 -
	wait(0);
337 +
	else if((c = getclient(ev->window))) {
338 +
		focus(c);
339 +
		if(CLEANMASK(ev->state) != MODKEY)
340 +
			return;
341 +
		if(ev->button == Button1 && (isfloating() || c->isfloating)) {
342 +
			restack();
343 +
			movemouse(c);
344 +
		}
345 +
		else if(ev->button == Button2)
346 +
			zoom(NULL);
347 +
		else if(ev->button == Button3
348 +
		&& (isfloating() || c->isfloating) && !c->isfixed)
349 +
		{
350 +
			restack();
351 +
			resizemouse(c);
352 +
		}
353 +
	}
280 354
}
281 355
282 356
static void
283 -
drawsquare(Bool filled, Bool empty, unsigned long col[ColLast]) {
284 -
	int x;
285 -
	XGCValues gcv;
286 -
	XRectangle r = { dc.x, dc.y, dc.w, dc.h };
287 -
288 -
	gcv.foreground = col[ColFG];
289 -
	XChangeGC(dpy, dc.gc, GCForeground, &gcv);
290 -
	x = (dc.font.ascent + dc.font.descent + 2) / 4;
291 -
	r.x = dc.x + 1;
292 -
	r.y = dc.y + 1;
293 -
	if(filled) {
294 -
		r.width = r.height = x + 1;
295 -
		XFillRectangles(dpy, dc.drawable, dc.gc, &r, 1);
357 +
cleanup(void) {
358 +
	close(STDIN_FILENO);
359 +
	while(stack) {
360 +
		unban(stack);
361 +
		unmanage(stack);
296 362
	}
297 -
	else if(empty) {
298 -
		r.width = r.height = x;
299 -
		XDrawRectangles(dpy, dc.drawable, dc.gc, &r, 1);
363 +
	if(dc.font.set)
364 +
		XFreeFontSet(dpy, dc.font.set);
365 +
	else
366 +
		XFreeFont(dpy, dc.font.xfont);
367 +
	XUngrabKey(dpy, AnyKey, AnyModifier, root);
368 +
	XFreePixmap(dpy, dc.drawable);
369 +
	XFreeGC(dpy, dc.gc);
370 +
	XDestroyWindow(dpy, barwin);
371 +
	XFreeCursor(dpy, cursor[CurNormal]);
372 +
	XFreeCursor(dpy, cursor[CurResize]);
373 +
	XFreeCursor(dpy, cursor[CurMove]);
374 +
	XSetInputFocus(dpy, PointerRoot, RevertToPointerRoot, CurrentTime);
375 +
	XSync(dpy, False);
376 +
	free(seltags);
377 +
}
378 +
379 +
static void
380 +
compileregs(void) {
381 +
	unsigned int i;
382 +
	regex_t *reg;
383 +
384 +
	if(regs)
385 +
		return;
386 +
	nrules = sizeof rules / sizeof rules[0];
387 +
	regs = emallocz(nrules * sizeof(Regs));
388 +
	for(i = 0; i < nrules; i++) {
389 +
		if(rules[i].prop) {
390 +
			reg = emallocz(sizeof(regex_t));
391 +
			if(regcomp(reg, rules[i].prop, REG_EXTENDED))
392 +
				free(reg);
393 +
			else
394 +
				regs[i].propregex = reg;
395 +
		}
396 +
		if(rules[i].tags) {
397 +
			reg = emallocz(sizeof(regex_t));
398 +
			if(regcomp(reg, rules[i].tags, REG_EXTENDED))
399 +
				free(reg);
400 +
			else
401 +
				regs[i].tagregex = reg;
402 +
		}
300 403
	}
301 404
}
302 405
303 -
static unsigned long
304 -
initcolor(const char *colstr) {
305 -
	Colormap cmap = DefaultColormap(dpy, screen);
306 -
	XColor color;
406 +
static void
407 +
configure(Client *c) {
408 +
	XConfigureEvent ce;
307 409
308 -
	if(!XAllocNamedColor(dpy, cmap, colstr, &color, &color))
309 -
		eprint("error, cannot allocate color '%s'\n", colstr);
310 -
	return color.pixel;
410 +
	ce.type = ConfigureNotify;
411 +
	ce.display = dpy;
412 +
	ce.event = c->win;
413 +
	ce.window = c->win;
414 +
	ce.x = c->x;
415 +
	ce.y = c->y;
416 +
	ce.width = c->w;
417 +
	ce.height = c->h;
418 +
	ce.border_width = c->border;
419 +
	ce.above = None;
420 +
	ce.override_redirect = False;
421 +
	XSendEvent(dpy, c->win, False, StructureNotifyMask, (XEvent *)&ce);
311 422
}
312 423
313 424
static void
314 -
initfont(const char *fontstr) {
315 -
	char *def, **missing;
316 -
	int i, n;
425 +
configurenotify(XEvent *e) {
426 +
	XConfigureEvent *ev = &e->xconfigure;
317 427
318 -
	missing = NULL;
319 -
	if(dc.font.set)
320 -
		XFreeFontSet(dpy, dc.font.set);
321 -
	dc.font.set = XCreateFontSet(dpy, fontstr, &missing, &n, &def);
322 -
	if(missing) {
323 -
		while(n--)
324 -
			fprintf(stderr, "dwm: missing fontset: %s\n", missing[n]);
325 -
		XFreeStringList(missing);
428 +
	if (ev->window == root && (ev->width != sw || ev->height != sh)) {
429 +
		sw = ev->width;
430 +
		sh = ev->height;
431 +
		XFreePixmap(dpy, dc.drawable);
432 +
		dc.drawable = XCreatePixmap(dpy, root, sw, bh, DefaultDepth(dpy, screen));
433 +
		XResizeWindow(dpy, barwin, sw, bh);
434 +
		updatebarpos();
435 +
		arrange();
326 436
	}
327 -
	if(dc.font.set) {
328 -
		XFontSetExtents *font_extents;
329 -
		XFontStruct **xfonts;
330 -
		char **font_names;
331 -
		dc.font.ascent = dc.font.descent = 0;
332 -
		font_extents = XExtentsOfFontSet(dc.font.set);
333 -
		n = XFontsOfFontSet(dc.font.set, &xfonts, &font_names);
334 -
		for(i = 0, dc.font.ascent = 0, dc.font.descent = 0; i < n; i++) {
335 -
			if(dc.font.ascent < (*xfonts)->ascent)
336 -
				dc.font.ascent = (*xfonts)->ascent;
337 -
			if(dc.font.descent < (*xfonts)->descent)
338 -
				dc.font.descent = (*xfonts)->descent;
339 -
			xfonts++;
437 +
}
438 +
439 +
static void
440 +
configurerequest(XEvent *e) {
441 +
	Client *c;
442 +
	XConfigureRequestEvent *ev = &e->xconfigurerequest;
443 +
	XWindowChanges wc;
444 +
445 +
	if((c = getclient(ev->window))) {
446 +
		c->ismax = False;
447 +
		if(ev->value_mask & CWBorderWidth)
448 +
			c->border = ev->border_width;
449 +
		if(c->isfixed || c->isfloating || isfloating()) {
450 +
			if(ev->value_mask & CWX)
451 +
				c->x = ev->x;
452 +
			if(ev->value_mask & CWY)
453 +
				c->y = ev->y;
454 +
			if(ev->value_mask & CWWidth)
455 +
				c->w = ev->width;
456 +
			if(ev->value_mask & CWHeight)
457 +
				c->h = ev->height;
458 +
			if((c->x + c->w) > sw && c->isfloating)
459 +
				c->x = sw / 2 - c->w / 2; /* center in x direction */
460 +
			if((c->y + c->h) > sh && c->isfloating)
461 +
				c->y = sh / 2 - c->h / 2; /* center in y direction */
462 +
			if((ev->value_mask & (CWX | CWY))
463 +
			&& !(ev->value_mask & (CWWidth | CWHeight)))
464 +
				configure(c);
465 +
			if(isvisible(c))
466 +
				XMoveResizeWindow(dpy, c->win, c->x, c->y, c->w, c->h);
340 467
		}
468 +
		else
469 +
			configure(c);
341 470
	}
342 471
	else {
343 -
		if(dc.font.xfont)
344 -
			XFreeFont(dpy, dc.font.xfont);
345 -
		dc.font.xfont = NULL;
346 -
		if(!(dc.font.xfont = XLoadQueryFont(dpy, fontstr))
347 -
		|| !(dc.font.xfont = XLoadQueryFont(dpy, "fixed")))
348 -
			eprint("error, cannot load font: '%s'\n", fontstr);
349 -
		dc.font.ascent = dc.font.xfont->ascent;
350 -
		dc.font.descent = dc.font.xfont->descent;
472 +
		wc.x = ev->x;
473 +
		wc.y = ev->y;
474 +
		wc.width = ev->width;
475 +
		wc.height = ev->height;
476 +
		wc.border_width = ev->border_width;
477 +
		wc.sibling = ev->above;
478 +
		wc.stack_mode = ev->detail;
479 +
		XConfigureWindow(dpy, ev->window, ev->value_mask, &wc);
351 480
	}
352 -
	dc.font.height = dc.font.ascent + dc.font.descent;
481 +
	XSync(dpy, False);
353 482
}
354 483
355 -
static Bool
356 -
isoccupied(unsigned int t) {
484 +
static void
485 +
destroynotify(XEvent *e) {
357 486
	Client *c;
487 +
	XDestroyWindowEvent *ev = &e->xdestroywindow;
358 488
359 -
	for(c = clients; c; c = c->next)
360 -
		if(c->tags[t])
361 -
			return True;
362 -
	return False;
489 +
	if((c = getclient(ev->window)))
490 +
		unmanage(c);
363 491
}
364 492
365 -
static unsigned int
366 -
textnw(const char *text, unsigned int len) {
367 -
	XRectangle r;
493 +
static void
494 +
detach(Client *c) {
495 +
	if(c->prev)
496 +
		c->prev->next = c->next;
497 +
	if(c->next)
498 +
		c->next->prev = c->prev;
499 +
	if(c == clients)
500 +
		clients = c->next;
501 +
	c->next = c->prev = NULL;
502 +
}
368 503
369 -
	if(dc.font.set) {
370 -
		XmbTextExtents(dc.font.set, text, len, NULL, &r);
371 -
		return r.width;
504 +
static void
505 +
detachstack(Client *c) {
506 +
	Client **tc;
507 +
508 +
	for(tc=&stack; *tc && *tc != c; tc=&(*tc)->snext);
509 +
	*tc = c->snext;
510 +
}
511 +
512 +
static void
513 +
drawbar(void) {
514 +
	int i, x;
515 +
516 +
	dc.x = dc.y = 0;
517 +
	for(i = 0; i < ntags; i++) {
518 +
		dc.w = textw(tags[i]);
519 +
		if(seltags[i]) {
520 +
			drawtext(tags[i], dc.sel);
521 +
			drawsquare(sel && sel->tags[i], isoccupied(i), dc.sel);
522 +
		}
523 +
		else {
524 +
			drawtext(tags[i], dc.norm);
525 +
			drawsquare(sel && sel->tags[i], isoccupied(i), dc.norm);
526 +
		}
527 +
		dc.x += dc.w;
372 528
	}
373 -
	return XTextWidth(dc.font.xfont, text, len);
529 +
	dc.w = blw;
530 +
	drawtext(layouts[ltidx].symbol, dc.norm);
531 +
	x = dc.x + dc.w;
532 +
	dc.w = textw(stext);
533 +
	dc.x = sw - dc.w;
534 +
	if(dc.x < x) {
535 +
		dc.x = x;
536 +
		dc.w = sw - x;
537 +
	}
538 +
	drawtext(stext, dc.norm);
539 +
	if((dc.w = dc.x - x) > bh) {
540 +
		dc.x = x;
541 +
		if(sel) {
542 +
			drawtext(sel->name, dc.sel);
543 +
			drawsquare(sel->ismax, sel->isfloating, dc.sel);
544 +
		}
545 +
		else
546 +
			drawtext(NULL, dc.norm);
547 +
	}
548 +
	XCopyArea(dpy, dc.drawable, barwin, dc.gc, 0, 0, sw, bh, 0, 0);
549 +
	XSync(dpy, False);
550 +
}
551 +
552 +
static void
553 +
drawsquare(Bool filled, Bool empty, unsigned long col[ColLast]) {
554 +
	int x;
555 +
	XGCValues gcv;
556 +
	XRectangle r = { dc.x, dc.y, dc.w, dc.h };
557 +
558 +
	gcv.foreground = col[ColFG];
559 +
	XChangeGC(dpy, dc.gc, GCForeground, &gcv);
560 +
	x = (dc.font.ascent + dc.font.descent + 2) / 4;
561 +
	r.x = dc.x + 1;
562 +
	r.y = dc.y + 1;
563 +
	if(filled) {
564 +
		r.width = r.height = x + 1;
565 +
		XFillRectangles(dpy, dc.drawable, dc.gc, &r, 1);
566 +
	}
567 +
	else if(empty) {
568 +
		r.width = r.height = x;
569 +
		XDrawRectangles(dpy, dc.drawable, dc.gc, &r, 1);
570 +
	}
374 571
}
375 572
376 573
static void
413 610
		XDrawString(dpy, dc.drawable, dc.gc, x, y, buf, len);
414 611
}
415 612
613 +
static void *
614 +
emallocz(unsigned int size) {
615 +
	void *res = calloc(1, size);
616 +
617 +
	if(!res)
618 +
		eprint("fatal: could not malloc() %u bytes\n", size);
619 +
	return res;
620 +
}
621 +
416 622
static void
417 -
drawbar(void) {
418 -
	int i, x;
623 +
enternotify(XEvent *e) {
624 +
	Client *c;
625 +
	XCrossingEvent *ev = &e->xcrossing;
419 626
420 -
	dc.x = dc.y = 0;
421 -
	for(i = 0; i < ntags; i++) {
422 -
		dc.w = textw(tags[i]);
423 -
		if(seltags[i]) {
424 -
			drawtext(tags[i], dc.sel);
425 -
			drawsquare(sel && sel->tags[i], isoccupied(i), dc.sel);
426 -
		}
427 -
		else {
428 -
			drawtext(tags[i], dc.norm);
429 -
			drawsquare(sel && sel->tags[i], isoccupied(i), dc.norm);
430 -
		}
431 -
		dc.x += dc.w;
627 +
	if(ev->mode != NotifyNormal || ev->detail == NotifyInferior)
628 +
		return;
629 +
	if((c = getclient(ev->window)))
630 +
		focus(c);
631 +
	else if(ev->window == root) {
632 +
		selscreen = True;
633 +
		focus(NULL);
432 634
	}
433 -
	dc.w = blw;
434 -
	drawtext(layouts[ltidx].symbol, dc.norm);
435 -
	x = dc.x + dc.w;
436 -
	dc.w = textw(stext);
437 -
	dc.x = sw - dc.w;
438 -
	if(dc.x < x) {
439 -
		dc.x = x;
440 -
		dc.w = sw - x;
441 -
	}
442 -
	drawtext(stext, dc.norm);
443 -
	if((dc.w = dc.x - x) > bh) {
444 -
		dc.x = x;
445 -
		if(sel) {
446 -
			drawtext(sel->name, dc.sel);
447 -
			drawsquare(sel->ismax, sel->isfloating, dc.sel);
448 -
		}
449 -
		else
450 -
			drawtext(NULL, dc.norm);
451 -
	}
452 -
	XCopyArea(dpy, dc.drawable, barwin, dc.gc, 0, 0, sw, bh, 0, 0);
453 -
	XSync(dpy, False);
454 635
}
455 636
456 637
static void
457 -
initstyle(void) {
458 -
	dc.norm[ColBorder] = initcolor(NORMBORDERCOLOR);
459 -
	dc.norm[ColBG] = initcolor(NORMBGCOLOR);
460 -
	dc.norm[ColFG] = initcolor(NORMFGCOLOR);
461 -
	dc.sel[ColBorder] = initcolor(SELBORDERCOLOR);
462 -
	dc.sel[ColBG] = initcolor(SELBGCOLOR);
463 -
	dc.sel[ColFG] = initcolor(SELFGCOLOR);
464 -
	initfont(FONT);
465 -
	dc.h = bh = dc.font.height + 2;
638 +
eprint(const char *errstr, ...) {
639 +
	va_list ap;
640 +
641 +
	va_start(ap, errstr);
642 +
	vfprintf(stderr, errstr, ap);
643 +
	va_end(ap);
644 +
	exit(EXIT_FAILURE);
466 645
}
467 646
468 647
static void
469 -
initbar(void) {
470 -
	XSetWindowAttributes wa;
648 +
expose(XEvent *e) {
649 +
	XExposeEvent *ev = &e->xexpose;
471 650
472 -
	wa.override_redirect = 1;
473 -
	wa.background_pixmap = ParentRelative;
474 -
	wa.event_mask = ButtonPressMask | ExposureMask;
475 -
	barwin = XCreateWindow(dpy, root, sx, sy, sw, bh, 0,
476 -
			DefaultDepth(dpy, screen), CopyFromParent, DefaultVisual(dpy, screen),
477 -
			CWOverrideRedirect | CWBackPixmap | CWEventMask, &wa);
478 -
	XDefineCursor(dpy, barwin, cursor[CurNormal]);
479 -
	updatebarpos();
480 -
	XMapRaised(dpy, barwin);
481 -
	strcpy(stext, "dwm-"VERSION);
482 -
	dc.drawable = XCreatePixmap(dpy, root, sw, bh, DefaultDepth(dpy, screen));
483 -
	dc.gc = XCreateGC(dpy, root, 0, 0);
484 -
	XSetLineAttributes(dpy, dc.gc, 1, LineSolid, CapButt, JoinMiter);
485 -
	if(!dc.font.set)
486 -
		XSetFont(dpy, dc.gc, dc.font.xfont->fid);
651 +
	if(ev->count == 0) {
652 +
		if(barwin == ev->window)
653 +
			drawbar();
654 +
	}
487 655
}
488 656
489 -
static unsigned int
490 -
textw(const char *text) {
491 -
	return textnw(text, strlen(text)) + dc.font.height;
657 +
static void
658 +
floating(void) { /* default floating layout */
659 +
	Client *c;
660 +
661 +
	for(c = clients; c; c = c->next)
662 +
		if(isvisible(c))
663 +
			resize(c, c->x, c->y, c->w, c->h, True);
492 664
}
493 665
494 666
static void
495 -
togglebar(const char *arg) {
496 -
	if(bpos == BarOff)
497 -
		bpos = (BARPOS == BarOff) ? BarTop : BARPOS;
667 +
focus(Client *c) {
668 +
	if((!c && selscreen) || (c && !isvisible(c)))
669 +
		for(c = stack; c && !isvisible(c); c = c->snext);
670 +
	if(sel && sel != c) {
671 +
		grabbuttons(sel, False);
672 +
		XSetWindowBorder(dpy, sel->win, dc.norm[ColBorder]);
673 +
	}
674 +
	if(c) {
675 +
		detachstack(c);
676 +
		attachstack(c);
677 +
		grabbuttons(c, True);
678 +
	}
679 +
	sel = c;
680 +
	drawbar();
681 +
	if(!selscreen)
682 +
		return;
683 +
	if(c) {
684 +
		XSetWindowBorder(dpy, c->win, dc.sel[ColBorder]);
685 +
		XSetInputFocus(dpy, c->win, RevertToPointerRoot, CurrentTime);
686 +
	}
498 687
	else
499 -
		bpos = BarOff;
500 -
	updatebarpos();
501 -
	arrange();
688 +
		XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime);
502 689
}
503 690
504 691
static void
505 -
updatebarpos(void) {
506 -
	XEvent ev;
692 +
focusnext(const char *arg) {
693 +
	Client *c;
507 694
508 -
	wax = sx;
509 -
	way = sy;
510 -
	wah = sh;
511 -
	waw = sw;
512 -
	switch(bpos) {
513 -
	default:
514 -
		wah -= bh;
515 -
		way += bh;
516 -
		XMoveWindow(dpy, barwin, sx, sy);
517 -
		break;
518 -
	case BarBot:
519 -
		wah -= bh;
520 -
		XMoveWindow(dpy, barwin, sx, sy + wah);
521 -
		break;
522 -
	case BarOff:
523 -
		XMoveWindow(dpy, barwin, sx, sy - bh);
524 -
		break;
695 +
	if(!sel)
696 +
		return;
697 +
	for(c = sel->next; c && !isvisible(c); c = c->next);
698 +
	if(!c)
699 +
		for(c = clients; c && !isvisible(c); c = c->next);
700 +
	if(c) {
701 +
		focus(c);
702 +
		restack();
525 703
	}
526 -
	XSync(dpy, False);
527 -
	while(XCheckMaskEvent(dpy, EnterWindowMask, &ev));
528 704
}
529 705
530 706
static void
531 -
attachstack(Client *c) {
532 -
	c->snext = stack;
533 -
	stack = c;
707 +
focusprev(const char *arg) {
708 +
	Client *c;
709 +
710 +
	if(!sel)
711 +
		return;
712 +
	for(c = sel->prev; c && !isvisible(c); c = c->prev);
713 +
	if(!c) {
714 +
		for(c = clients; c && c->next; c = c->next);
715 +
		for(; c && !isvisible(c); c = c->prev);
716 +
	}
717 +
	if(c) {
718 +
		focus(c);
719 +
		restack();
720 +
	}
534 721
}
535 722
536 -
static void
537 -
detachstack(Client *c) {
538 -
	Client **tc;
723 +
static Client *
724 +
getclient(Window w) {
725 +
	Client *c;
539 726
540 -
	for(tc=&stack; *tc && *tc != c; tc=&(*tc)->snext);
541 -
	*tc = c->snext;
727 +
	for(c = clients; c && c->win != w; c = c->next);
728 +
	return c;
729 +
}
730 +
731 +
static long
732 +
getstate(Window w) {
733 +
	int format, status;
734 +
	long result = -1;
735 +
	unsigned char *p = NULL;
736 +
	unsigned long n, extra;
737 +
	Atom real;
738 +
739 +
	status = XGetWindowProperty(dpy, w, wmatom[WMState], 0L, 2L, False, wmatom[WMState],
740 +
			&real, &format, &n, &extra, (unsigned char **)&p);
741 +
	if(status != Success)
742 +
		return -1;
743 +
	if(n != 0)
744 +
		result = *p;
745 +
	XFree(p);
746 +
	return result;
747 +
}
748 +
749 +
static Bool
750 +
gettextprop(Window w, Atom atom, char *text, unsigned int size) {
751 +
	char **list = NULL;
752 +
	int n;
753 +
	XTextProperty name;
754 +
755 +
	if(!text || size == 0)
756 +
		return False;
757 +
	text[0] = '\0';
758 +
	XGetTextProperty(dpy, w, &name, atom);
759 +
	if(!name.nitems)
760 +
		return False;
761 +
	if(name.encoding == XA_STRING)
762 +
		strncpy(text, (char *)name.value, size - 1);
763 +
	else {
764 +
		if(XmbTextPropertyToTextList(dpy, &name, &list, &n) >= Success
765 +
		&& n > 0 && *list)
766 +
		{
767 +
			strncpy(text, *list, size - 1);
768 +
			XFreeStringList(list);
769 +
		}
770 +
	}
771 +
	text[size - 1] = '\0';
772 +
	XFree(name.value);
773 +
	return True;
542 774
}
543 775
544 776
static void
578 810
				GrabModeAsync, GrabModeSync, None, None);
579 811
}
580 812
813 +
static unsigned int
814 +
idxoftag(const char *tag) {
815 +
	unsigned int i;
816 +
817 +
	for(i = 0; i < ntags; i++)
818 +
		if(tags[i] == tag)
819 +
			return i;
820 +
	return 0;
821 +
}
822 +
823 +
static void
824 +
initbar(void) {
825 +
	XSetWindowAttributes wa;
826 +
827 +
	wa.override_redirect = 1;
828 +
	wa.background_pixmap = ParentRelative;
829 +
	wa.event_mask = ButtonPressMask | ExposureMask;
830 +
	barwin = XCreateWindow(dpy, root, sx, sy, sw, bh, 0,
831 +
			DefaultDepth(dpy, screen), CopyFromParent, DefaultVisual(dpy, screen),
832 +
			CWOverrideRedirect | CWBackPixmap | CWEventMask, &wa);
833 +
	XDefineCursor(dpy, barwin, cursor[CurNormal]);
834 +
	updatebarpos();
835 +
	XMapRaised(dpy, barwin);
836 +
	strcpy(stext, "dwm-"VERSION);
837 +
	dc.drawable = XCreatePixmap(dpy, root, sw, bh, DefaultDepth(dpy, screen));
838 +
	dc.gc = XCreateGC(dpy, root, 0, 0);
839 +
	XSetLineAttributes(dpy, dc.gc, 1, LineSolid, CapButt, JoinMiter);
840 +
	if(!dc.font.set)
841 +
		XSetFont(dpy, dc.gc, dc.font.xfont->fid);
842 +
}
843 +
844 +
static unsigned long
845 +
initcolor(const char *colstr) {
846 +
	Colormap cmap = DefaultColormap(dpy, screen);
847 +
	XColor color;
848 +
849 +
	if(!XAllocNamedColor(dpy, cmap, colstr, &color, &color))
850 +
		eprint("error, cannot allocate color '%s'\n", colstr);
851 +
	return color.pixel;
852 +
}
853 +
854 +
static void
855 +
initfont(const char *fontstr) {
856 +
	char *def, **missing;
857 +
	int i, n;
858 +
859 +
	missing = NULL;
860 +
	if(dc.font.set)
861 +
		XFreeFontSet(dpy, dc.font.set);
862 +
	dc.font.set = XCreateFontSet(dpy, fontstr, &missing, &n, &def);
863 +
	if(missing) {
864 +
		while(n--)
865 +
			fprintf(stderr, "dwm: missing fontset: %s\n", missing[n]);
866 +
		XFreeStringList(missing);
867 +
	}
868 +
	if(dc.font.set) {
869 +
		XFontSetExtents *font_extents;
870 +
		XFontStruct **xfonts;
871 +
		char **font_names;
872 +
		dc.font.ascent = dc.font.descent = 0;
873 +
		font_extents = XExtentsOfFontSet(dc.font.set);
874 +
		n = XFontsOfFontSet(dc.font.set, &xfonts, &font_names);
875 +
		for(i = 0, dc.font.ascent = 0, dc.font.descent = 0; i < n; i++) {
876 +
			if(dc.font.ascent < (*xfonts)->ascent)
877 +
				dc.font.ascent = (*xfonts)->ascent;
878 +
			if(dc.font.descent < (*xfonts)->descent)
879 +
				dc.font.descent = (*xfonts)->descent;
880 +
			xfonts++;
881 +
		}
882 +
	}
883 +
	else {
884 +
		if(dc.font.xfont)
885 +
			XFreeFont(dpy, dc.font.xfont);
886 +
		dc.font.xfont = NULL;
887 +
		if(!(dc.font.xfont = XLoadQueryFont(dpy, fontstr))
888 +
		|| !(dc.font.xfont = XLoadQueryFont(dpy, "fixed")))
889 +
			eprint("error, cannot load font: '%s'\n", fontstr);
890 +
		dc.font.ascent = dc.font.xfont->ascent;
891 +
		dc.font.descent = dc.font.xfont->descent;
892 +
	}
893 +
	dc.font.height = dc.font.ascent + dc.font.descent;
894 +
}
895 +
896 +
static void
897 +
initlayouts(void) {
898 +
	unsigned int i, w;
899 +
900 +
	nlayouts = sizeof layouts / sizeof layouts[0];
901 +
	for(blw = i = 0; i < nlayouts; i++) {
902 +
		w = textw(layouts[i].symbol);
903 +
		if(w > blw)
904 +
			blw = w;
905 +
	}
906 +
}
907 +
908 +
static void
909 +
initstyle(void) {
910 +
	dc.norm[ColBorder] = initcolor(NORMBORDERCOLOR);
911 +
	dc.norm[ColBG] = initcolor(NORMBGCOLOR);
912 +
	dc.norm[ColFG] = initcolor(NORMFGCOLOR);
913 +
	dc.sel[ColBorder] = initcolor(SELBORDERCOLOR);
914 +
	dc.sel[ColBG] = initcolor(SELBGCOLOR);
915 +
	dc.sel[ColFG] = initcolor(SELFGCOLOR);
916 +
	initfont(FONT);
917 +
	dc.h = bh = dc.font.height + 2;
918 +
}
919 +
920 +
static Bool
921 +
isarrange(void (*func)())
922 +
{
923 +
	return func == layouts[ltidx].arrange;
924 +
}
925 +
926 +
static Bool
927 +
isfloating(void) {
928 +
	return layouts[ltidx].arrange == floating;
929 +
}
930 +
931 +
static Bool
932 +
isoccupied(unsigned int t) {
933 +
	Client *c;
934 +
935 +
	for(c = clients; c; c = c->next)
936 +
		if(c->tags[t])
937 +
			return True;
938 +
	return False;
939 +
}
940 +
581 941
static Bool
582 942
isprotodel(Client *c) {
583 943
	int i, n;
593 953
	return ret;
594 954
}
595 955
596 -
static void
597 -
setclientstate(Client *c, long state) {
598 -
	long data[] = {state, None};
599 -
600 -
	XChangeProperty(dpy, c->win, wmatom[WMState], wmatom[WMState], 32,
601 -
			PropModeReplace, (unsigned char *)data, 2);
602 -
}
603 -
604 -
static int
605 -
xerrordummy(Display *dsply, XErrorEvent *ee) {
606 -
	return 0;
607 -
}
956 +
static Bool
957 +
isvisible(Client *c) {
958 +
	unsigned int i;
608 959
609 -
static void
610 -
ban(Client *c) {
611 -
	if(c->isbanned)
612 -
		return;
613 -
	XMoveWindow(dpy, c->win, c->x + 2 * sw, c->y);
614 -
	c->isbanned = True;
960 +
	for(i = 0; i < ntags; i++)
961 +
		if(c->tags[i] && seltags[i])
962 +
			return True;
963 +
	return False;
615 964
}
616 965
617 966
static void
618 -
configure(Client *c) {
619 -
	XConfigureEvent ce;
967 +
keypress(XEvent *e) {
968 +
	KEYS
969 +
	unsigned int len = sizeof keys / sizeof keys[0];
970 +
	unsigned int i;
971 +
	KeyCode code;
972 +
	KeySym keysym;
973 +
	XKeyEvent *ev;
620 974
621 -
	ce.type = ConfigureNotify;
622 -
	ce.display = dpy;
623 -
	ce.event = c->win;
624 -
	ce.window = c->win;
625 -
	ce.x = c->x;
626 -
	ce.y = c->y;
627 -
	ce.width = c->w;
628 -
	ce.height = c->h;
629 -
	ce.border_width = c->border;
630 -
	ce.above = None;
631 -
	ce.override_redirect = False;
632 -
	XSendEvent(dpy, c->win, False, StructureNotifyMask, (XEvent *)&ce);
975 +
	if(!e) { /* grabkeys */
976 +
		XUngrabKey(dpy, AnyKey, AnyModifier, root);
977 +
		for(i = 0; i < len; i++) {
978 +
			code = XKeysymToKeycode(dpy, keys[i].keysym);
979 +
			XGrabKey(dpy, code, keys[i].mod, root, True,
980 +
					GrabModeAsync, GrabModeAsync);
981 +
			XGrabKey(dpy, code, keys[i].mod | LockMask, root, True,
982 +
					GrabModeAsync, GrabModeAsync);
983 +
			XGrabKey(dpy, code, keys[i].mod | numlockmask, root, True,
984 +
					GrabModeAsync, GrabModeAsync);
985 +
			XGrabKey(dpy, code, keys[i].mod | numlockmask | LockMask, root, True,
986 +
					GrabModeAsync, GrabModeAsync);
987 +
		}
988 +
		return;
989 +
	}
990 +
	ev = &e->xkey;
991 +
	keysym = XKeycodeToKeysym(dpy, (KeyCode)ev->keycode, 0);
992 +
	for(i = 0; i < len; i++)
993 +
		if(keysym == keys[i].keysym
994 +
		&& CLEANMASK(keys[i].mod) == CLEANMASK(ev->state))
995 +
		{
996 +
			if(keys[i].func)
997 +
				keys[i].func(keys[i].arg);
998 +
		}
633 999
}
634 1000
635 1001
static void
652 1018
}
653 1019
654 1020
static void
1021 +
leavenotify(XEvent *e) {
1022 +
	XCrossingEvent *ev = &e->xcrossing;
1023 +
1024 +
	if((ev->window == root) && !ev->same_screen) {
1025 +
		selscreen = False;
1026 +
		focus(NULL);
1027 +
	}
1028 +
}
1029 +
1030 +
static void
655 1031
manage(Window w, XWindowAttributes *wa) {
656 1032
	unsigned int i;
657 1033
	Client *c, *t = NULL;
710 1086
}
711 1087
712 1088
static void
1089 +
mappingnotify(XEvent *e) {
1090 +
	XMappingEvent *ev = &e->xmapping;
1091 +
1092 +
	XRefreshKeyboardMapping(ev);
1093 +
	if(ev->request == MappingKeyboard)
1094 +
		keypress(NULL);
1095 +
}
1096 +
1097 +
static void
1098 +
maprequest(XEvent *e) {
1099 +
	static XWindowAttributes wa;
1100 +
	XMapRequestEvent *ev = &e->xmaprequest;
1101 +
1102 +
	if(!XGetWindowAttributes(dpy, ev->window, &wa))
1103 +
		return;
1104 +
	if(wa.override_redirect)
1105 +
		return;
1106 +
	if(!getclient(ev->window))
1107 +
		manage(ev->window, &wa);
1108 +
}
1109 +
1110 +
static void
1111 +
movemouse(Client *c) {
1112 +
	int x1, y1, ocx, ocy, di, nx, ny;
1113 +
	unsigned int dui;
1114 +
	Window dummy;
1115 +
	XEvent ev;
1116 +
1117 +
	ocx = nx = c->x;
1118 +
	ocy = ny = c->y;
1119 +
	if(XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync,
1120 +
			None, cursor[CurMove], CurrentTime) != GrabSuccess)
1121 +
		return;
1122 +
	c->ismax = False;
1123 +
	XQueryPointer(dpy, root, &dummy, &dummy, &x1, &y1, &di, &di, &dui);
1124 +
	for(;;) {
1125 +
		XMaskEvent(dpy, MOUSEMASK | ExposureMask | SubstructureRedirectMask, &ev);
1126 +
		switch (ev.type) {
1127 +
		case ButtonRelease:
1128 +
			XUngrabPointer(dpy, CurrentTime);
1129 +
			return;
1130 +
		case ConfigureRequest:
1131 +
		case Expose:
1132 +
		case MapRequest:
1133 +
			handler[ev.type](&ev);
1134 +
			break;
1135 +
		case MotionNotify:
1136 +
			XSync(dpy, False);
1137 +
			nx = ocx + (ev.xmotion.x - x1);
1138 +
			ny = ocy + (ev.xmotion.y - y1);
1139 +
			if(abs(wax + nx) < SNAP)
1140 +
				nx = wax;
1141 +
			else if(abs((wax + waw) - (nx + c->w + 2 * c->border)) < SNAP)
1142 +
				nx = wax + waw - c->w - 2 * c->border;
1143 +
			if(abs(way - ny) < SNAP)
1144 +
				ny = way;
1145 +
			else if(abs((way + wah) - (ny + c->h + 2 * c->border)) < SNAP)
1146 +
				ny = way + wah - c->h - 2 * c->border;
1147 +
			resize(c, nx, ny, c->w, c->h, False);
1148 +
			break;
1149 +
		}
1150 +
	}
1151 +
}
1152 +
1153 +
static Client *
1154 +
nexttiled(Client *c) {
1155 +
	for(; c && (c->isfloating || !isvisible(c)); c = c->next);
1156 +
	return c;
1157 +
}
1158 +
1159 +
static void
1160 +
propertynotify(XEvent *e) {
1161 +
	Client *c;
1162 +
	Window trans;
1163 +
	XPropertyEvent *ev = &e->xproperty;
1164 +
1165 +
	if(ev->state == PropertyDelete)
1166 +
		return; /* ignore */
1167 +
	if((c = getclient(ev->window))) {
1168 +
		switch (ev->atom) {
1169 +
			default: break;
1170 +
			case XA_WM_TRANSIENT_FOR:
1171 +
				XGetTransientForHint(dpy, c->win, &trans);
1172 +
				if(!c->isfloating && (c->isfloating = (getclient(trans) != NULL)))
1173 +
					arrange();
1174 +
				break;
1175 +
			case XA_WM_NORMAL_HINTS:
1176 +
				updatesizehints(c);
1177 +
				break;
1178 +
		}
1179 +
		if(ev->atom == XA_WM_NAME || ev->atom == netatom[NetWMName]) {
1180 +
			updatetitle(c);
1181 +
			if(c == sel)
1182 +
				drawbar();
1183 +
		}
1184 +
	}
1185 +
}
1186 +
1187 +
static void
1188 +
quit(const char *arg) {
1189 +
	readin = running = False;
1190 +
}
1191 +
1192 +
static void
713 1193
resize(Client *c, int x, int y, int w, int h, Bool sizehints) {
714 1194
	double dx, dy, max, min, ratio;
715 1195
	XWindowChanges wc; 
773 1253
}
774 1254
775 1255
static void
776 -
unban(Client *c) {
777 -
	if(!c->isbanned)
778 -
		return;
779 -
	XMoveWindow(dpy, c->win, c->x, c->y);
780 -
	c->isbanned = False;
781 -
}
782 -
783 -
static void
784 -
unmanage(Client *c) {
785 -
	XWindowChanges wc;
786 -
787 -
	wc.border_width = c->oldborder;
788 -
	/* The server grab construct avoids race conditions. */
789 -
	XGrabServer(dpy);
790 -
	XSetErrorHandler(xerrordummy);
791 -
	XConfigureWindow(dpy, c->win, CWBorderWidth, &wc); /* restore border */
792 -
	detach(c);
793 -
	detachstack(c);
794 -
	if(sel == c)
795 -
		focus(NULL);
796 -
	XUngrabButton(dpy, AnyButton, AnyModifier, c->win);
797 -
	setclientstate(c, WithdrawnState);
798 -
	free(c->tags);
799 -
	free(c);
800 -
	XSync(dpy, False);
801 -
	XSetErrorHandler(xerror);
802 -
	XUngrabServer(dpy);
803 -
	arrange();
804 -
}
805 -
806 -
static void
807 -
updatesizehints(Client *c) {
808 -
	long msize;
809 -
	XSizeHints size;
810 -
811 -
	if(!XGetWMNormalHints(dpy, c->win, &size, &msize) || !size.flags)
812 -
		size.flags = PSize;
813 -
	c->flags = size.flags;
814 -
	if(c->flags & PBaseSize) {
815 -
		c->basew = size.base_width;
816 -
		c->baseh = size.base_height;
817 -
	}
818 -
	else if(c->flags & PMinSize) {
819 -
		c->basew = size.min_width;
820 -
		c->baseh = size.min_height;
821 -
	}
822 -
	else
823 -
		c->basew = c->baseh = 0;
824 -
	if(c->flags & PResizeInc) {
825 -
		c->incw = size.width_inc;
826 -
		c->inch = size.height_inc;
827 -
	}
828 -
	else
829 -
		c->incw = c->inch = 0;
830 -
	if(c->flags & PMaxSize) {
831 -
		c->maxw = size.max_width;
832 -
		c->maxh = size.max_height;
833 -
	}
834 -
	else
835 -
		c->maxw = c->maxh = 0;
836 -
	if(c->flags & PMinSize) {
837 -
		c->minw = size.min_width;
838 -
		c->minh = size.min_height;
839 -
	}
840 -
	else if(c->flags & PBaseSize) {
841 -
		c->minw = size.base_width;
842 -
		c->minh = size.base_height;
843 -
	}
844 -
	else
845 -
		c->minw = c->minh = 0;
846 -
	if(c->flags & PAspect) {
847 -
		c->minax = size.min_aspect.x;
848 -
		c->maxax = size.max_aspect.x;
849 -
		c->minay = size.min_aspect.y;
850 -
		c->maxay = size.max_aspect.y;
851 -
	}
852 -
	else
853 -
		c->minax = c->maxax = c->minay = c->maxay = 0;
854 -
	c->isfixed = (c->maxw && c->minw && c->maxh && c->minh
855 -
			&& c->maxw == c->minw && c->maxh == c->minh);
856 -
}
857 -
858 -
static void
859 -
updatetitle(Client *c) {
860 -
	if(!gettextprop(c->win, netatom[NetWMName], c->name, sizeof c->name))
861 -
		gettextprop(c->win, wmatom[WMName], c->name, sizeof c->name);
862 -
}
863 -
864 -
static Client *
865 -
getclient(Window w) {
866 -
	Client *c;
867 -
868 -
	for(c = clients; c && c->win != w; c = c->next);
869 -
	return c;
870 -
}
871 -
872 -
static void
873 -
movemouse(Client *c) {
874 -
	int x1, y1, ocx, ocy, di, nx, ny;
875 -
	unsigned int dui;
876 -
	Window dummy;
877 -
	XEvent ev;
878 -
879 -
	ocx = nx = c->x;
880 -
	ocy = ny = c->y;
881 -
	if(XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync,
882 -
			None, cursor[CurMove], CurrentTime) != GrabSuccess)
883 -
		return;
884 -
	c->ismax = False;
885 -
	XQueryPointer(dpy, root, &dummy, &dummy, &x1, &y1, &di, &di, &dui);
886 -
	for(;;) {
887 -
		XMaskEvent(dpy, MOUSEMASK | ExposureMask | SubstructureRedirectMask, &ev);
888 -
		switch (ev.type) {
889 -
		case ButtonRelease:
890 -
			XUngrabPointer(dpy, CurrentTime);
891 -
			return;
892 -
		case ConfigureRequest:
893 -
		case Expose:
894 -
		case MapRequest:
895 -
			handler[ev.type](&ev);
896 -
			break;
897 -
		case MotionNotify:
898 -
			XSync(dpy, False);
899 -
			nx = ocx + (ev.xmotion.x - x1);
900 -
			ny = ocy + (ev.xmotion.y - y1);
901 -
			if(abs(wax + nx) < SNAP)
902 -
				nx = wax;
903 -
			else if(abs((wax + waw) - (nx + c->w + 2 * c->border)) < SNAP)
904 -
				nx = wax + waw - c->w - 2 * c->border;
905 -
			if(abs(way - ny) < SNAP)
906 -
				ny = way;
907 -
			else if(abs((way + wah) - (ny + c->h + 2 * c->border)) < SNAP)
908 -
				ny = way + wah - c->h - 2 * c->border;
909 -
			resize(c, nx, ny, c->w, c->h, False);
910 -
			break;
911 -
		}
912 -
	}
913 -
}
914 -
915 -
static void
916 1256
resizemouse(Client *c) {
917 1257
	int ocx, ocy;
918 1258
	int nw, nh;
952 1292
}
953 1293
954 1294
static void
955 -
buttonpress(XEvent *e) {
956 -
	unsigned int i, x;
957 -
	Client *c;
958 -
	XButtonPressedEvent *ev = &e->xbutton;
959 -
960 -
	if(barwin == ev->window) {
961 -
		x = 0;
962 -
		for(i = 0; i < ntags; i++) {
963 -
			x += textw(tags[i]);
964 -
			if(ev->x < x) {
965 -
				if(ev->button == Button1) {
966 -
					if(ev->state & MODKEY)
967 -
						tag(tags[i]);
968 -
					else
969 -
						view(tags[i]);
970 -
				}
971 -
				else if(ev->button == Button3) {
972 -
					if(ev->state & MODKEY)
973 -
						toggletag(tags[i]);
974 -
					else
975 -
						toggleview(tags[i]);
976 -
				}
977 -
				return;
978 -
			}
979 -
		}
980 -
		if((ev->x < x + blw) && ev->button == Button1)
981 -
			setlayout(NULL);
982 -
	}
983 -
	else if((c = getclient(ev->window))) {
984 -
		focus(c);
985 -
		if(CLEANMASK(ev->state) != MODKEY)
986 -
			return;
987 -
		if(ev->button == Button1 && (isfloating() || c->isfloating)) {
988 -
			restack();
989 -
			movemouse(c);
990 -
		}
991 -
		else if(ev->button == Button2)
992 -
			zoom(NULL);
993 -
		else if(ev->button == Button3
994 -
		&& (isfloating() || c->isfloating) && !c->isfixed)
995 -
		{
996 -
			restack();
997 -
			resizemouse(c);
998 -
		}
999 -
	}
1000 -
}
1001 -
1002 -
static void
1003 -
configurerequest(XEvent *e) {
1295 +
restack(void) {
1004 1296
	Client *c;
1005 -
	XConfigureRequestEvent *ev = &e->xconfigurerequest;
1297 +
	XEvent ev;
1006 1298
	XWindowChanges wc;
1007 1299
1008 -
	if((c = getclient(ev->window))) {
1009 -
		c->ismax = False;
1010 -
		if(ev->value_mask & CWBorderWidth)
1011 -
			c->border = ev->border_width;
1012 -
		if(c->isfixed || c->isfloating || isfloating()) {
1013 -
			if(ev->value_mask & CWX)
1014 -
				c->x = ev->x;
1015 -
			if(ev->value_mask & CWY)
1016 -
				c->y = ev->y;
1017 -
			if(ev->value_mask & CWWidth)
1018 -
				c->w = ev->width;
1019 -
			if(ev->value_mask & CWHeight)
1020 -
				c->h = ev->height;
1021 -
			if((c->x + c->w) > sw && c->isfloating)
1022 -
				c->x = sw / 2 - c->w / 2; /* center in x direction */
1023 -
			if((c->y + c->h) > sh && c->isfloating)
1024 -
				c->y = sh / 2 - c->h / 2; /* center in y direction */
1025 -
			if((ev->value_mask & (CWX | CWY))
1026 -
			&& !(ev->value_mask & (CWWidth | CWHeight)))
1027 -
				configure(c);
1028 -
			if(isvisible(c))
1029 -
				XMoveResizeWindow(dpy, c->win, c->x, c->y, c->w, c->h);
1300 +
	drawbar();
1301 +
	if(!sel)
1302 +
		return;
1303 +
	if(sel->isfloating || isfloating())
1304 +
		XRaiseWindow(dpy, sel->win);
1305 +
	if(!isfloating()) {
1306 +
		wc.stack_mode = Below;
1307 +
		wc.sibling = barwin;
1308 +
		if(!sel->isfloating) {
1309 +
			XConfigureWindow(dpy, sel->win, CWSibling | CWStackMode, &wc);
1310 +
			wc.sibling = sel->win;
1030 1311
		}
1031 -
		else
1032 -
			configure(c);
1033 -
	}
1034 -
	else {
1035 -
		wc.x = ev->x;
1036 -
		wc.y = ev->y;
1037 -
		wc.width = ev->width;
1038 -
		wc.height = ev->height;
1039 -
		wc.border_width = ev->border_width;
1040 -
		wc.sibling = ev->above;
1041 -
		wc.stack_mode = ev->detail;
1042 -
		XConfigureWindow(dpy, ev->window, ev->value_mask, &wc);
1312 +
		for(c = nexttiled(clients); c; c = nexttiled(c->next)) {
1313 +
			if(c == sel)
1314 +
				continue;
1315 +
			XConfigureWindow(dpy, c->win, CWSibling | CWStackMode, &wc);
1316 +
			wc.sibling = c->win;
1317 +
		}
1043 1318
	}
1044 1319
	XSync(dpy, False);
1045 -
}
1046 -
1047 -
static void
1048 -
configurenotify(XEvent *e) {
1049 -
	XConfigureEvent *ev = &e->xconfigure;
1050 -
1051 -
	if (ev->window == root && (ev->width != sw || ev->height != sh)) {
1052 -
		sw = ev->width;
1053 -
		sh = ev->height;
1054 -
		XFreePixmap(dpy, dc.drawable);
1055 -
		dc.drawable = XCreatePixmap(dpy, root, sw, bh, DefaultDepth(dpy, screen));
1056 -
		XResizeWindow(dpy, barwin, sw, bh);
1057 -
		updatebarpos();
1058 -
		arrange();
1059 -
	}
1060 -
}
1061 -
1062 -
static void
1063 -
destroynotify(XEvent *e) {
1064 -
	Client *c;
1065 -
	XDestroyWindowEvent *ev = &e->xdestroywindow;
1066 -
1067 -
	if((c = getclient(ev->window)))
1068 -
		unmanage(c);
1320 +
	while(XCheckMaskEvent(dpy, EnterWindowMask, &ev));
1069 1321
}
1070 1322
1071 1323
static void
1072 -
enternotify(XEvent *e) {
1073 -
	Client *c;
1074 -
	XCrossingEvent *ev = &e->xcrossing;
1324 +
scan(void) {
1325 +
	unsigned int i, num;
1326 +
	Window *wins, d1, d2;
1327 +
	XWindowAttributes wa;
1075 1328
1076 -
	if(ev->mode != NotifyNormal || ev->detail == NotifyInferior)
1077 -
		return;
1078 -
	if((c = getclient(ev->window)))
1079 -
		focus(c);
1080 -
	else if(ev->window == root) {
1081 -
		selscreen = True;
1082 -
		focus(NULL);
1329 +
	wins = NULL;
1330 +
	if(XQueryTree(dpy, root, &d1, &d2, &wins, &num)) {
1331 +
		for(i = 0; i < num; i++) {
1332 +
			if(!XGetWindowAttributes(dpy, wins[i], &wa)
1333 +
			|| wa.override_redirect || XGetTransientForHint(dpy, wins[i], &d1))
1334 +
				continue;
1335 +
			if(wa.map_state == IsViewable || getstate(wins[i]) == IconicState)
1336 +
				manage(wins[i], &wa);
1337 +
		}
1338 +
		for(i = 0; i < num; i++) { /* now the transients */
1339 +
			if(!XGetWindowAttributes(dpy, wins[i], &wa))
1340 +
				continue;
1341 +
			if(XGetTransientForHint(dpy, wins[i], &d1)
1342 +
			&& (wa.map_state == IsViewable || getstate(wins[i]) == IconicState))
1343 +
				manage(wins[i], &wa);
1344 +
		}
1083 1345
	}
1346 +
	if(wins)
1347 +
		XFree(wins);
1084 1348
}
1085 1349
1086 1350
static void
1087 -
expose(XEvent *e) {
1088 -
	XExposeEvent *ev = &e->xexpose;
1351 +
setclientstate(Client *c, long state) {
1352 +
	long data[] = {state, None};
1089 1353
1090 -
	if(ev->count == 0) {
1091 -
		if(barwin == ev->window)
1092 -
			drawbar();
1093 -
	}
1354 +
	XChangeProperty(dpy, c->win, wmatom[WMState], wmatom[WMState], 32,
1355 +
			PropModeReplace, (unsigned char *)data, 2);
1094 1356
}
1095 1357
1096 1358
static void
1097 -
keypress(XEvent *e) {
1098 -
	KEYS
1099 -
	unsigned int len = sizeof keys / sizeof keys[0];
1359 +
setlayout(const char *arg) {
1100 1360
	unsigned int i;
1101 -
	KeyCode code;
1102 -
	KeySym keysym;
1103 -
	XKeyEvent *ev;
1104 1361
1105 -
	if(!e) { /* grabkeys */
1106 -
		XUngrabKey(dpy, AnyKey, AnyModifier, root);
1107 -
		for(i = 0; i < len; i++) {
1108 -
			code = XKeysymToKeycode(dpy, keys[i].keysym);
1109 -
			XGrabKey(dpy, code, keys[i].mod, root, True,
1110 -
					GrabModeAsync, GrabModeAsync);
1111 -
			XGrabKey(dpy, code, keys[i].mod | LockMask, root, True,
1112 -
					GrabModeAsync, GrabModeAsync);
1113 -
			XGrabKey(dpy, code, keys[i].mod | numlockmask, root, True,
1114 -
					GrabModeAsync, GrabModeAsync);
1115 -
			XGrabKey(dpy, code, keys[i].mod | numlockmask | LockMask, root, True,
1116 -
					GrabModeAsync, GrabModeAsync);
1117 -
		}
1118 -
		return;
1362 +
	if(!arg) {
1363 +
		if(++ltidx == nlayouts)
1364 +
			ltidx = 0;;
1119 1365
	}
1120 -
	ev = &e->xkey;
1121 -
	keysym = XKeycodeToKeysym(dpy, (KeyCode)ev->keycode, 0);
1122 -
	for(i = 0; i < len; i++)
1123 -
		if(keysym == keys[i].keysym
1124 -
		&& CLEANMASK(keys[i].mod) == CLEANMASK(ev->state))
1125 -
		{
1126 -
			if(keys[i].func)
1127 -
				keys[i].func(keys[i].arg);
1128 -
		}
1129 -
}
1130 -
1131 -
static void
1132 -
leavenotify(XEvent *e) {
1133 -
	XCrossingEvent *ev = &e->xcrossing;
1134 -
1135 -
	if((ev->window == root) && !ev->same_screen) {
1136 -
		selscreen = False;
1137 -
		focus(NULL);
1366 +
	else {
1367 +
		for(i = 0; i < nlayouts; i++)
1368 +
			if(!strcmp(arg, layouts[i].symbol))
1369 +
				break;
1370 +
		if(i == nlayouts)
1371 +
			return;
1372 +
		ltidx = i;
1138 1373
	}
1139 -
}
1140 -
1141 -
static void
1142 -
mappingnotify(XEvent *e) {
1143 -
	XMappingEvent *ev = &e->xmapping;
1144 -
1145 -
	XRefreshKeyboardMapping(ev);
1146 -
	if(ev->request == MappingKeyboard)
1147 -
		keypress(NULL);
1374 +
	if(sel)
1375 +
		arrange();
1376 +
	else
1377 +
		drawbar();
1148 1378
}
1149 1379
1150 1380
static void
1151 -
maprequest(XEvent *e) {
1152 -
	static XWindowAttributes wa;
1153 -
	XMapRequestEvent *ev = &e->xmaprequest;
1381 +
setmwfact(const char *arg) {
1382 +
	double delta;
1154 1383
1155 -
	if(!XGetWindowAttributes(dpy, ev->window, &wa))
1156 -
		return;
1157 -
	if(wa.override_redirect)
1384 +
	if(!isarrange(tile))
1158 1385
		return;
1159 -
	if(!getclient(ev->window))
1160 -
		manage(ev->window, &wa);
1161 -
}
1162 -
1163 -
static void
1164 -
propertynotify(XEvent *e) {
1165 -
	Client *c;
1166 -
	Window trans;
1167 -
	XPropertyEvent *ev = &e->xproperty;
1168 -
1169 -
	if(ev->state == PropertyDelete)
1170 -
		return; /* ignore */
1171 -
	if((c = getclient(ev->window))) {
1172 -
		switch (ev->atom) {
1173 -
			default: break;
1174 -
			case XA_WM_TRANSIENT_FOR:
1175 -
				XGetTransientForHint(dpy, c->win, &trans);
1176 -
				if(!c->isfloating && (c->isfloating = (getclient(trans) != NULL)))
1177 -
					arrange();
1178 -
				break;
1179 -
			case XA_WM_NORMAL_HINTS:
1180 -
				updatesizehints(c);
1181 -
				break;
1182 -
		}
1183 -
		if(ev->atom == XA_WM_NAME || ev->atom == netatom[NetWMName]) {
1184 -
			updatetitle(c);
1185 -
			if(c == sel)
1186 -
				drawbar();
1187 -
		}
1386 +
	/* arg handling, manipulate mwfact */
1387 +
	if(arg == NULL)
1388 +
		mwfact = MWFACT;
1389 +
	else if(1 == sscanf(arg, "%lf", &delta)) {
1390 +
		if(arg[0] != '+' && arg[0] != '-')
1391 +
			mwfact = delta;
1392 +
		else
1393 +
			mwfact += delta;
1394 +
		if(mwfact < 0.1)
1395 +
			mwfact = 0.1;
1396 +
		else if(mwfact > 0.9)
1397 +
			mwfact = 0.9;
1188 1398
	}
1189 -
}
1190 -
1191 -
static void
1192 -
unmapnotify(XEvent *e) {
1193 -
	Client *c;
1194 -
	XUnmapEvent *ev = &e->xunmap;
1195 -
1196 -
	if((c = getclient(ev->window)))
1197 -
		unmanage(c);
1198 -
}
1199 -
1200 -
static unsigned int
1201 -
idxoftag(const char *tag) {
1202 -
	unsigned int i;
1203 -
1204 -
	for(i = 0; i < ntags; i++)
1205 -
		if(tags[i] == tag)
1206 -
			return i;
1207 -
	return 0;
1208 -
}
1209 -
1210 -
static void
1211 -
floating(void) { /* default floating layout */
1212 -
	Client *c;
1213 -
1214 -
	for(c = clients; c; c = c->next)
1215 -
		if(isvisible(c))
1216 -
			resize(c, c->x, c->y, c->w, c->h, True);
1399 +
	arrange();
1217 1400
}
1218 1401
1219 1402
static void
1220 -
applyrules(Client *c) {
1221 -
	static char buf[512];
1222 -
	unsigned int i, j;
1223 -
	regmatch_t tmp;
1224 -
	Bool matched = False;
1225 -
	XClassHint ch = { 0 };
1403 +
setup(void) {
1404 +
	int i, j;
1405 +
	unsigned int mask;
1406 +
	Window w;
1407 +
	XModifierKeymap *modmap;
1408 +
	XSetWindowAttributes wa;
1226 1409
1227 -
	/* rule matching */
1228 -
	XGetClassHint(dpy, c->win, &ch);
1229 -
	snprintf(buf, sizeof buf, "%s:%s:%s",
1230 -
			ch.res_class ? ch.res_class : "",
1231 -
			ch.res_name ? ch.res_name : "", c->name);
1232 -
	for(i = 0; i < nrules; i++)
1233 -
		if(regs[i].propregex && !regexec(regs[i].propregex, buf, 1, &tmp, 0)) {
1234 -
			c->isfloating = rules[i].isfloating;
1235 -
			for(j = 0; regs[i].tagregex && j < ntags; j++) {
1236 -
				if(!regexec(regs[i].tagregex, tags[j], 1, &tmp, 0)) {
1237 -
					matched = True;
1238 -
					c->tags[j] = True;
1239 -
				}
1240 -
			}
1410 +
	/* init atoms */
1411 +
	wmatom[WMProtocols] = XInternAtom(dpy, "WM_PROTOCOLS", False);
1412 +
	wmatom[WMDelete] = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
1413 +
	wmatom[WMName] = XInternAtom(dpy, "WM_NAME", False);
1414 +
	wmatom[WMState] = XInternAtom(dpy, "WM_STATE", False);
1415 +
	netatom[NetSupported] = XInternAtom(dpy, "_NET_SUPPORTED", False);
1416 +
	netatom[NetWMName] = XInternAtom(dpy, "_NET_WM_NAME", False);
1417 +
	XChangeProperty(dpy, root, netatom[NetSupported], XA_ATOM, 32,
1418 +
			PropModeReplace, (unsigned char *) netatom, NetLast);
1419 +
	/* init cursors */
1420 +
	cursor[CurNormal] = XCreateFontCursor(dpy, XC_left_ptr);
1421 +
	cursor[CurResize] = XCreateFontCursor(dpy, XC_sizing);
1422 +
	cursor[CurMove] = XCreateFontCursor(dpy, XC_fleur);
1423 +
	/* init modifier map */
1424 +
	modmap = XGetModifierMapping(dpy);
1425 +
	for (i = 0; i < 8; i++)
1426 +
		for (j = 0; j < modmap->max_keypermod; j++) {
1427 +
			if(modmap->modifiermap[i * modmap->max_keypermod + j]
1428 +
					== XKeysymToKeycode(dpy, XK_Num_Lock))
1429 +
				numlockmask = (1 << i);
1241 1430
		}
1242 -
	if(ch.res_class)
1243 -
		XFree(ch.res_class);
1244 -
	if(ch.res_name)
1245 -
		XFree(ch.res_name);
1246 -
	if(!matched)
1247 -
		for(i = 0; i < ntags; i++)
1248 -
			c->tags[i] = seltags[i];
1431 +
	XFreeModifiermap(modmap);
1432 +
	/* select for events */
1433 +
	wa.event_mask = SubstructureRedirectMask | SubstructureNotifyMask
1434 +
		| EnterWindowMask | LeaveWindowMask | StructureNotifyMask;
1435 +
	wa.cursor = cursor[CurNormal];
1436 +
	XChangeWindowAttributes(dpy, root, CWEventMask | CWCursor, &wa);
1437 +
	XSelectInput(dpy, root, wa.event_mask);
1438 +
	keypress(NULL); /* grabkeys */
1439 +
	compileregs();
1440 +
	for(ntags = 0; tags[ntags]; ntags++);
1441 +
	seltags = emallocz(sizeof(Bool) * ntags);
1442 +
	seltags[0] = True;
1443 +
	/* geometry */
1444 +
	sx = sy = 0;
1445 +
	sw = DisplayWidth(dpy, screen);
1446 +
	sh = DisplayHeight(dpy, screen);
1447 +
	initstyle();
1448 +
	initlayouts();
1449 +
	initbar();
1450 +
	/* multihead support */
1451 +
	selscreen = XQueryPointer(dpy, root, &w, &w, &i, &i, &i, &i, &mask);
1249 1452
}
1250 1453
1251 1454
static void
1252 -
compileregs(void) {
1253 -
	unsigned int i;
1254 -
	regex_t *reg;
1455 +
spawn(const char *arg) {
1456 +
	static char *shell = NULL;
1255 1457
1256 -
	if(regs)
1458 +
	if(!shell && !(shell = getenv("SHELL")))
1459 +
		shell = "/bin/sh";
1460 +
	if(!arg)
1257 1461
		return;
1258 -
	nrules = sizeof rules / sizeof rules[0];
1259 -
	regs = emallocz(nrules * sizeof(Regs));
1260 -
	for(i = 0; i < nrules; i++) {
1261 -
		if(rules[i].prop) {
1262 -
			reg = emallocz(sizeof(regex_t));
1263 -
			if(regcomp(reg, rules[i].prop, REG_EXTENDED))
1264 -
				free(reg);
1265 -
			else
1266 -
				regs[i].propregex = reg;
1267 -
		}
1268 -
		if(rules[i].tags) {
1269 -
			reg = emallocz(sizeof(regex_t));
1270 -
			if(regcomp(reg, rules[i].tags, REG_EXTENDED))
1271 -
				free(reg);
1272 -
			else
1273 -
				regs[i].tagregex = reg;
1462 +
	/* The double-fork construct avoids zombie processes and keeps the code
1463 +
	 * clean from stupid signal handlers. */
1464 +
	if(fork() == 0) {
1465 +
		if(fork() == 0) {
1466 +
			if(dpy)
1467 +
				close(ConnectionNumber(dpy));
1468 +
			setsid();
1469 +
			execl(shell, shell, "-c", arg, (char *)NULL);
1470 +
			fprintf(stderr, "dwm: execl '%s -c %s'", shell, arg);
1471 +
			perror(" failed");
1274 1472
		}
1275 -
	}
1276 -
}
1277 -
1278 -
static void
1279 -
focusnext(const char *arg) {
1280 -
	Client *c;
1281 -
1282 -
	if(!sel)
1283 -
		return;
1284 -
	for(c = sel->next; c && !isvisible(c); c = c->next);
1285 -
	if(!c)
1286 -
		for(c = clients; c && !isvisible(c); c = c->next);
1287 -
	if(c) {
1288 -
		focus(c);
1289 -
		restack();
1473 +
		exit(0);
1290 1474
	}
1475 +
	wait(0);
1291 1476
}
1292 1477
1293 1478
static void
1294 -
focusprev(const char *arg) {
1295 -
	Client *c;
1479 +
tag(const char *arg) {
1480 +
	unsigned int i;
1296 1481
1297 1482
	if(!sel)
1298 1483
		return;
1299 -
	for(c = sel->prev; c && !isvisible(c); c = c->prev);
1300 -
	if(!c) {
1301 -
		for(c = clients; c && c->next; c = c->next);
1302 -
		for(; c && !isvisible(c); c = c->prev);
1303 -
	}
1304 -
	if(c) {
1305 -
		focus(c);
1306 -
		restack();
1307 -
	}
1484 +
	for(i = 0; i < ntags; i++)
1485 +
		sel->tags[i] = arg == NULL;
1486 +
	i = idxoftag(arg);
1487 +
	if(i >= 0 && i < ntags)
1488 +
		sel->tags[i] = True;
1489 +
	arrange();
1308 1490
}
1309 1491
1310 -
static void
1311 -
initlayouts(void) {
1312 -
	unsigned int i, w;
1492 +
static unsigned int
1493 +
textnw(const char *text, unsigned int len) {
1494 +
	XRectangle r;
1313 1495
1314 -
	nlayouts = sizeof layouts / sizeof layouts[0];
1315 -
	for(blw = i = 0; i < nlayouts; i++) {
1316 -
		w = textw(layouts[i].symbol);
1317 -
		if(w > blw)
1318 -
			blw = w;
1496 +
	if(dc.font.set) {
1497 +
		XmbTextExtents(dc.font.set, text, len, NULL, &r);
1498 +
		return r.width;
1319 1499
	}
1320 -
}
1321 -
1322 -
static Bool
1323 -
isfloating(void) {
1324 -
	return layouts[ltidx].arrange == floating;
1500 +
	return XTextWidth(dc.font.xfont, text, len);
1325 1501
}
1326 1502
1327 -
static Bool
1328 -
isvisible(Client *c) {
1329 -
	unsigned int i;
1330 -
1331 -
	for(i = 0; i < ntags; i++)
1332 -
		if(c->tags[i] && seltags[i])
1333 -
			return True;
1334 -
	return False;
1503 +
static unsigned int
1504 +
textw(const char *text) {
1505 +
	return textnw(text, strlen(text)) + dc.font.height;
1335 1506
}
1336 1507
1337 1508
static void
1338 -
restack(void) {
1509 +
tile(void) {
1510 +
	unsigned int i, n, nx, ny, nw, nh, mw, th;
1339 1511
	Client *c;
1340 -
	XEvent ev;
1341 -
	XWindowChanges wc;
1342 1512
1343 -
	drawbar();
1344 -
	if(!sel)
1345 -
		return;
1346 -
	if(sel->isfloating || isfloating())
1347 -
		XRaiseWindow(dpy, sel->win);
1348 -
	if(!isfloating()) {
1349 -
		wc.stack_mode = Below;
1350 -
		wc.sibling = barwin;
1351 -
		if(!sel->isfloating) {
1352 -
			XConfigureWindow(dpy, sel->win, CWSibling | CWStackMode, &wc);
1353 -
			wc.sibling = sel->win;
1513 +
	for(n = 0, c = nexttiled(clients); c; c = nexttiled(c->next))
1514 +
		n++;
1515 +
1516 +
	/* window geoms */
1517 +
	mw = (n == 1) ? waw : mwfact * waw;
1518 +
	th = (n > 1) ? wah / (n - 1) : 0;
1519 +
	if(n > 1 && th < bh)
1520 +
		th = wah;
1521 +
1522 +
	nx = wax;
1523 +
	ny = way;
1524 +
	for(i = 0, c = nexttiled(clients); c; c = nexttiled(c->next), i++) {
1525 +
		c->ismax = False;
1526 +
		if(i == 0) { /* master */
1527 +
			nw = mw - 2 * c->border;
1528 +
			nh = wah - 2 * c->border;
1354 1529
		}
1355 -
		for(c = nexttiled(clients); c; c = nexttiled(c->next)) {
1356 -
			if(c == sel)
1357 -
				continue;
1358 -
			XConfigureWindow(dpy, c->win, CWSibling | CWStackMode, &wc);
1359 -
			wc.sibling = c->win;
1530 +
		else {  /* tile window */
1531 +
			if(i == 1) {
1532 +
				ny = way;
1533 +
				nx += mw;
1534 +
			}
1535 +
			nw = waw - mw - 2 * c->border;
1536 +
			if(i + 1 == n) /* remainder */
1537 +
				nh = (way + wah) - ny - 2 * c->border;
1538 +
			else
1539 +
				nh = th - 2 * c->border;
1360 1540
		}
1541 +
		resize(c, nx, ny, nw, nh, RESIZEHINTS);
1542 +
		if(n > 1 && th != wah)
1543 +
			ny += nh + 2 * c->border;
1361 1544
	}
1362 -
	XSync(dpy, False);
1363 -
	while(XCheckMaskEvent(dpy, EnterWindowMask, &ev));
1364 1545
}
1365 1546
1366 1547
static void
1367 -
setlayout(const char *arg) {
1368 -
	unsigned int i;
1369 -
1370 -
	if(!arg) {
1371 -
		if(++ltidx == nlayouts)
1372 -
			ltidx = 0;;
1373 -
	}
1374 -
	else {
1375 -
		for(i = 0; i < nlayouts; i++)
1376 -
			if(!strcmp(arg, layouts[i].symbol))
1377 -
				break;
1378 -
		if(i == nlayouts)
1379 -
			return;
1380 -
		ltidx = i;
1381 -
	}
1382 -
	if(sel)
1383 -
		arrange();
1548 +
togglebar(const char *arg) {
1549 +
	if(bpos == BarOff)
1550 +
		bpos = (BARPOS == BarOff) ? BarTop : BARPOS;
1384 1551
	else
1385 -
		drawbar();
1386 -
}
1387 -
1388 -
static void
1389 -
tag(const char *arg) {
1390 -
	unsigned int i;
1391 -
1392 -
	if(!sel)
1393 -
		return;
1394 -
	for(i = 0; i < ntags; i++)
1395 -
		sel->tags[i] = arg == NULL;
1396 -
	i = idxoftag(arg);
1397 -
	if(i >= 0 && i < ntags)
1398 -
		sel->tags[i] = True;
1552 +
		bpos = BarOff;
1553 +
	updatebarpos();
1399 1554
	arrange();
1400 1555
}
1401 1556
1455 1610
}
1456 1611
1457 1612
static void
1458 -
view(const char *arg) {
1459 -
	unsigned int i;
1460 -
1461 -
	for(i = 0; i < ntags; i++)
1462 -
		seltags[i] = arg == NULL;
1463 -
	i = idxoftag(arg);
1464 -
	if(i >= 0 && i < ntags)
1465 -
		seltags[i] = True;
1466 -
	arrange();
1613 +
unban(Client *c) {
1614 +
	if(!c->isbanned)
1615 +
		return;
1616 +
	XMoveWindow(dpy, c->win, c->x, c->y);
1617 +
	c->isbanned = False;
1467 1618
}
1468 1619
1469 1620
static void
1470 -
cleanup(void) {
1471 -
	close(STDIN_FILENO);
1472 -
	while(stack) {
1473 -
		unban(stack);
1474 -
		unmanage(stack);
1475 -
	}
1476 -
	if(dc.font.set)
1477 -
		XFreeFontSet(dpy, dc.font.set);
1478 -
	else
1479 -
		XFreeFont(dpy, dc.font.xfont);
1480 -
	XUngrabKey(dpy, AnyKey, AnyModifier, root);
1481 -
	XFreePixmap(dpy, dc.drawable);
1482 -
	XFreeGC(dpy, dc.gc);
1483 -
	XDestroyWindow(dpy, barwin);
1484 -
	XFreeCursor(dpy, cursor[CurNormal]);
1485 -
	XFreeCursor(dpy, cursor[CurResize]);
1486 -
	XFreeCursor(dpy, cursor[CurMove]);
1487 -
	XSetInputFocus(dpy, PointerRoot, RevertToPointerRoot, CurrentTime);
1621 +
unmanage(Client *c) {
1622 +
	XWindowChanges wc;
1623 +
1624 +
	wc.border_width = c->oldborder;
1625 +
	/* The server grab construct avoids race conditions. */
1626 +
	XGrabServer(dpy);
1627 +
	XSetErrorHandler(xerrordummy);
1628 +
	XConfigureWindow(dpy, c->win, CWBorderWidth, &wc); /* restore border */
1629 +
	detach(c);
1630 +
	detachstack(c);
1631 +
	if(sel == c)
1632 +
		focus(NULL);
1633 +
	XUngrabButton(dpy, AnyButton, AnyModifier, c->win);
1634 +
	setclientstate(c, WithdrawnState);
1635 +
	free(c->tags);
1636 +
	free(c);
1488 1637
	XSync(dpy, False);
1489 -
	free(seltags);
1638 +
	XSetErrorHandler(xerror);
1639 +
	XUngrabServer(dpy);
1640 +
	arrange();
1490 1641
}
1491 1642
1492 -
static long
1493 -
getstate(Window w) {
1494 -
	int format, status;
1495 -
	long result = -1;
1496 -
	unsigned char *p = NULL;
1497 -
	unsigned long n, extra;
1498 -
	Atom real;
1643 +
static void
1644 +
unmapnotify(XEvent *e) {
1645 +
	Client *c;
1646 +
	XUnmapEvent *ev = &e->xunmap;
1499 1647
1500 -
	status = XGetWindowProperty(dpy, w, wmatom[WMState], 0L, 2L, False, wmatom[WMState],
1501 -
			&real, &format, &n, &extra, (unsigned char **)&p);
1502 -
	if(status != Success)
1503 -
		return -1;
1504 -
	if(n != 0)
1505 -
		result = *p;
1506 -
	XFree(p);
1507 -
	return result;
1648 +
	if((c = getclient(ev->window)))
1649 +
		unmanage(c);
1508 1650
}
1509 1651
1510 1652
static void
1511 -
scan(void) {
1512 -
	unsigned int i, num;
1513 -
	Window *wins, d1, d2;
1514 -
	XWindowAttributes wa;
1653 +
updatebarpos(void) {
1654 +
	XEvent ev;
1515 1655
1516 -
	wins = NULL;
1517 -
	if(XQueryTree(dpy, root, &d1, &d2, &wins, &num)) {
1518 -
		for(i = 0; i < num; i++) {
1519 -
			if(!XGetWindowAttributes(dpy, wins[i], &wa)
1520 -
			|| wa.override_redirect || XGetTransientForHint(dpy, wins[i], &d1))
1521 -
				continue;
1522 -
			if(wa.map_state == IsViewable || getstate(wins[i]) == IconicState)
1523 -
				manage(wins[i], &wa);
1524 -
		}
1525 -
		for(i = 0; i < num; i++) { /* now the transients */
1526 -
			if(!XGetWindowAttributes(dpy, wins[i], &wa))
1527 -
				continue;
1528 -
			if(XGetTransientForHint(dpy, wins[i], &d1)
1529 -
			&& (wa.map_state == IsViewable || getstate(wins[i]) == IconicState))
1530 -
				manage(wins[i], &wa);
1531 -
		}
1656 +
	wax = sx;
1657 +
	way = sy;
1658 +
	wah = sh;
1659 +
	waw = sw;
1660 +
	switch(bpos) {
1661 +
	default:
1662 +
		wah -= bh;
1663 +
		way += bh;
1664 +
		XMoveWindow(dpy, barwin, sx, sy);
1665 +
		break;
1666 +
	case BarBot:
1667 +
		wah -= bh;
1668 +
		XMoveWindow(dpy, barwin, sx, sy + wah);
1669 +
		break;
1670 +
	case BarOff:
1671 +
		XMoveWindow(dpy, barwin, sx, sy - bh);
1672 +
		break;
1532 1673
	}
1533 -
	if(wins)
1534 -
		XFree(wins);
1674 +
	XSync(dpy, False);
1675 +
	while(XCheckMaskEvent(dpy, EnterWindowMask, &ev));
1535 1676
}
1536 1677
1537 1678
static void
1538 -
setup(void) {
1539 -
	int i, j;
1540 -
	unsigned int mask;
1541 -
	Window w;
1542 -
	XModifierKeymap *modmap;
1543 -
	XSetWindowAttributes wa;
1544 -
1545 -
	/* init atoms */
1546 -
	wmatom[WMProtocols] = XInternAtom(dpy, "WM_PROTOCOLS", False);
1547 -
	wmatom[WMDelete] = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
1548 -
	wmatom[WMName] = XInternAtom(dpy, "WM_NAME", False);
1549 -
	wmatom[WMState] = XInternAtom(dpy, "WM_STATE", False);
1550 -
	netatom[NetSupported] = XInternAtom(dpy, "_NET_SUPPORTED", False);
1551 -
	netatom[NetWMName] = XInternAtom(dpy, "_NET_WM_NAME", False);
1552 -
	XChangeProperty(dpy, root, netatom[NetSupported], XA_ATOM, 32,
1553 -
			PropModeReplace, (unsigned char *) netatom, NetLast);
1554 -
	/* init cursors */
1555 -
	cursor[CurNormal] = XCreateFontCursor(dpy, XC_left_ptr);
1556 -
	cursor[CurResize] = XCreateFontCursor(dpy, XC_sizing);
1557 -
	cursor[CurMove] = XCreateFontCursor(dpy, XC_fleur);
1558 -
	/* init modifier map */
1559 -
	modmap = XGetModifierMapping(dpy);
1560 -
	for (i = 0; i < 8; i++)
1561 -
		for (j = 0; j < modmap->max_keypermod; j++) {
1562 -
			if(modmap->modifiermap[i * modmap->max_keypermod + j]
1563 -
					== XKeysymToKeycode(dpy, XK_Num_Lock))
1564 -
				numlockmask = (1 << i);
1565 -
		}
1566 -
	XFreeModifiermap(modmap);
1567 -
	/* select for events */
1568 -
	wa.event_mask = SubstructureRedirectMask | SubstructureNotifyMask
1569 -
		| EnterWindowMask | LeaveWindowMask | StructureNotifyMask;
1570 -
	wa.cursor = cursor[CurNormal];
1571 -
	XChangeWindowAttributes(dpy, root, CWEventMask | CWCursor, &wa);
1572 -
	XSelectInput(dpy, root, wa.event_mask);
1573 -
	keypress(NULL); /* grabkeys */
1574 -
	compileregs();
1575 -
	for(ntags = 0; tags[ntags]; ntags++);
1576 -
	seltags = emallocz(sizeof(Bool) * ntags);
1577 -
	seltags[0] = True;
1578 -
	/* geometry */
1579 -
	sx = sy = 0;
1580 -
	sw = DisplayWidth(dpy, screen);
1581 -
	sh = DisplayHeight(dpy, screen);
1582 -
	initstyle();
1583 -
	initlayouts();
1584 -
	initbar();
1585 -
	/* multihead support */
1586 -
	selscreen = XQueryPointer(dpy, root, &w, &w, &i, &i, &i, &i, &mask);
1587 -
}
1588 -
1589 -
/*
1590 -
 * Startup Error handler to check if another window manager
1591 -
 * is already running.
1592 -
 */
1593 -
static int
1594 -
xerrorstart(Display *dsply, XErrorEvent *ee) {
1595 -
	otherwm = True;
1596 -
	return -1;
1597 -
}
1598 -
1599 -
static Bool
1600 -
gettextprop(Window w, Atom atom, char *text, unsigned int size) {
1601 -
	char **list = NULL;
1602 -
	int n;
1603 -
	XTextProperty name;
1679 +
updatesizehints(Client *c) {
1680 +
	long msize;
1681 +
	XSizeHints size;
1604 1682
1605 -
	if(!text || size == 0)
1606 -
		return False;
1607 -
	text[0] = '\0';
1608 -
	XGetTextProperty(dpy, w, &name, atom);
1609 -
	if(!name.nitems)
1610 -
		return False;
1611 -
	if(name.encoding == XA_STRING)
1612 -
		strncpy(text, (char *)name.value, size - 1);
1613 -
	else {
1614 -
		if(XmbTextPropertyToTextList(dpy, &name, &list, &n) >= Success
1615 -
		&& n > 0 && *list)
1616 -
		{
1617 -
			strncpy(text, *list, size - 1);
1618 -
			XFreeStringList(list);
1619 -
		}
1683 +
	if(!XGetWMNormalHints(dpy, c->win, &size, &msize) || !size.flags)
1684 +
		size.flags = PSize;
1685 +
	c->flags = size.flags;
1686 +
	if(c->flags & PBaseSize) {
1687 +
		c->basew = size.base_width;
1688 +
		c->baseh = size.base_height;
1620 1689
	}
1621 -
	text[size - 1] = '\0';
1622 -
	XFree(name.value);
1623 -
	return True;
1690 +
	else if(c->flags & PMinSize) {
1691 +
		c->basew = size.min_width;
1692 +
		c->baseh = size.min_height;
1693 +
	}
1694 +
	else
1695 +
		c->basew = c->baseh = 0;
1696 +
	if(c->flags & PResizeInc) {
1697 +
		c->incw = size.width_inc;
1698 +
		c->inch = size.height_inc;
1699 +
	}
1700 +
	else
1701 +
		c->incw = c->inch = 0;
1702 +
	if(c->flags & PMaxSize) {
1703 +
		c->maxw = size.max_width;
1704 +
		c->maxh = size.max_height;
1705 +
	}
1706 +
	else
1707 +
		c->maxw = c->maxh = 0;
1708 +
	if(c->flags & PMinSize) {
1709 +
		c->minw = size.min_width;
1710 +
		c->minh = size.min_height;
1711 +
	}
1712 +
	else if(c->flags & PBaseSize) {
1713 +
		c->minw = size.base_width;
1714 +
		c->minh = size.base_height;
1715 +
	}
1716 +
	else
1717 +
		c->minw = c->minh = 0;
1718 +
	if(c->flags & PAspect) {
1719 +
		c->minax = size.min_aspect.x;
1720 +
		c->maxax = size.max_aspect.x;
1721 +
		c->minay = size.min_aspect.y;
1722 +
		c->maxay = size.max_aspect.y;
1723 +
	}
1724 +
	else
1725 +
		c->minax = c->maxax = c->minay = c->maxay = 0;
1726 +
	c->isfixed = (c->maxw && c->minw && c->maxh && c->minh
1727 +
			&& c->maxw == c->minw && c->maxh == c->minh);
1624 1728
}
1625 1729
1626 1730
static void
1627 -
quit(const char *arg) {
1628 -
	readin = running = False;
1731 +
updatetitle(Client *c) {
1732 +
	if(!gettextprop(c->win, netatom[NetWMName], c->name, sizeof c->name))
1733 +
		gettextprop(c->win, wmatom[WMName], c->name, sizeof c->name);
1629 1734
}
1630 1735
1631 1736
/* There's no way to check accesses to destroyed windows, thus those cases are
1632 1737
 * ignored (especially on UnmapNotify's).  Other types of errors call Xlibs
1633 -
 * default error handler, which may call exit.
1634 -
 */
1738 +
 * default error handler, which may call exit.  */
1635 1739
static int
1636 1740
xerror(Display *dpy, XErrorEvent *ee) {
1637 1741
	if(ee->error_code == BadWindow
1648 1752
	return xerrorxlib(dpy, ee); /* may call exit */
1649 1753
}
1650 1754
1651 -
static void
1652 -
arrange(void) {
1653 -
	Client *c;
1654 -
1655 -
	for(c = clients; c; c = c->next)
1656 -
		if(isvisible(c))
1657 -
			unban(c);
1658 -
		else
1659 -
			ban(c);
1660 -
	layouts[ltidx].arrange();
1661 -
	focus(NULL);
1662 -
	restack();
1663 -
}
1664 -
1665 -
static void
1666 -
attach(Client *c) {
1667 -
	if(clients)
1668 -
		clients->prev = c;
1669 -
	c->next = clients;
1670 -
	clients = c;
1671 -
}
1672 -
1673 -
static void
1674 -
detach(Client *c) {
1675 -
	if(c->prev)
1676 -
		c->prev->next = c->next;
1677 -
	if(c->next)
1678 -
		c->next->prev = c->prev;
1679 -
	if(c == clients)
1680 -
		clients = c->next;
1681 -
	c->next = c->prev = NULL;
1682 -
}
1683 -
1684 -
static void
1685 -
focus(Client *c) {
1686 -
	if((!c && selscreen) || (c && !isvisible(c)))
1687 -
		for(c = stack; c && !isvisible(c); c = c->snext);
1688 -
	if(sel && sel != c) {
1689 -
		grabbuttons(sel, False);
1690 -
		XSetWindowBorder(dpy, sel->win, dc.norm[ColBorder]);
1691 -
	}
1692 -
	if(c) {
1693 -
		detachstack(c);
1694 -
		attachstack(c);
1695 -
		grabbuttons(c, True);
1696 -
	}
1697 -
	sel = c;
1698 -
	drawbar();
1699 -
	if(!selscreen)
1700 -
		return;
1701 -
	if(c) {
1702 -
		XSetWindowBorder(dpy, c->win, dc.sel[ColBorder]);
1703 -
		XSetInputFocus(dpy, c->win, RevertToPointerRoot, CurrentTime);
1704 -
	}
1705 -
	else
1706 -
		XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime);
1707 -
}
1708 -
1709 -
static Bool
1710 -
isarrange(void (*func)())
1711 -
{
1712 -
	return func == layouts[ltidx].arrange;
1755 +
static int
1756 +
xerrordummy(Display *dsply, XErrorEvent *ee) {
1757 +
	return 0;
1713 1758
}
1714 1759
1715 -
static Client *
1716 -
nexttiled(Client *c) {
1717 -
	for(; c && (c->isfloating || !isvisible(c)); c = c->next);
1718 -
	return c;
1760 +
/* Startup Error handler to check if another window manager
1761 +
 * is already running. */
1762 +
static int
1763 +
xerrorstart(Display *dsply, XErrorEvent *ee) {
1764 +
	otherwm = True;
1765 +
	return -1;
1719 1766
}
1720 1767
1721 1768
static void
1722 -
setmwfact(const char *arg) {
1723 -
	double delta;
1769 +
view(const char *arg) {
1770 +
	unsigned int i;
1724 1771
1725 -
	if(!isarrange(tile))
1726 -
		return;
1727 -
	/* arg handling, manipulate mwfact */
1728 -
	if(arg == NULL)
1729 -
		mwfact = MWFACT;
1730 -
	else if(1 == sscanf(arg, "%lf", &delta)) {
1731 -
		if(arg[0] != '+' && arg[0] != '-')
1732 -
			mwfact = delta;
1733 -
		else
1734 -
			mwfact += delta;
1735 -
		if(mwfact < 0.1)
1736 -
			mwfact = 0.1;
1737 -
		else if(mwfact > 0.9)
1738 -
			mwfact = 0.9;
1739 -
	}
1772 +
	for(i = 0; i < ntags; i++)
1773 +
		seltags[i] = arg == NULL;
1774 +
	i = idxoftag(arg);
1775 +
	if(i >= 0 && i < ntags)
1776 +
		seltags[i] = True;
1740 1777
	arrange();
1741 -
}
1742 -
1743 -
static void
1744 -
tile(void) {
1745 -
	unsigned int i, n, nx, ny, nw, nh, mw, th;
1746 -
	Client *c;
1747 -
1748 -
	for(n = 0, c = nexttiled(clients); c; c = nexttiled(c->next))
1749 -
		n++;
1750 -
1751 -
	/* window geoms */
1752 -
	mw = (n == 1) ? waw : mwfact * waw;
1753 -
	th = (n > 1) ? wah / (n - 1) : 0;
1754 -
	if(n > 1 && th < bh)
1755 -
		th = wah;
1756 -
1757 -
	nx = wax;
1758 -
	ny = way;
1759 -
	for(i = 0, c = nexttiled(clients); c; c = nexttiled(c->next), i++) {
1760 -
		c->ismax = False;
1761 -
		if(i == 0) { /* master */
1762 -
			nw = mw - 2 * c->border;
1763 -
			nh = wah - 2 * c->border;
1764 -
		}
1765 -
		else {  /* tile window */
1766 -
			if(i == 1) {
1767 -
				ny = way;
1768 -
				nx += mw;
1769 -
			}
1770 -
			nw = waw - mw - 2 * c->border;
1771 -
			if(i + 1 == n) /* remainder */
1772 -
				nh = (way + wah) - ny - 2 * c->border;
1773 -
			else
1774 -
				nh = th - 2 * c->border;
1775 -
		}
1776 -
		resize(c, nx, ny, nw, nh, RESIZEHINTS);
1777 -
		if(n > 1 && th != wah)
1778 -
			ny += nh + 2 * c->border;
1779 -
	}
1780 1778
}
1781 1779
1782 1780
static void