dwm.c
changeset 996 b4d47b6a8ba8
parent 995 693bda958a30
child 997 8e721021e636
equal deleted inserted replaced
995:693bda958a30 996:b4d47b6a8ba8
   231 static Display *dpy;
   231 static Display *dpy;
   232 static DC dc = {0};
   232 static DC dc = {0};
   233 static Window barwin, root;
   233 static Window barwin, root;
   234 static Regs *regs = NULL;
   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 #include "config.h"
   237 #include "config.h"
   238 
   238 
   239 static void
   239 /* implementation */
   240 eprint(const char *errstr, ...) {
   240 static void
   241 	va_list ap;
   241 applyrules(Client *c) {
   242 
   242 	static char buf[512];
   243 	va_start(ap, errstr);
   243 	unsigned int i, j;
   244 	vfprintf(stderr, errstr, ap);
   244 	regmatch_t tmp;
   245 	va_end(ap);
   245 	Bool matched = False;
   246 	exit(EXIT_FAILURE);
   246 	XClassHint ch = { 0 };
   247 }
   247 
   248 
   248 	/* rule matching */
   249 static void *
   249 	XGetClassHint(dpy, c->win, &ch);
   250 emallocz(unsigned int size) {
   250 	snprintf(buf, sizeof buf, "%s:%s:%s",
   251 	void *res = calloc(1, size);
   251 			ch.res_class ? ch.res_class : "",
   252 
   252 			ch.res_name ? ch.res_name : "", c->name);
   253 	if(!res)
   253 	for(i = 0; i < nrules; i++)
   254 		eprint("fatal: could not malloc() %u bytes\n", size);
   254 		if(regs[i].propregex && !regexec(regs[i].propregex, buf, 1, &tmp, 0)) {
   255 	return res;
   255 			c->isfloating = rules[i].isfloating;
   256 }
   256 			for(j = 0; regs[i].tagregex && j < ntags; j++) {
   257 
   257 				if(!regexec(regs[i].tagregex, tags[j], 1, &tmp, 0)) {
   258 static void
   258 					matched = True;
   259 spawn(const char *arg) {
   259 					c->tags[j] = True;
   260 	static char *shell = NULL;
   260 				}
   261 
   261 			}
   262 	if(!shell && !(shell = getenv("SHELL")))
   262 		}
   263 		shell = "/bin/sh";
   263 	if(ch.res_class)
   264 	if(!arg)
   264 		XFree(ch.res_class);
   265 		return;
   265 	if(ch.res_name)
   266 	/* The double-fork construct avoids zombie processes and keeps the code
   266 		XFree(ch.res_name);
   267 	 * clean from stupid signal handlers. */
   267 	if(!matched)
   268 	if(fork() == 0) {
   268 		for(i = 0; i < ntags; i++)
   269 		if(fork() == 0) {
   269 			c->tags[i] = seltags[i];
   270 			if(dpy)
   270 }
   271 				close(ConnectionNumber(dpy));
   271 
   272 			setsid();
   272 static void
   273 			execl(shell, shell, "-c", arg, (char *)NULL);
   273 arrange(void) {
   274 			fprintf(stderr, "dwm: execl '%s -c %s'", shell, arg);
   274 	Client *c;
   275 			perror(" failed");
   275 
   276 		}
   276 	for(c = clients; c; c = c->next)
   277 		exit(0);
   277 		if(isvisible(c))
   278 	}
   278 			unban(c);
   279 	wait(0);
   279 		else
       
   280 			ban(c);
       
   281 	layouts[ltidx].arrange();
       
   282 	focus(NULL);
       
   283 	restack();
       
   284 }
       
   285 
       
   286 static void
       
   287 attach(Client *c) {
       
   288 	if(clients)
       
   289 		clients->prev = c;
       
   290 	c->next = clients;
       
   291 	clients = c;
       
   292 }
       
   293 
       
   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)
       
   303 		return;
       
   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 			}
       
   333 		}
       
   334 		if((ev->x < x + blw) && ev->button == Button1)
       
   335 			setlayout(NULL);
       
   336 	}
       
   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 	}
       
   354 }
       
   355 
       
   356 static void
       
   357 cleanup(void) {
       
   358 	close(STDIN_FILENO);
       
   359 	while(stack) {
       
   360 		unban(stack);
       
   361 		unmanage(stack);
       
   362 	}
       
   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 		}
       
   403 	}
       
   404 }
       
   405 
       
   406 static void
       
   407 configure(Client *c) {
       
   408 	XConfigureEvent ce;
       
   409 
       
   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);
       
   422 }
       
   423 
       
   424 static void
       
   425 configurenotify(XEvent *e) {
       
   426 	XConfigureEvent *ev = &e->xconfigure;
       
   427 
       
   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();
       
   436 	}
       
   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);
       
   467 		}
       
   468 		else
       
   469 			configure(c);
       
   470 	}
       
   471 	else {
       
   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);
       
   480 	}
       
   481 	XSync(dpy, False);
       
   482 }
       
   483 
       
   484 static void
       
   485 destroynotify(XEvent *e) {
       
   486 	Client *c;
       
   487 	XDestroyWindowEvent *ev = &e->xdestroywindow;
       
   488 
       
   489 	if((c = getclient(ev->window)))
       
   490 		unmanage(c);
       
   491 }
       
   492 
       
   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 }
       
   503 
       
   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;
       
   528 	}
       
   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);
   280 }
   550 }
   281 
   551 
   282 static void
   552 static void
   283 drawsquare(Bool filled, Bool empty, unsigned long col[ColLast]) {
   553 drawsquare(Bool filled, Bool empty, unsigned long col[ColLast]) {
   284 	int x;
   554 	int x;
   296 	}
   566 	}
   297 	else if(empty) {
   567 	else if(empty) {
   298 		r.width = r.height = x;
   568 		r.width = r.height = x;
   299 		XDrawRectangles(dpy, dc.drawable, dc.gc, &r, 1);
   569 		XDrawRectangles(dpy, dc.drawable, dc.gc, &r, 1);
   300 	}
   570 	}
   301 }
       
   302 
       
   303 static unsigned long
       
   304 initcolor(const char *colstr) {
       
   305 	Colormap cmap = DefaultColormap(dpy, screen);
       
   306 	XColor color;
       
   307 
       
   308 	if(!XAllocNamedColor(dpy, cmap, colstr, &color, &color))
       
   309 		eprint("error, cannot allocate color '%s'\n", colstr);
       
   310 	return color.pixel;
       
   311 }
       
   312 
       
   313 static void
       
   314 initfont(const char *fontstr) {
       
   315 	char *def, **missing;
       
   316 	int i, n;
       
   317 
       
   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);
       
   326 	}
       
   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++;
       
   340 		}
       
   341 	}
       
   342 	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;
       
   351 	}
       
   352 	dc.font.height = dc.font.ascent + dc.font.descent;
       
   353 }
       
   354 
       
   355 static Bool
       
   356 isoccupied(unsigned int t) {
       
   357 	Client *c;
       
   358 
       
   359 	for(c = clients; c; c = c->next)
       
   360 		if(c->tags[t])
       
   361 			return True;
       
   362 	return False;
       
   363 }
       
   364 
       
   365 static unsigned int
       
   366 textnw(const char *text, unsigned int len) {
       
   367 	XRectangle r;
       
   368 
       
   369 	if(dc.font.set) {
       
   370 		XmbTextExtents(dc.font.set, text, len, NULL, &r);
       
   371 		return r.width;
       
   372 	}
       
   373 	return XTextWidth(dc.font.xfont, text, len);
       
   374 }
   571 }
   375 
   572 
   376 static void
   573 static void
   377 drawtext(const char *text, unsigned long col[ColLast]) {
   574 drawtext(const char *text, unsigned long col[ColLast]) {
   378 	int x, y, w, h;
   575 	int x, y, w, h;
   411 		XmbDrawString(dpy, dc.drawable, dc.font.set, dc.gc, x, y, buf, len);
   608 		XmbDrawString(dpy, dc.drawable, dc.font.set, dc.gc, x, y, buf, len);
   412 	else
   609 	else
   413 		XDrawString(dpy, dc.drawable, dc.gc, x, y, buf, len);
   610 		XDrawString(dpy, dc.drawable, dc.gc, x, y, buf, len);
   414 }
   611 }
   415 
   612 
   416 static void
   613 static void *
   417 drawbar(void) {
   614 emallocz(unsigned int size) {
   418 	int i, x;
   615 	void *res = calloc(1, size);
   419 
   616 
   420 	dc.x = dc.y = 0;
   617 	if(!res)
   421 	for(i = 0; i < ntags; i++) {
   618 		eprint("fatal: could not malloc() %u bytes\n", size);
   422 		dc.w = textw(tags[i]);
   619 	return res;
   423 		if(seltags[i]) {
   620 }
   424 			drawtext(tags[i], dc.sel);
   621 
   425 			drawsquare(sel && sel->tags[i], isoccupied(i), dc.sel);
   622 static void
   426 		}
   623 enternotify(XEvent *e) {
   427 		else {
   624 	Client *c;
   428 			drawtext(tags[i], dc.norm);
   625 	XCrossingEvent *ev = &e->xcrossing;
   429 			drawsquare(sel && sel->tags[i], isoccupied(i), dc.norm);
   626 
   430 		}
   627 	if(ev->mode != NotifyNormal || ev->detail == NotifyInferior)
   431 		dc.x += dc.w;
   628 		return;
   432 	}
   629 	if((c = getclient(ev->window)))
   433 	dc.w = blw;
   630 		focus(c);
   434 	drawtext(layouts[ltidx].symbol, dc.norm);
   631 	else if(ev->window == root) {
   435 	x = dc.x + dc.w;
   632 		selscreen = True;
   436 	dc.w = textw(stext);
   633 		focus(NULL);
   437 	dc.x = sw - dc.w;
   634 	}
   438 	if(dc.x < x) {
   635 }
   439 		dc.x = x;
   636 
   440 		dc.w = sw - x;
   637 static void
   441 	}
   638 eprint(const char *errstr, ...) {
   442 	drawtext(stext, dc.norm);
   639 	va_list ap;
   443 	if((dc.w = dc.x - x) > bh) {
   640 
   444 		dc.x = x;
   641 	va_start(ap, errstr);
   445 		if(sel) {
   642 	vfprintf(stderr, errstr, ap);
   446 			drawtext(sel->name, dc.sel);
   643 	va_end(ap);
   447 			drawsquare(sel->ismax, sel->isfloating, dc.sel);
   644 	exit(EXIT_FAILURE);
   448 		}
   645 }
   449 		else
   646 
   450 			drawtext(NULL, dc.norm);
   647 static void
   451 	}
   648 expose(XEvent *e) {
   452 	XCopyArea(dpy, dc.drawable, barwin, dc.gc, 0, 0, sw, bh, 0, 0);
   649 	XExposeEvent *ev = &e->xexpose;
   453 	XSync(dpy, False);
   650 
   454 }
   651 	if(ev->count == 0) {
   455 
   652 		if(barwin == ev->window)
   456 static void
   653 			drawbar();
   457 initstyle(void) {
   654 	}
   458 	dc.norm[ColBorder] = initcolor(NORMBORDERCOLOR);
   655 }
   459 	dc.norm[ColBG] = initcolor(NORMBGCOLOR);
   656 
   460 	dc.norm[ColFG] = initcolor(NORMFGCOLOR);
   657 static void
   461 	dc.sel[ColBorder] = initcolor(SELBORDERCOLOR);
   658 floating(void) { /* default floating layout */
   462 	dc.sel[ColBG] = initcolor(SELBGCOLOR);
   659 	Client *c;
   463 	dc.sel[ColFG] = initcolor(SELFGCOLOR);
   660 
   464 	initfont(FONT);
   661 	for(c = clients; c; c = c->next)
   465 	dc.h = bh = dc.font.height + 2;
   662 		if(isvisible(c))
       
   663 			resize(c, c->x, c->y, c->w, c->h, True);
       
   664 }
       
   665 
       
   666 static void
       
   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 	}
       
   687 	else
       
   688 		XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime);
       
   689 }
       
   690 
       
   691 static void
       
   692 focusnext(const char *arg) {
       
   693 	Client *c;
       
   694 
       
   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();
       
   703 	}
       
   704 }
       
   705 
       
   706 static void
       
   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 	}
       
   721 }
       
   722 
       
   723 static Client *
       
   724 getclient(Window w) {
       
   725 	Client *c;
       
   726 
       
   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;
       
   774 }
       
   775 
       
   776 static void
       
   777 grabbuttons(Client *c, Bool focused) {
       
   778 	XUngrabButton(dpy, AnyButton, AnyModifier, c->win);
       
   779 
       
   780 	if(focused) {
       
   781 		XGrabButton(dpy, Button1, MODKEY, c->win, False, BUTTONMASK,
       
   782 				GrabModeAsync, GrabModeSync, None, None);
       
   783 		XGrabButton(dpy, Button1, MODKEY | LockMask, c->win, False, BUTTONMASK,
       
   784 				GrabModeAsync, GrabModeSync, None, None);
       
   785 		XGrabButton(dpy, Button1, MODKEY | numlockmask, c->win, False, BUTTONMASK,
       
   786 				GrabModeAsync, GrabModeSync, None, None);
       
   787 		XGrabButton(dpy, Button1, MODKEY | numlockmask | LockMask, c->win, False, BUTTONMASK,
       
   788 				GrabModeAsync, GrabModeSync, None, None);
       
   789 
       
   790 		XGrabButton(dpy, Button2, MODKEY, c->win, False, BUTTONMASK,
       
   791 				GrabModeAsync, GrabModeSync, None, None);
       
   792 		XGrabButton(dpy, Button2, MODKEY | LockMask, c->win, False, BUTTONMASK,
       
   793 				GrabModeAsync, GrabModeSync, None, None);
       
   794 		XGrabButton(dpy, Button2, MODKEY | numlockmask, c->win, False, BUTTONMASK,
       
   795 				GrabModeAsync, GrabModeSync, None, None);
       
   796 		XGrabButton(dpy, Button2, MODKEY | numlockmask | LockMask, c->win, False, BUTTONMASK,
       
   797 				GrabModeAsync, GrabModeSync, None, None);
       
   798 
       
   799 		XGrabButton(dpy, Button3, MODKEY, c->win, False, BUTTONMASK,
       
   800 				GrabModeAsync, GrabModeSync, None, None);
       
   801 		XGrabButton(dpy, Button3, MODKEY | LockMask, c->win, False, BUTTONMASK,
       
   802 				GrabModeAsync, GrabModeSync, None, None);
       
   803 		XGrabButton(dpy, Button3, MODKEY | numlockmask, c->win, False, BUTTONMASK,
       
   804 				GrabModeAsync, GrabModeSync, None, None);
       
   805 		XGrabButton(dpy, Button3, MODKEY | numlockmask | LockMask, c->win, False, BUTTONMASK,
       
   806 				GrabModeAsync, GrabModeSync, None, None);
       
   807 	}
       
   808 	else
       
   809 		XGrabButton(dpy, AnyButton, AnyModifier, c->win, False, BUTTONMASK,
       
   810 				GrabModeAsync, GrabModeSync, None, None);
       
   811 }
       
   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;
   466 }
   821 }
   467 
   822 
   468 static void
   823 static void
   469 initbar(void) {
   824 initbar(void) {
   470 	XSetWindowAttributes wa;
   825 	XSetWindowAttributes wa;
   484 	XSetLineAttributes(dpy, dc.gc, 1, LineSolid, CapButt, JoinMiter);
   839 	XSetLineAttributes(dpy, dc.gc, 1, LineSolid, CapButt, JoinMiter);
   485 	if(!dc.font.set)
   840 	if(!dc.font.set)
   486 		XSetFont(dpy, dc.gc, dc.font.xfont->fid);
   841 		XSetFont(dpy, dc.gc, dc.font.xfont->fid);
   487 }
   842 }
   488 
   843 
   489 static unsigned int
   844 static unsigned long
   490 textw(const char *text) {
   845 initcolor(const char *colstr) {
   491 	return textnw(text, strlen(text)) + dc.font.height;
   846 	Colormap cmap = DefaultColormap(dpy, screen);
   492 }
   847 	XColor color;
   493 
   848 
   494 static void
   849 	if(!XAllocNamedColor(dpy, cmap, colstr, &color, &color))
   495 togglebar(const char *arg) {
   850 		eprint("error, cannot allocate color '%s'\n", colstr);
   496 	if(bpos == BarOff)
   851 	return color.pixel;
   497 		bpos = (BARPOS == BarOff) ? BarTop : BARPOS;
   852 }
   498 	else
   853 
   499 		bpos = BarOff;
   854 static void
   500 	updatebarpos();
   855 initfont(const char *fontstr) {
   501 	arrange();
   856 	char *def, **missing;
   502 }
   857 	int i, n;
   503 
   858 
   504 static void
   859 	missing = NULL;
   505 updatebarpos(void) {
   860 	if(dc.font.set)
   506 	XEvent ev;
   861 		XFreeFontSet(dpy, dc.font.set);
   507 
   862 	dc.font.set = XCreateFontSet(dpy, fontstr, &missing, &n, &def);
   508 	wax = sx;
   863 	if(missing) {
   509 	way = sy;
   864 		while(n--)
   510 	wah = sh;
   865 			fprintf(stderr, "dwm: missing fontset: %s\n", missing[n]);
   511 	waw = sw;
   866 		XFreeStringList(missing);
   512 	switch(bpos) {
   867 	}
   513 	default:
   868 	if(dc.font.set) {
   514 		wah -= bh;
   869 		XFontSetExtents *font_extents;
   515 		way += bh;
   870 		XFontStruct **xfonts;
   516 		XMoveWindow(dpy, barwin, sx, sy);
   871 		char **font_names;
   517 		break;
   872 		dc.font.ascent = dc.font.descent = 0;
   518 	case BarBot:
   873 		font_extents = XExtentsOfFontSet(dc.font.set);
   519 		wah -= bh;
   874 		n = XFontsOfFontSet(dc.font.set, &xfonts, &font_names);
   520 		XMoveWindow(dpy, barwin, sx, sy + wah);
   875 		for(i = 0, dc.font.ascent = 0, dc.font.descent = 0; i < n; i++) {
   521 		break;
   876 			if(dc.font.ascent < (*xfonts)->ascent)
   522 	case BarOff:
   877 				dc.font.ascent = (*xfonts)->ascent;
   523 		XMoveWindow(dpy, barwin, sx, sy - bh);
   878 			if(dc.font.descent < (*xfonts)->descent)
   524 		break;
   879 				dc.font.descent = (*xfonts)->descent;
   525 	}
   880 			xfonts++;
   526 	XSync(dpy, False);
   881 		}
   527 	while(XCheckMaskEvent(dpy, EnterWindowMask, &ev));
   882 	}
   528 }
   883 	else {
   529 
   884 		if(dc.font.xfont)
   530 static void
   885 			XFreeFont(dpy, dc.font.xfont);
   531 attachstack(Client *c) {
   886 		dc.font.xfont = NULL;
   532 	c->snext = stack;
   887 		if(!(dc.font.xfont = XLoadQueryFont(dpy, fontstr))
   533 	stack = c;
   888 		|| !(dc.font.xfont = XLoadQueryFont(dpy, "fixed")))
   534 }
   889 			eprint("error, cannot load font: '%s'\n", fontstr);
   535 
   890 		dc.font.ascent = dc.font.xfont->ascent;
   536 static void
   891 		dc.font.descent = dc.font.xfont->descent;
   537 detachstack(Client *c) {
   892 	}
   538 	Client **tc;
   893 	dc.font.height = dc.font.ascent + dc.font.descent;
   539 
   894 }
   540 	for(tc=&stack; *tc && *tc != c; tc=&(*tc)->snext);
   895 
   541 	*tc = c->snext;
   896 static void
   542 }
   897 initlayouts(void) {
   543 
   898 	unsigned int i, w;
   544 static void
   899 
   545 grabbuttons(Client *c, Bool focused) {
   900 	nlayouts = sizeof layouts / sizeof layouts[0];
   546 	XUngrabButton(dpy, AnyButton, AnyModifier, c->win);
   901 	for(blw = i = 0; i < nlayouts; i++) {
   547 
   902 		w = textw(layouts[i].symbol);
   548 	if(focused) {
   903 		if(w > blw)
   549 		XGrabButton(dpy, Button1, MODKEY, c->win, False, BUTTONMASK,
   904 			blw = w;
   550 				GrabModeAsync, GrabModeSync, None, None);
   905 	}
   551 		XGrabButton(dpy, Button1, MODKEY | LockMask, c->win, False, BUTTONMASK,
   906 }
   552 				GrabModeAsync, GrabModeSync, None, None);
   907 
   553 		XGrabButton(dpy, Button1, MODKEY | numlockmask, c->win, False, BUTTONMASK,
   908 static void
   554 				GrabModeAsync, GrabModeSync, None, None);
   909 initstyle(void) {
   555 		XGrabButton(dpy, Button1, MODKEY | numlockmask | LockMask, c->win, False, BUTTONMASK,
   910 	dc.norm[ColBorder] = initcolor(NORMBORDERCOLOR);
   556 				GrabModeAsync, GrabModeSync, None, None);
   911 	dc.norm[ColBG] = initcolor(NORMBGCOLOR);
   557 
   912 	dc.norm[ColFG] = initcolor(NORMFGCOLOR);
   558 		XGrabButton(dpy, Button2, MODKEY, c->win, False, BUTTONMASK,
   913 	dc.sel[ColBorder] = initcolor(SELBORDERCOLOR);
   559 				GrabModeAsync, GrabModeSync, None, None);
   914 	dc.sel[ColBG] = initcolor(SELBGCOLOR);
   560 		XGrabButton(dpy, Button2, MODKEY | LockMask, c->win, False, BUTTONMASK,
   915 	dc.sel[ColFG] = initcolor(SELFGCOLOR);
   561 				GrabModeAsync, GrabModeSync, None, None);
   916 	initfont(FONT);
   562 		XGrabButton(dpy, Button2, MODKEY | numlockmask, c->win, False, BUTTONMASK,
   917 	dc.h = bh = dc.font.height + 2;
   563 				GrabModeAsync, GrabModeSync, None, None);
   918 }
   564 		XGrabButton(dpy, Button2, MODKEY | numlockmask | LockMask, c->win, False, BUTTONMASK,
   919 
   565 				GrabModeAsync, GrabModeSync, None, None);
   920 static Bool
   566 
   921 isarrange(void (*func)())
   567 		XGrabButton(dpy, Button3, MODKEY, c->win, False, BUTTONMASK,
   922 {
   568 				GrabModeAsync, GrabModeSync, None, None);
   923 	return func == layouts[ltidx].arrange;
   569 		XGrabButton(dpy, Button3, MODKEY | LockMask, c->win, False, BUTTONMASK,
   924 }
   570 				GrabModeAsync, GrabModeSync, None, None);
   925 
   571 		XGrabButton(dpy, Button3, MODKEY | numlockmask, c->win, False, BUTTONMASK,
   926 static Bool
   572 				GrabModeAsync, GrabModeSync, None, None);
   927 isfloating(void) {
   573 		XGrabButton(dpy, Button3, MODKEY | numlockmask | LockMask, c->win, False, BUTTONMASK,
   928 	return layouts[ltidx].arrange == floating;
   574 				GrabModeAsync, GrabModeSync, None, None);
   929 }
   575 	}
   930 
   576 	else
   931 static Bool
   577 		XGrabButton(dpy, AnyButton, AnyModifier, c->win, False, BUTTONMASK,
   932 isoccupied(unsigned int t) {
   578 				GrabModeAsync, GrabModeSync, None, None);
   933 	Client *c;
       
   934 
       
   935 	for(c = clients; c; c = c->next)
       
   936 		if(c->tags[t])
       
   937 			return True;
       
   938 	return False;
   579 }
   939 }
   580 
   940 
   581 static Bool
   941 static Bool
   582 isprotodel(Client *c) {
   942 isprotodel(Client *c) {
   583 	int i, n;
   943 	int i, n;
   591 		XFree(protocols);
   951 		XFree(protocols);
   592 	}
   952 	}
   593 	return ret;
   953 	return ret;
   594 }
   954 }
   595 
   955 
   596 static void
   956 static Bool
   597 setclientstate(Client *c, long state) {
   957 isvisible(Client *c) {
   598 	long data[] = {state, None};
   958 	unsigned int i;
   599 
   959 
   600 	XChangeProperty(dpy, c->win, wmatom[WMState], wmatom[WMState], 32,
   960 	for(i = 0; i < ntags; i++)
   601 			PropModeReplace, (unsigned char *)data, 2);
   961 		if(c->tags[i] && seltags[i])
   602 }
   962 			return True;
   603 
   963 	return False;
   604 static int
   964 }
   605 xerrordummy(Display *dsply, XErrorEvent *ee) {
   965 
   606 	return 0;
   966 static void
   607 }
   967 keypress(XEvent *e) {
   608 
   968 	KEYS
   609 static void
   969 	unsigned int len = sizeof keys / sizeof keys[0];
   610 ban(Client *c) {
   970 	unsigned int i;
   611 	if(c->isbanned)
   971 	KeyCode code;
   612 		return;
   972 	KeySym keysym;
   613 	XMoveWindow(dpy, c->win, c->x + 2 * sw, c->y);
   973 	XKeyEvent *ev;
   614 	c->isbanned = True;
   974 
   615 }
   975 	if(!e) { /* grabkeys */
   616 
   976 		XUngrabKey(dpy, AnyKey, AnyModifier, root);
   617 static void
   977 		for(i = 0; i < len; i++) {
   618 configure(Client *c) {
   978 			code = XKeysymToKeycode(dpy, keys[i].keysym);
   619 	XConfigureEvent ce;
   979 			XGrabKey(dpy, code, keys[i].mod, root, True,
   620 
   980 					GrabModeAsync, GrabModeAsync);
   621 	ce.type = ConfigureNotify;
   981 			XGrabKey(dpy, code, keys[i].mod | LockMask, root, True,
   622 	ce.display = dpy;
   982 					GrabModeAsync, GrabModeAsync);
   623 	ce.event = c->win;
   983 			XGrabKey(dpy, code, keys[i].mod | numlockmask, root, True,
   624 	ce.window = c->win;
   984 					GrabModeAsync, GrabModeAsync);
   625 	ce.x = c->x;
   985 			XGrabKey(dpy, code, keys[i].mod | numlockmask | LockMask, root, True,
   626 	ce.y = c->y;
   986 					GrabModeAsync, GrabModeAsync);
   627 	ce.width = c->w;
   987 		}
   628 	ce.height = c->h;
   988 		return;
   629 	ce.border_width = c->border;
   989 	}
   630 	ce.above = None;
   990 	ev = &e->xkey;
   631 	ce.override_redirect = False;
   991 	keysym = XKeycodeToKeysym(dpy, (KeyCode)ev->keycode, 0);
   632 	XSendEvent(dpy, c->win, False, StructureNotifyMask, (XEvent *)&ce);
   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 static void
  1001 static void
   636 killclient(const char *arg) {
  1002 killclient(const char *arg) {
   637 	XEvent ev;
  1003 	XEvent ev;
   647 		ev.xclient.data.l[1] = CurrentTime;
  1013 		ev.xclient.data.l[1] = CurrentTime;
   648 		XSendEvent(dpy, sel->win, False, NoEventMask, &ev);
  1014 		XSendEvent(dpy, sel->win, False, NoEventMask, &ev);
   649 	}
  1015 	}
   650 	else
  1016 	else
   651 		XKillClient(dpy, sel->win);
  1017 		XKillClient(dpy, sel->win);
       
  1018 }
       
  1019 
       
  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 	}
   652 }
  1028 }
   653 
  1029 
   654 static void
  1030 static void
   655 manage(Window w, XWindowAttributes *wa) {
  1031 manage(Window w, XWindowAttributes *wa) {
   656 	unsigned int i;
  1032 	unsigned int i;
   708 	setclientstate(c, NormalState);
  1084 	setclientstate(c, NormalState);
   709 	arrange();
  1085 	arrange();
   710 }
  1086 }
   711 
  1087 
   712 static void
  1088 static void
   713 resize(Client *c, int x, int y, int w, int h, Bool sizehints) {
  1089 mappingnotify(XEvent *e) {
   714 	double dx, dy, max, min, ratio;
  1090 	XMappingEvent *ev = &e->xmapping;
   715 	XWindowChanges wc; 
  1091 
   716 
  1092 	XRefreshKeyboardMapping(ev);
   717 	if(sizehints) {
  1093 	if(ev->request == MappingKeyboard)
   718 		if(c->minay > 0 && c->maxay > 0 && (h - c->baseh) > 0 && (w - c->basew) > 0) {
  1094 		keypress(NULL);
   719 			dx = (double)(w - c->basew);
  1095 }
   720 			dy = (double)(h - c->baseh);
  1096 
   721 			min = (double)(c->minax) / (double)(c->minay);
  1097 static void
   722 			max = (double)(c->maxax) / (double)(c->maxay);
  1098 maprequest(XEvent *e) {
   723 			ratio = dx / dy;
  1099 	static XWindowAttributes wa;
   724 			if(max > 0 && min > 0 && ratio > 0) {
  1100 	XMapRequestEvent *ev = &e->xmaprequest;
   725 				if(ratio < min) {
  1101 
   726 					dy = (dx * min + dy) / (min * min + 1);
  1102 	if(!XGetWindowAttributes(dpy, ev->window, &wa))
   727 					dx = dy * min;
  1103 		return;
   728 					w = (int)dx + c->basew;
  1104 	if(wa.override_redirect)
   729 					h = (int)dy + c->baseh;
  1105 		return;
   730 				}
  1106 	if(!getclient(ev->window))
   731 				else if(ratio > max) {
  1107 		manage(ev->window, &wa);
   732 					dy = (dx * min + dy) / (max * max + 1);
       
   733 					dx = dy * min;
       
   734 					w = (int)dx + c->basew;
       
   735 					h = (int)dy + c->baseh;
       
   736 				}
       
   737 			}
       
   738 		}
       
   739 		if(c->minw && w < c->minw)
       
   740 			w = c->minw;
       
   741 		if(c->minh && h < c->minh)
       
   742 			h = c->minh;
       
   743 		if(c->maxw && w > c->maxw)
       
   744 			w = c->maxw;
       
   745 		if(c->maxh && h > c->maxh)
       
   746 			h = c->maxh;
       
   747 		if(c->incw)
       
   748 			w -= (w - c->basew) % c->incw;
       
   749 		if(c->inch)
       
   750 			h -= (h - c->baseh) % c->inch;
       
   751 	}
       
   752 	if(w <= 0 || h <= 0)
       
   753 		return;
       
   754 	/* offscreen appearance fixes */
       
   755 	if(x > sw)
       
   756 		x = sw - w - 2 * c->border;
       
   757 	if(y > sh)
       
   758 		y = sh - h - 2 * c->border;
       
   759 	if(x + w + 2 * c->border < sx)
       
   760 		x = sx;
       
   761 	if(y + h + 2 * c->border < sy)
       
   762 		y = sy;
       
   763 	if(c->x != x || c->y != y || c->w != w || c->h != h) {
       
   764 		c->x = wc.x = x;
       
   765 		c->y = wc.y = y;
       
   766 		c->w = wc.width = w;
       
   767 		c->h = wc.height = h;
       
   768 		wc.border_width = c->border;
       
   769 		XConfigureWindow(dpy, c->win, CWX | CWY | CWWidth | CWHeight | CWBorderWidth, &wc);
       
   770 		configure(c);
       
   771 		XSync(dpy, False);
       
   772 	}
       
   773 }
       
   774 
       
   775 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 }
  1108 }
   871 
  1109 
   872 static void
  1110 static void
   873 movemouse(Client *c) {
  1111 movemouse(Client *c) {
   874 	int x1, y1, ocx, ocy, di, nx, ny;
  1112 	int x1, y1, ocx, ocy, di, nx, ny;
   910 			break;
  1148 			break;
   911 		}
  1149 		}
   912 	}
  1150 	}
   913 }
  1151 }
   914 
  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
       
  1193 resize(Client *c, int x, int y, int w, int h, Bool sizehints) {
       
  1194 	double dx, dy, max, min, ratio;
       
  1195 	XWindowChanges wc; 
       
  1196 
       
  1197 	if(sizehints) {
       
  1198 		if(c->minay > 0 && c->maxay > 0 && (h - c->baseh) > 0 && (w - c->basew) > 0) {
       
  1199 			dx = (double)(w - c->basew);
       
  1200 			dy = (double)(h - c->baseh);
       
  1201 			min = (double)(c->minax) / (double)(c->minay);
       
  1202 			max = (double)(c->maxax) / (double)(c->maxay);
       
  1203 			ratio = dx / dy;
       
  1204 			if(max > 0 && min > 0 && ratio > 0) {
       
  1205 				if(ratio < min) {
       
  1206 					dy = (dx * min + dy) / (min * min + 1);
       
  1207 					dx = dy * min;
       
  1208 					w = (int)dx + c->basew;
       
  1209 					h = (int)dy + c->baseh;
       
  1210 				}
       
  1211 				else if(ratio > max) {
       
  1212 					dy = (dx * min + dy) / (max * max + 1);
       
  1213 					dx = dy * min;
       
  1214 					w = (int)dx + c->basew;
       
  1215 					h = (int)dy + c->baseh;
       
  1216 				}
       
  1217 			}
       
  1218 		}
       
  1219 		if(c->minw && w < c->minw)
       
  1220 			w = c->minw;
       
  1221 		if(c->minh && h < c->minh)
       
  1222 			h = c->minh;
       
  1223 		if(c->maxw && w > c->maxw)
       
  1224 			w = c->maxw;
       
  1225 		if(c->maxh && h > c->maxh)
       
  1226 			h = c->maxh;
       
  1227 		if(c->incw)
       
  1228 			w -= (w - c->basew) % c->incw;
       
  1229 		if(c->inch)
       
  1230 			h -= (h - c->baseh) % c->inch;
       
  1231 	}
       
  1232 	if(w <= 0 || h <= 0)
       
  1233 		return;
       
  1234 	/* offscreen appearance fixes */
       
  1235 	if(x > sw)
       
  1236 		x = sw - w - 2 * c->border;
       
  1237 	if(y > sh)
       
  1238 		y = sh - h - 2 * c->border;
       
  1239 	if(x + w + 2 * c->border < sx)
       
  1240 		x = sx;
       
  1241 	if(y + h + 2 * c->border < sy)
       
  1242 		y = sy;
       
  1243 	if(c->x != x || c->y != y || c->w != w || c->h != h) {
       
  1244 		c->x = wc.x = x;
       
  1245 		c->y = wc.y = y;
       
  1246 		c->w = wc.width = w;
       
  1247 		c->h = wc.height = h;
       
  1248 		wc.border_width = c->border;
       
  1249 		XConfigureWindow(dpy, c->win, CWX | CWY | CWWidth | CWHeight | CWBorderWidth, &wc);
       
  1250 		configure(c);
       
  1251 		XSync(dpy, False);
       
  1252 	}
       
  1253 }
       
  1254 
   915 static void
  1255 static void
   916 resizemouse(Client *c) {
  1256 resizemouse(Client *c) {
   917 	int ocx, ocy;
  1257 	int ocx, ocy;
   918 	int nw, nh;
  1258 	int nw, nh;
   919 	XEvent ev;
  1259 	XEvent ev;
   950 		}
  1290 		}
   951 	}
  1291 	}
   952 }
  1292 }
   953 
  1293 
   954 static void
  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) {
       
  1004 	Client *c;
       
  1005 	XConfigureRequestEvent *ev = &e->xconfigurerequest;
       
  1006 	XWindowChanges wc;
       
  1007 
       
  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);
       
  1030 		}
       
  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);
       
  1043 	}
       
  1044 	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);
       
  1069 }
       
  1070 
       
  1071 static void
       
  1072 enternotify(XEvent *e) {
       
  1073 	Client *c;
       
  1074 	XCrossingEvent *ev = &e->xcrossing;
       
  1075 
       
  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);
       
  1083 	}
       
  1084 }
       
  1085 
       
  1086 static void
       
  1087 expose(XEvent *e) {
       
  1088 	XExposeEvent *ev = &e->xexpose;
       
  1089 
       
  1090 	if(ev->count == 0) {
       
  1091 		if(barwin == ev->window)
       
  1092 			drawbar();
       
  1093 	}
       
  1094 }
       
  1095 
       
  1096 static void
       
  1097 keypress(XEvent *e) {
       
  1098 	KEYS
       
  1099 	unsigned int len = sizeof keys / sizeof keys[0];
       
  1100 	unsigned int i;
       
  1101 	KeyCode code;
       
  1102 	KeySym keysym;
       
  1103 	XKeyEvent *ev;
       
  1104 
       
  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;
       
  1119 	}
       
  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);
       
  1138 	}
       
  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);
       
  1148 }
       
  1149 
       
  1150 static void
       
  1151 maprequest(XEvent *e) {
       
  1152 	static XWindowAttributes wa;
       
  1153 	XMapRequestEvent *ev = &e->xmaprequest;
       
  1154 
       
  1155 	if(!XGetWindowAttributes(dpy, ev->window, &wa))
       
  1156 		return;
       
  1157 	if(wa.override_redirect)
       
  1158 		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 		}
       
  1188 	}
       
  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);
       
  1217 }
       
  1218 
       
  1219 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 };
       
  1226 
       
  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 			}
       
  1241 		}
       
  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];
       
  1249 }
       
  1250 
       
  1251 static void
       
  1252 compileregs(void) {
       
  1253 	unsigned int i;
       
  1254 	regex_t *reg;
       
  1255 
       
  1256 	if(regs)
       
  1257 		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;
       
  1274 		}
       
  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();
       
  1290 	}
       
  1291 }
       
  1292 
       
  1293 static void
       
  1294 focusprev(const char *arg) {
       
  1295 	Client *c;
       
  1296 
       
  1297 	if(!sel)
       
  1298 		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 	}
       
  1308 }
       
  1309 
       
  1310 static void
       
  1311 initlayouts(void) {
       
  1312 	unsigned int i, w;
       
  1313 
       
  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;
       
  1319 	}
       
  1320 }
       
  1321 
       
  1322 static Bool
       
  1323 isfloating(void) {
       
  1324 	return layouts[ltidx].arrange == floating;
       
  1325 }
       
  1326 
       
  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;
       
  1335 }
       
  1336 
       
  1337 static void
       
  1338 restack(void) {
  1295 restack(void) {
  1339 	Client *c;
  1296 	Client *c;
  1340 	XEvent ev;
  1297 	XEvent ev;
  1341 	XWindowChanges wc;
  1298 	XWindowChanges wc;
  1342 
  1299 
  1362 	XSync(dpy, False);
  1319 	XSync(dpy, False);
  1363 	while(XCheckMaskEvent(dpy, EnterWindowMask, &ev));
  1320 	while(XCheckMaskEvent(dpy, EnterWindowMask, &ev));
  1364 }
  1321 }
  1365 
  1322 
  1366 static void
  1323 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();
       
  1384 	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;
       
  1399 	arrange();
       
  1400 }
       
  1401 
       
  1402 static void
       
  1403 togglefloating(const char *arg) {
       
  1404 	if(!sel)
       
  1405 		return;
       
  1406 	sel->isfloating = !sel->isfloating;
       
  1407 	if(sel->isfloating)
       
  1408 		resize(sel, sel->x, sel->y, sel->w, sel->h, True);
       
  1409 	arrange();
       
  1410 }
       
  1411 
       
  1412 static void
       
  1413 togglemax(const char *arg) {
       
  1414 	XEvent ev;
       
  1415 
       
  1416 	if(!sel || (!isfloating() && !sel->isfloating) || sel->isfixed)
       
  1417 		return;
       
  1418 	if((sel->ismax = !sel->ismax)) {
       
  1419 		sel->rx = sel->x;
       
  1420 		sel->ry = sel->y;
       
  1421 		sel->rw = sel->w;
       
  1422 		sel->rh = sel->h;
       
  1423 		resize(sel, wax, way, waw - 2 * sel->border, wah - 2 * sel->border, True);
       
  1424 	}
       
  1425 	else
       
  1426 		resize(sel, sel->rx, sel->ry, sel->rw, sel->rh, True);
       
  1427 	drawbar();
       
  1428 	while(XCheckMaskEvent(dpy, EnterWindowMask, &ev));
       
  1429 }
       
  1430 
       
  1431 static void
       
  1432 toggletag(const char *arg) {
       
  1433 	unsigned int i, j;
       
  1434 
       
  1435 	if(!sel)
       
  1436 		return;
       
  1437 	i = idxoftag(arg);
       
  1438 	sel->tags[i] = !sel->tags[i];
       
  1439 	for(j = 0; j < ntags && !sel->tags[j]; j++);
       
  1440 	if(j == ntags)
       
  1441 		sel->tags[i] = True;
       
  1442 	arrange();
       
  1443 }
       
  1444 
       
  1445 static void
       
  1446 toggleview(const char *arg) {
       
  1447 	unsigned int i, j;
       
  1448 
       
  1449 	i = idxoftag(arg);
       
  1450 	seltags[i] = !seltags[i];
       
  1451 	for(j = 0; j < ntags && !seltags[j]; j++);
       
  1452 	if(j == ntags)
       
  1453 		seltags[i] = True; /* cannot toggle last view */
       
  1454 	arrange();
       
  1455 }
       
  1456 
       
  1457 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();
       
  1467 }
       
  1468 
       
  1469 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);
       
  1488 	XSync(dpy, False);
       
  1489 	free(seltags);
       
  1490 }
       
  1491 
       
  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;
       
  1499 
       
  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;
       
  1508 }
       
  1509 
       
  1510 static void
       
  1511 scan(void) {
  1324 scan(void) {
  1512 	unsigned int i, num;
  1325 	unsigned int i, num;
  1513 	Window *wins, d1, d2;
  1326 	Window *wins, d1, d2;
  1514 	XWindowAttributes wa;
  1327 	XWindowAttributes wa;
  1515 
  1328 
  1530 				manage(wins[i], &wa);
  1343 				manage(wins[i], &wa);
  1531 		}
  1344 		}
  1532 	}
  1345 	}
  1533 	if(wins)
  1346 	if(wins)
  1534 		XFree(wins);
  1347 		XFree(wins);
       
  1348 }
       
  1349 
       
  1350 static void
       
  1351 setclientstate(Client *c, long state) {
       
  1352 	long data[] = {state, None};
       
  1353 
       
  1354 	XChangeProperty(dpy, c->win, wmatom[WMState], wmatom[WMState], 32,
       
  1355 			PropModeReplace, (unsigned char *)data, 2);
       
  1356 }
       
  1357 
       
  1358 static void
       
  1359 setlayout(const char *arg) {
       
  1360 	unsigned int i;
       
  1361 
       
  1362 	if(!arg) {
       
  1363 		if(++ltidx == nlayouts)
       
  1364 			ltidx = 0;;
       
  1365 	}
       
  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;
       
  1373 	}
       
  1374 	if(sel)
       
  1375 		arrange();
       
  1376 	else
       
  1377 		drawbar();
       
  1378 }
       
  1379 
       
  1380 static void
       
  1381 setmwfact(const char *arg) {
       
  1382 	double delta;
       
  1383 
       
  1384 	if(!isarrange(tile))
       
  1385 		return;
       
  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;
       
  1398 	}
       
  1399 	arrange();
  1535 }
  1400 }
  1536 
  1401 
  1537 static void
  1402 static void
  1538 setup(void) {
  1403 setup(void) {
  1539 	int i, j;
  1404 	int i, j;
  1584 	initbar();
  1449 	initbar();
  1585 	/* multihead support */
  1450 	/* multihead support */
  1586 	selscreen = XQueryPointer(dpy, root, &w, &w, &i, &i, &i, &i, &mask);
  1451 	selscreen = XQueryPointer(dpy, root, &w, &w, &i, &i, &i, &i, &mask);
  1587 }
  1452 }
  1588 
  1453 
  1589 /*
  1454 static void
  1590  * Startup Error handler to check if another window manager
  1455 spawn(const char *arg) {
  1591  * is already running.
  1456 	static char *shell = NULL;
  1592  */
  1457 
  1593 static int
  1458 	if(!shell && !(shell = getenv("SHELL")))
  1594 xerrorstart(Display *dsply, XErrorEvent *ee) {
  1459 		shell = "/bin/sh";
  1595 	otherwm = True;
  1460 	if(!arg)
  1596 	return -1;
  1461 		return;
  1597 }
  1462 	/* The double-fork construct avoids zombie processes and keeps the code
  1598 
  1463 	 * clean from stupid signal handlers. */
  1599 static Bool
  1464 	if(fork() == 0) {
  1600 gettextprop(Window w, Atom atom, char *text, unsigned int size) {
  1465 		if(fork() == 0) {
  1601 	char **list = NULL;
  1466 			if(dpy)
  1602 	int n;
  1467 				close(ConnectionNumber(dpy));
  1603 	XTextProperty name;
  1468 			setsid();
  1604 
  1469 			execl(shell, shell, "-c", arg, (char *)NULL);
  1605 	if(!text || size == 0)
  1470 			fprintf(stderr, "dwm: execl '%s -c %s'", shell, arg);
  1606 		return False;
  1471 			perror(" failed");
  1607 	text[0] = '\0';
  1472 		}
  1608 	XGetTextProperty(dpy, w, &name, atom);
  1473 		exit(0);
  1609 	if(!name.nitems)
  1474 	}
  1610 		return False;
  1475 	wait(0);
  1611 	if(name.encoding == XA_STRING)
  1476 }
  1612 		strncpy(text, (char *)name.value, size - 1);
  1477 
  1613 	else {
  1478 static void
  1614 		if(XmbTextPropertyToTextList(dpy, &name, &list, &n) >= Success
  1479 tag(const char *arg) {
  1615 		&& n > 0 && *list)
  1480 	unsigned int i;
  1616 		{
  1481 
  1617 			strncpy(text, *list, size - 1);
  1482 	if(!sel)
  1618 			XFreeStringList(list);
  1483 		return;
  1619 		}
  1484 	for(i = 0; i < ntags; i++)
  1620 	}
  1485 		sel->tags[i] = arg == NULL;
  1621 	text[size - 1] = '\0';
  1486 	i = idxoftag(arg);
  1622 	XFree(name.value);
  1487 	if(i >= 0 && i < ntags)
  1623 	return True;
  1488 		sel->tags[i] = True;
  1624 }
  1489 	arrange();
  1625 
  1490 }
  1626 static void
  1491 
  1627 quit(const char *arg) {
  1492 static unsigned int
  1628 	readin = running = False;
  1493 textnw(const char *text, unsigned int len) {
       
  1494 	XRectangle r;
       
  1495 
       
  1496 	if(dc.font.set) {
       
  1497 		XmbTextExtents(dc.font.set, text, len, NULL, &r);
       
  1498 		return r.width;
       
  1499 	}
       
  1500 	return XTextWidth(dc.font.xfont, text, len);
       
  1501 }
       
  1502 
       
  1503 static unsigned int
       
  1504 textw(const char *text) {
       
  1505 	return textnw(text, strlen(text)) + dc.font.height;
       
  1506 }
       
  1507 
       
  1508 static void
       
  1509 tile(void) {
       
  1510 	unsigned int i, n, nx, ny, nw, nh, mw, th;
       
  1511 	Client *c;
       
  1512 
       
  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;
       
  1529 		}
       
  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;
       
  1540 		}
       
  1541 		resize(c, nx, ny, nw, nh, RESIZEHINTS);
       
  1542 		if(n > 1 && th != wah)
       
  1543 			ny += nh + 2 * c->border;
       
  1544 	}
       
  1545 }
       
  1546 
       
  1547 static void
       
  1548 togglebar(const char *arg) {
       
  1549 	if(bpos == BarOff)
       
  1550 		bpos = (BARPOS == BarOff) ? BarTop : BARPOS;
       
  1551 	else
       
  1552 		bpos = BarOff;
       
  1553 	updatebarpos();
       
  1554 	arrange();
       
  1555 }
       
  1556 
       
  1557 static void
       
  1558 togglefloating(const char *arg) {
       
  1559 	if(!sel)
       
  1560 		return;
       
  1561 	sel->isfloating = !sel->isfloating;
       
  1562 	if(sel->isfloating)
       
  1563 		resize(sel, sel->x, sel->y, sel->w, sel->h, True);
       
  1564 	arrange();
       
  1565 }
       
  1566 
       
  1567 static void
       
  1568 togglemax(const char *arg) {
       
  1569 	XEvent ev;
       
  1570 
       
  1571 	if(!sel || (!isfloating() && !sel->isfloating) || sel->isfixed)
       
  1572 		return;
       
  1573 	if((sel->ismax = !sel->ismax)) {
       
  1574 		sel->rx = sel->x;
       
  1575 		sel->ry = sel->y;
       
  1576 		sel->rw = sel->w;
       
  1577 		sel->rh = sel->h;
       
  1578 		resize(sel, wax, way, waw - 2 * sel->border, wah - 2 * sel->border, True);
       
  1579 	}
       
  1580 	else
       
  1581 		resize(sel, sel->rx, sel->ry, sel->rw, sel->rh, True);
       
  1582 	drawbar();
       
  1583 	while(XCheckMaskEvent(dpy, EnterWindowMask, &ev));
       
  1584 }
       
  1585 
       
  1586 static void
       
  1587 toggletag(const char *arg) {
       
  1588 	unsigned int i, j;
       
  1589 
       
  1590 	if(!sel)
       
  1591 		return;
       
  1592 	i = idxoftag(arg);
       
  1593 	sel->tags[i] = !sel->tags[i];
       
  1594 	for(j = 0; j < ntags && !sel->tags[j]; j++);
       
  1595 	if(j == ntags)
       
  1596 		sel->tags[i] = True;
       
  1597 	arrange();
       
  1598 }
       
  1599 
       
  1600 static void
       
  1601 toggleview(const char *arg) {
       
  1602 	unsigned int i, j;
       
  1603 
       
  1604 	i = idxoftag(arg);
       
  1605 	seltags[i] = !seltags[i];
       
  1606 	for(j = 0; j < ntags && !seltags[j]; j++);
       
  1607 	if(j == ntags)
       
  1608 		seltags[i] = True; /* cannot toggle last view */
       
  1609 	arrange();
       
  1610 }
       
  1611 
       
  1612 static void
       
  1613 unban(Client *c) {
       
  1614 	if(!c->isbanned)
       
  1615 		return;
       
  1616 	XMoveWindow(dpy, c->win, c->x, c->y);
       
  1617 	c->isbanned = False;
       
  1618 }
       
  1619 
       
  1620 static void
       
  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);
       
  1637 	XSync(dpy, False);
       
  1638 	XSetErrorHandler(xerror);
       
  1639 	XUngrabServer(dpy);
       
  1640 	arrange();
       
  1641 }
       
  1642 
       
  1643 static void
       
  1644 unmapnotify(XEvent *e) {
       
  1645 	Client *c;
       
  1646 	XUnmapEvent *ev = &e->xunmap;
       
  1647 
       
  1648 	if((c = getclient(ev->window)))
       
  1649 		unmanage(c);
       
  1650 }
       
  1651 
       
  1652 static void
       
  1653 updatebarpos(void) {
       
  1654 	XEvent ev;
       
  1655 
       
  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;
       
  1673 	}
       
  1674 	XSync(dpy, False);
       
  1675 	while(XCheckMaskEvent(dpy, EnterWindowMask, &ev));
       
  1676 }
       
  1677 
       
  1678 static void
       
  1679 updatesizehints(Client *c) {
       
  1680 	long msize;
       
  1681 	XSizeHints size;
       
  1682 
       
  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;
       
  1689 	}
       
  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);
       
  1728 }
       
  1729 
       
  1730 static void
       
  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 /* There's no way to check accesses to destroyed windows, thus those cases are
  1736 /* There's no way to check accesses to destroyed windows, thus those cases are
  1632  * ignored (especially on UnmapNotify's).  Other types of errors call Xlibs
  1737  * ignored (especially on UnmapNotify's).  Other types of errors call Xlibs
  1633  * default error handler, which may call exit.
  1738  * default error handler, which may call exit.  */
  1634  */
       
  1635 static int
  1739 static int
  1636 xerror(Display *dpy, XErrorEvent *ee) {
  1740 xerror(Display *dpy, XErrorEvent *ee) {
  1637 	if(ee->error_code == BadWindow
  1741 	if(ee->error_code == BadWindow
  1638 	|| (ee->request_code == X_SetInputFocus && ee->error_code == BadMatch)
  1742 	|| (ee->request_code == X_SetInputFocus && ee->error_code == BadMatch)
  1639 	|| (ee->request_code == X_PolyText8 && ee->error_code == BadDrawable)
  1743 	|| (ee->request_code == X_PolyText8 && ee->error_code == BadDrawable)
  1646 	fprintf(stderr, "dwm: fatal error: request code=%d, error code=%d\n",
  1750 	fprintf(stderr, "dwm: fatal error: request code=%d, error code=%d\n",
  1647 		ee->request_code, ee->error_code);
  1751 		ee->request_code, ee->error_code);
  1648 	return xerrorxlib(dpy, ee); /* may call exit */
  1752 	return xerrorxlib(dpy, ee); /* may call exit */
  1649 }
  1753 }
  1650 
  1754 
  1651 static void
  1755 static int
  1652 arrange(void) {
  1756 xerrordummy(Display *dsply, XErrorEvent *ee) {
  1653 	Client *c;
  1757 	return 0;
  1654 
  1758 }
  1655 	for(c = clients; c; c = c->next)
  1759 
  1656 		if(isvisible(c))
  1760 /* Startup Error handler to check if another window manager
  1657 			unban(c);
  1761  * is already running. */
  1658 		else
  1762 static int
  1659 			ban(c);
  1763 xerrorstart(Display *dsply, XErrorEvent *ee) {
  1660 	layouts[ltidx].arrange();
  1764 	otherwm = True;
  1661 	focus(NULL);
  1765 	return -1;
  1662 	restack();
  1766 }
  1663 }
  1767 
  1664 
  1768 static void
  1665 static void
  1769 view(const char *arg) {
  1666 attach(Client *c) {
  1770 	unsigned int i;
  1667 	if(clients)
  1771 
  1668 		clients->prev = c;
  1772 	for(i = 0; i < ntags; i++)
  1669 	c->next = clients;
  1773 		seltags[i] = arg == NULL;
  1670 	clients = c;
  1774 	i = idxoftag(arg);
  1671 }
  1775 	if(i >= 0 && i < ntags)
  1672 
  1776 		seltags[i] = True;
  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;
       
  1713 }
       
  1714 
       
  1715 static Client *
       
  1716 nexttiled(Client *c) {
       
  1717 	for(; c && (c->isfloating || !isvisible(c)); c = c->next);
       
  1718 	return c;
       
  1719 }
       
  1720 
       
  1721 static void
       
  1722 setmwfact(const char *arg) {
       
  1723 	double delta;
       
  1724 
       
  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 	}
       
  1740 	arrange();
  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 static void
  1780 static void
  1783 zoom(const char *arg) {
  1781 zoom(const char *arg) {
  1784 	Client *c;
  1782 	Client *c;