wmii

git clone git://oldgit.suckless.org/wmii/
Log | Files | Refs | README | LICENSE

message.c (20457B)


      1 /* Copyright ©2006-2010 Kris Maglione <maglione.k at Gmail>
      2  * See LICENSE file for license details.
      3  */
      4 #include "dat.h"
      5 #include <ctype.h>
      6 #include "fns.h"
      7 
      8 static char* msg_grow(View*, IxpMsg*);
      9 static char* msg_nudge(View*, IxpMsg*);
     10 static char* msg_selectframe(Area*, IxpMsg*, int);
     11 static char* msg_sendframe(Frame*, int, bool);
     12 
     13 #define DIR(s) (\
     14 	s == LUP	? North : \
     15 	s == LDOWN	? South : \
     16 	s == LLEFT	? West  : \
     17 	s == LRIGHT	? East  : \
     18 	(error(Ebadvalue), 0))
     19 
     20 static char
     21 	Ebadcmd[] = "bad command",
     22 	Ebadvalue[] = "bad value",
     23 	Ebadusage[] = "bad usage";
     24 
     25 /* Edit |sort Edit |sed 's/"([^"]+)"/L\1/g' | tr 'a-z' 'A-Z' */
     26 enum {
     27 	LALLOW,
     28 	LBAR,
     29 	LBORDER,
     30 	LCLIENT,
     31 	LCOLMODE,
     32 	LCOLORS,
     33 	LDEBUG,
     34 	LDOWN,
     35 	LEXEC,
     36 	LFLOATING,
     37 	LFOCUSCOLORS,
     38 	LFONT,
     39 	LFONTPAD,
     40 	LFULLSCREEN,
     41 	LGRABMOD,
     42 	LGROUP,
     43 	LGROW,
     44 	LINCMODE,
     45 	LKILL,
     46 	LLABEL,
     47 	LLEFT,
     48 	LNORMCOLORS,
     49 	LNUDGE,
     50 	LOFF,
     51 	LON,
     52 	LQUIT,
     53 	LRIGHT,
     54 	LSELCOLORS,
     55 	LSELECT,
     56 	LSEND,
     57 	LSLAY,
     58 	LSPAWN,
     59 	LSWAP,
     60 	LTAGS,
     61 	LTOGGLE,
     62 	LUP,
     63 	LURGENT,
     64 	LVIEW,
     65 	LTILDE,
     66 };
     67 char *symtab[] = {
     68 	"allow",
     69 	"bar",
     70 	"border",
     71 	"client",
     72 	"colmode",
     73 	"colors",
     74 	"debug",
     75 	"down",
     76 	"exec",
     77 	"floating",
     78 	"focuscolors",
     79 	"font",
     80 	"fontpad",
     81 	"fullscreen",
     82 	"grabmod",
     83 	"group",
     84 	"grow",
     85 	"incmode",
     86 	"kill",
     87 	"label",
     88 	"left",
     89 	"normcolors",
     90 	"nudge",
     91 	"off",
     92 	"on",
     93 	"quit",
     94 	"right",
     95 	"selcolors",
     96 	"select",
     97 	"send",
     98 	"slay",
     99 	"spawn",
    100 	"swap",
    101 	"tags",
    102 	"toggle",
    103 	"up",
    104 	"urgent",
    105 	"view",
    106 	"~",
    107 };
    108 
    109 static char* barpostab[] = {
    110 	"bottom", "top",
    111 };
    112 char* debugtab[] = {
    113 	"9p",
    114 	"dnd",
    115 	"event",
    116 	"ewmh",
    117 	"focus",
    118 	"generic",
    119 	"stack",
    120 	nil
    121 };
    122 static char* permtab[] = {
    123 	"activate", nil
    124 };
    125 static char* incmodetab[] = {
    126 	"ignore", "show", "squeeze",
    127 };
    128 static char* floatingtab[] = {
    129 	"never", "off", "on", "always"
    130 };
    131 static char* toggletab[] = {
    132 	"off", "on", "toggle",
    133 };
    134 
    135 /* Edit ,y/^[a-zA-Z].*\n.* {\n/d
    136  * Edit s/^([a-zA-Z].*)\n(.*) {\n/\1 \2;\n/
    137  * Edit ,x/^static.*\n/d
    138  */
    139 
    140 static int
    141 _bsearch(char *from, char **tab, int ntab) {
    142 	int i, n, m, cmp;
    143 	char *to, *end;
    144 	Rune r;
    145 
    146 	if(from == nil)
    147 		return -1;
    148 
    149 	end = buffer + sizeof buffer - UTFmax - 1;
    150 	for(to=buffer; *from && to < end;) {
    151 		from += chartorune(&r, from);
    152 		if(r != 0x80) {
    153 			r = tolowerrune(r);
    154 			to += runetochar(to, &r);
    155 		}
    156 	}
    157 	*to = '\0';
    158 	to = buffer;
    159 
    160 	n = ntab;
    161 	i = 0;
    162 	while(n) {
    163 		m = n/2;
    164 		cmp = strcmp(to, tab[i+m]);
    165 		if(cmp == 0)
    166 			return i+m;
    167 		if(cmp < 0 || m == 0)
    168 			n = m;
    169 		else {
    170 			i += m;
    171 			n = n-m;
    172 		}
    173 	}
    174 	return -1;
    175 }
    176 
    177 static int
    178 _lsearch(char *from, char **tab, int ntab) {
    179 	int i;
    180 
    181 	if(from != nil)
    182 		for(i=0; i < ntab; i++)
    183 			if(!strcmp(from, tab[i]))
    184 				return i;
    185 	error(Ebadvalue);
    186 	return 0;
    187 }
    188 
    189 static int
    190 getsym(char *s) {
    191 	return _bsearch(s, symtab, nelem(symtab));
    192 }
    193 
    194 static void
    195 setdef(int *ptr, char *s, char *tab[], int ntab) {
    196 	int i;
    197 
    198 	i = _bsearch(s, tab, ntab);
    199 	if(i < 0)
    200 		error(Ebadvalue);
    201 	*ptr = i;
    202 }
    203 
    204 static int
    205 gettoggle(char *s) {
    206 	return _lsearch(s, toggletab, nelem(toggletab));
    207 }
    208 
    209 static int
    210 getdirection(IxpMsg *m) {
    211 	int i;
    212 
    213 	switch(i = getsym(msg_getword(m, 0))) {
    214 	case LLEFT:
    215 	case LRIGHT:
    216 	case LUP:
    217 	case LDOWN:
    218 		return i;
    219 	}
    220 	error(Ebadusage);
    221 	return -1;
    222 }
    223 
    224 static ulong
    225 msg_getulong(const char *s) {
    226 	ulong l;
    227 
    228 	if(!(s && getulong(s, &l)))
    229 		error(Ebadvalue);
    230 	return l;
    231 }
    232 
    233 static long
    234 msg_getlong(const char *s) {
    235 	long l;
    236 
    237 	if(!(s && getlong(s, &l)))
    238 		error(Ebadvalue);
    239 	return l;
    240 }
    241 
    242 void
    243 msg_eatrunes(IxpMsg *m, int (*p)(Rune), int val) {
    244 	Rune r;
    245 	int n;
    246 
    247 	while(m->pos < m->end) {
    248 		n = chartorune(&r, m->pos);
    249 		if(p(r) != val)
    250 			break;
    251 		m->pos += n;
    252 	}
    253 	if(m->pos > m->end)
    254 		m->pos = m->end;
    255 }
    256 
    257 char*
    258 msg_getword(IxpMsg *m, char *errmsg) {
    259 	char *ret;
    260 	Rune r;
    261 	int n;
    262 
    263 	msg_eatrunes(m, isspacerune, true);
    264 	ret = m->pos;
    265 	msg_eatrunes(m, isspacerune, false);
    266 	n = chartorune(&r, m->pos);
    267 	*m->pos = '\0';
    268 	m->pos += n;
    269 	msg_eatrunes(m, isspacerune, true);
    270 
    271 	/* Filter out comments. */
    272 	if(*ret == '#') {
    273 		*ret = '\0';
    274 		m->pos = m->end;
    275 	}
    276 	if(*ret == '\\')
    277 		if(ret[1] == '\\' || ret[1] == '#')
    278 			ret++;
    279 	if(*ret == '\0')
    280 		ret = nil;
    281 	if(ret == nil && errmsg)
    282 		error(errmsg);
    283 	return ret;
    284 }
    285 
    286 typedef struct Mask Mask;
    287 struct Mask {
    288 	long*	mask;
    289 	char**	table;
    290 };
    291 
    292 static int
    293 Mfmt(Fmt *f) {
    294 	Mask m;
    295 
    296 	m = va_arg(f->args, Mask);
    297 	return unmask(f, *m.mask, m.table, '+');
    298 }
    299 
    300 char*
    301 mask(char **s, int *add, int *old) {
    302 	static char seps[] = "+-^";
    303 	char *p, *q;
    304 
    305 again:
    306 	p = *s;
    307 	if(*old == '\0')
    308 		return nil;
    309 	*add = *old;
    310 
    311 	if(*p == '/') {
    312 		/* Check for regex. */
    313 		if(!(q = strchr(p+1, '/')))
    314 			goto fail;
    315 		if(*q++ != '/' || !memchr(seps, (*old=*q), sizeof seps))
    316 			goto fail;
    317 	}
    318 	else {
    319 		for(q=p; (*old=*q) && !strchr(seps, *q);)
    320 			q++;
    321 		if(memchr(p, '/', q-p))
    322 			goto fail;
    323 	}
    324 
    325 	*q++ = '\0';
    326 	*s = q;
    327 	if(p + 1 == q)
    328 		goto again;
    329 	return p;
    330 fail:
    331 	while((*old=*q) && !strchr(seps, *q))
    332 		q++;
    333 	goto again;
    334 }
    335 
    336 static void
    337 setmask(Mask m, char *s) {
    338 	char *opt;
    339 	int add, old, i, n;
    340 	long newmask;
    341 
    342 	if(s == nil)
    343 		s = "";
    344 	for(n=0; m.table[n]; n++)
    345 		;
    346 	newmask = memchr("+-^", s[0], 3) ? *m.mask : 0L;
    347 
    348 	old = '+';
    349 	while((opt = mask(&s, &add, &old))) {
    350 		i = _bsearch(opt, m.table, n);
    351 		if(i == -1)
    352 			error(Ebadvalue);
    353 		else if(add = '^')
    354 			newmask ^= 1<<i;
    355 		else if(add == '+')
    356 			newmask |= 1<<i;
    357 		else if(add == '-')
    358 			newmask &= ~(1<<i);
    359 	}
    360 	*m.mask = newmask;
    361 }
    362 
    363 void
    364 msg_debug(char *s) {
    365 	setmask((Mask){&debugflag, debugtab}, s);
    366 }
    367 
    368 static Client*
    369 strclient(View *v, char *s) {
    370 	Client *c;
    371 
    372 	/*
    373 	 * sel
    374 	 * 0x<window xid>
    375 	 */
    376 
    377 	if(s && !strcmp(s, "sel"))
    378 		c = view_selclient(v);
    379 	else
    380 		c = win2client(msg_getulong(s));
    381 	if(c == nil)
    382 		error(Ebadvalue);
    383 	return c;
    384 }
    385 
    386 Area*
    387 strarea(View *v, ulong scrn, const char *area) {
    388 	Area *a;
    389 	const char *screen;
    390 	char *p;
    391 	long i;
    392 
    393 	/*
    394 	 * sel
    395 	 * ~
    396 	 * <column number>
    397 	 */
    398 
    399 	if(area == nil)
    400 		error(Ebadvalue);
    401 
    402 	if((p = strchr(area, ':'))) {
    403 		/* <screen>:<area> */
    404 		*p++ = '\0';
    405 		screen = area;
    406 		area = p;
    407 
    408 		if(!strcmp(screen, "sel"))
    409 			scrn = v->selscreen;
    410 		else
    411 			scrn = msg_getulong(screen);
    412 	}
    413 	else if(!strcmp(area, "sel"))
    414 		return v->sel;
    415 
    416 	if(!strcmp(area, "sel")) {
    417 		if(scrn != v->selscreen)
    418 			error(Ebadvalue);
    419 		return v->sel;
    420 	}
    421 
    422 	if(!strcmp(area, "~"))
    423 		return v->floating;
    424 
    425 	if(scrn < 0)
    426 		error(Ebadvalue);
    427 
    428 	i = msg_getlong(area);
    429 	if(i == 0)
    430 		error(Ebadvalue);
    431 
    432 	if(i > 0) {
    433 		for(a = v->areas[scrn]; a; a = a->next)
    434 			if(i-- == 1) break;
    435 	}
    436 	else {
    437 		/* FIXME: Switch to circularly linked list. */
    438 		for(a = v->areas[scrn]; a->next; a = a->next)
    439 			;
    440 		for(; a; a = a->prev)
    441 			if(++i == 0) break;
    442 	}
    443 	if(a == nil)
    444 		error(Ebadvalue);
    445 	return a;
    446 }
    447 
    448 static Frame*
    449 getframe(View *v, int scrn, IxpMsg *m) {
    450 	Frame *f;
    451 	Area *a;
    452 	char *s;
    453 	ulong l;
    454 
    455 	s = msg_getword(m, Ebadvalue);
    456 	if(!strcmp(s, "client"))
    457 		f = client_viewframe(strclient(v, msg_getword(m, Ebadvalue)),
    458 				     v);
    459 	else {
    460 		/* XXX: Multihead */
    461 		a = strarea(v, scrn, s);
    462 
    463 		s = msg_getword(m, Ebadvalue);
    464 		f = nil;
    465 		if(!strcmp(s, "sel"))
    466 			f = a->sel;
    467 		else {
    468 			l = msg_getulong(s);
    469 			for(f=a->frame; f; f=f->anext)
    470 				if(--l == 0) break;
    471 		}
    472 	}
    473 	if(f == nil)
    474 		error(Ebadvalue);
    475 	return f;
    476 }
    477 
    478 char*
    479 readctl_bar(Bar *b) {
    480 	bufclear();
    481 	bufprint("colors %s\n", b->colors.colstr);
    482 	bufprint("label %s\n", b->text);
    483 	return buffer;
    484 }
    485 
    486 char*
    487 message_bar(Bar *b, IxpMsg *m) {
    488 
    489 	switch(getsym(msg_getword(m, nil))) {
    490 	case LCOLORS:
    491 		msg_parsecolors(m, &b->colors);
    492 		break;
    493 	case LLABEL:
    494 		utflcpy(b->text, (char*)m->pos, sizeof b->text);
    495 		break;
    496 	default:
    497 		error(Ebadvalue);
    498 	}
    499 	bar_draw(b->screen);
    500 	return nil;
    501 }
    502 
    503 char*
    504 readctl_client(Client *c) {
    505 	bufclear();
    506 	bufprint("%#C\n", c);
    507 	bufprint("allow %M\n", (Mask){&c->permission, permtab});
    508 	bufprint("floating %s\n", floatingtab[c->floating + 1]);
    509 	if(c->fullscreen >= 0)
    510 		bufprint("fullscreen %d\n", c->fullscreen);
    511 	else
    512 		bufprint("fullscreen off\n");
    513 	bufprint("group %#ulx\n", c->group ? c->group->leader : 0);
    514 	if(c->pid)
    515 		bufprint("pid %d\n", c->pid);
    516 	bufprint("tags %s\n", c->tags);
    517 	bufprint("urgent %s\n", TOGGLE(c->urgent));
    518 	return buffer;
    519 }
    520 
    521 char*
    522 message_client(Client *c, IxpMsg *m) {
    523 	char *s;
    524 	long l;
    525 
    526 	s = msg_getword(m, Ebadcmd);
    527 
    528 	/*
    529 	 * Toggle ::= on
    530 	 *	    | off
    531 	 *	    | toggle
    532 	 *	    | <screen>
    533 	 * floating <toggle>
    534 	 * fullscreen <toggle>
    535 	 * kill
    536 	 * slay
    537 	 * tags <tags>
    538 	 * urgent <toggle>
    539 	 */
    540 
    541 	switch(getsym(s)) {
    542 	case LALLOW:
    543 		setmask((Mask){&c->permission, permtab}, msg_getword(m, 0));
    544 		break;
    545 	case LFLOATING:
    546 		c->floating = -1 + _lsearch(msg_getword(m, Ebadvalue), floatingtab, nelem(floatingtab));
    547 		break;
    548 	case LFULLSCREEN:
    549 		s = msg_getword(m, Ebadvalue);
    550 		if(getlong(s, &l))
    551 			fullscreen(c, On, l);
    552 		else
    553 			fullscreen(c, gettoggle(s), -1);
    554 		break;
    555 	case LGROUP:
    556 		group_remove(c);
    557 		c->w.hints->group = msg_getulong(msg_getword(m, Ebadvalue));
    558 		if(c->w.hints->group)
    559 			group_init(c);
    560 		break;
    561 	case LKILL:
    562 		client_kill(c, true);
    563 		break;
    564 	case LSLAY:
    565 		client_kill(c, false);
    566 		break;
    567 	case LTAGS:
    568 		client_applytags(c, m->pos);
    569 		break;
    570 	case LURGENT:
    571 		client_seturgent(c, gettoggle(msg_getword(m, Ebadvalue)), UrgManager);
    572 		break;
    573 	default:
    574 		error(Ebadcmd);
    575 	}
    576 	return nil;
    577 }
    578 
    579 char*
    580 message_root(void *p, IxpMsg *m) {
    581 	Font *fn;
    582 	char *s, *ret;
    583 	ulong n;
    584 	int i;
    585 
    586 	USED(p);
    587 	ret = nil;
    588 	s = msg_getword(m, 0);
    589 	if(s == nil)
    590 		return nil;
    591 
    592 	if(!strcmp(s, "backtrace")) {
    593 		backtrace(m->pos);
    594 		return nil;
    595 	}
    596 
    597 	if(!strcmp(s, "xinerama")) {
    598 		setenv("XINERAMA_SCREENS", m->pos, 1);
    599 		init_screens();
    600 		return nil;
    601 	}
    602 
    603 	switch(getsym(s)) {
    604 	case LBAR: /* bar on? <"top" | "bottom"> */
    605 		s = msg_getword(m, Ebadvalue);
    606 		if(!strcmp(s, "on"))
    607 			s = msg_getword(m, Ebadvalue);
    608 		setdef(&screen->barpos, s, barpostab, nelem(barpostab));
    609 		view_update(selview);
    610 		break;
    611 	case LBORDER:
    612 		def.border = msg_getulong(msg_getword(m, 0));;
    613 		view_update(selview);
    614 		break;
    615 	case LCOLMODE:
    616 		setdef(&def.colmode, msg_getword(m, 0), modes, Collast);
    617 		break;
    618 	case LDEBUG:
    619 		msg_debug(msg_getword(m, 0));
    620 		break;
    621 	case LEXEC:
    622 		execstr = strdup(m->pos);
    623 		srv.running = 0;
    624 		break;
    625 	case LSPAWN:
    626 		spawn_command(m->pos);
    627 		break;
    628 	case LFOCUSCOLORS:
    629 		msg_parsecolors(m, &def.focuscolor);
    630 		goto updatecolors;
    631 	case LFONT:
    632 		fn = loadfont(m->pos);
    633 		if(fn) {
    634 			freefont(def.font);
    635 			def.font = fn;
    636 			for(n=0; n < nscreens; n++)
    637 				bar_resize(screens[n]);
    638 		}else
    639 			ret = "can't load font";
    640 		view_update(selview);
    641 		break;
    642 	case LFONTPAD:
    643 		if(!getint(msg_getword(m, 0), &def.font->pad.min.x) ||
    644 		   !getint(msg_getword(m, 0), &def.font->pad.max.x) ||
    645 		   !getint(msg_getword(m, 0), &def.font->pad.max.y) ||
    646 		   !getint(msg_getword(m, 0), &def.font->pad.min.y))
    647 			ret = "invalid rectangle";
    648 		else {
    649 			for(n=0; n < nscreens; n++)
    650 				bar_resize(screens[n]);
    651 			view_update(selview);
    652 		}
    653 		break;
    654 	case LGRABMOD:
    655 		s = msg_getword(m, Ebadvalue);
    656 		if(!parsekey(s, &i, nil) || i == 0)
    657 			return Ebadvalue;
    658 
    659 		def.mod = i;
    660 		break;
    661 	case LINCMODE:
    662 		setdef(&def.incmode, msg_getword(m, 0), incmodetab, nelem(incmodetab));
    663 		view_update(selview);
    664 		break;
    665 	case LNORMCOLORS:
    666 		msg_parsecolors(m, &def.normcolor);
    667 	updatecolors:
    668 		for(Client *c=client; c; c=c->next)
    669 			client_reparent(c);
    670 		view_update(selview);
    671 		break;
    672 	case LSELCOLORS:
    673 		warning("selcolors have been removed");
    674 		return Ebadcmd;
    675 	case LVIEW:
    676 		view_select(m->pos);
    677 		break;
    678 	case LQUIT:
    679 		srv.running = 0;
    680 		break;
    681 	default:
    682 		return Ebadcmd;
    683 	}
    684 	return ret;
    685 }
    686 
    687 char*
    688 readctl_root(void) {
    689 	fmtinstall('M', Mfmt);
    690 	bufclear();
    691 	bufprint("bar on %s\n", barpostab[screen->barpos]);
    692 	bufprint("border %d\n", def.border);
    693 	bufprint("colmode %s\n", modes[def.colmode]);
    694 	if(debugflag)
    695 		bufprint("debug %M\n", (Mask){&debugflag, debugtab});
    696 	if(debugfile)
    697 		bufprint("debugfile %M", (Mask){&debugfile, debugtab});
    698 	bufprint("focuscolors %s\n", def.focuscolor.colstr);
    699 	bufprint("font %s\n", def.font->name);
    700 	bufprint("fontpad %d %d %d %d\n", def.font->pad.min.x, def.font->pad.max.x,
    701 		 def.font->pad.max.y, def.font->pad.min.y);
    702 	bufprint("grabmod %s\n", (Mask){&def.mod, modkey_names});
    703 	bufprint("incmode %s\n", incmodetab[def.incmode]);
    704 	bufprint("normcolors %s\n", def.normcolor.colstr);
    705 	bufprint("view %s\n", selview->name);
    706 	return buffer;
    707 }
    708 
    709 char*
    710 message_view(View *v, IxpMsg *m) {
    711 	Area *a;
    712 	char *s;
    713 
    714 	s = msg_getword(m, 0);
    715 	if(s == nil)
    716 		return nil;
    717 
    718 	/*
    719 	 * area ::= ~
    720 	 *        | <column number>
    721 	 *        | sel
    722 	 * direction ::= left
    723 	 *             | right
    724 	 *             | up
    725 	 *             | down
    726 	 * # This *should* be identical to <frame>
    727 	 * place ::= <column number>
    728 	 *	  #| ~ # This should be, but isn't.
    729 	 *	   | <direction>
    730 	 *         | toggle
    731 	 * colmode ::= default
    732 	 *           | stack
    733 	 *           | normal
    734 	 * column ::= sel
    735 	 *          | <column number>
    736 	 * frame ::= up
    737 	 *         | down
    738 	 *         | left
    739 	 *         | right
    740 	 *         | toggle
    741 	 *         | client <window xid>
    742 	 *         | sel
    743 	 *         | ~
    744 	 *         | <column> <frame number>
    745 	 *         | <column>
    746 	 * amount ::=
    747 	 *	    | <number>
    748 	 *          | <number>px
    749 	 *
    750 	 * colmode <area> <colmode>
    751 	 * select <area>
    752 	 * send <frame> <place>
    753 	 * swap <frame> <place>
    754 	 * grow <thing> <direction> <amount>
    755 	 * nudge <thing> <direction> <amount>
    756 	 * select <ordframe>
    757 	 */
    758 
    759 	switch(getsym(s)) {
    760 	case LCOLMODE:
    761 		s = msg_getword(m, Ebadvalue);
    762 		a = strarea(v, screen->idx, s);
    763 
    764 		s = msg_getword(m, Ebadvalue);
    765 		if(!column_setmode(a, s))
    766 			return Ebadvalue;
    767 
    768 		column_arrange(a, false);
    769 		view_restack(v);
    770 
    771 		view_update(v);
    772 		return nil;
    773 	case LGROW:
    774 		return msg_grow(v, m);
    775 	case LNUDGE:
    776 		return msg_nudge(v, m);
    777 	case LSELECT:
    778 		return msg_selectarea(v->sel, m);
    779 	case LSEND:
    780 		return msg_sendclient(v, m, false);
    781 	case LSWAP:
    782 		return msg_sendclient(v, m, true);
    783 	default:
    784 		return Ebadcmd;
    785 	}
    786 	/* not reached */
    787 }
    788 
    789 char*
    790 readctl_view(View *v) {
    791 	Area *a;
    792 	int s;
    793 
    794 	bufclear();
    795 	bufprint("%s\n", v->name);
    796 
    797 	bufprint("urgent %s\n", TOGGLE(v->urgent));
    798 
    799 	/* select <area>[ <frame>] */
    800 	bufprint("select %a", v->sel);
    801 	if(v->sel->sel)
    802 		bufprint(" %d", frame_idx(v->sel->sel));
    803 	bufprint("\n");
    804 
    805 	/* select client <client> */
    806 	if(v->sel->sel)
    807 		bufprint("select client %#C\n", v->sel->sel->client);
    808 
    809 	foreach_area(v, s, a)
    810 		bufprint("colmode %a %s\n", a, column_getmode(a));
    811 	return buffer;
    812 }
    813 
    814 static void
    815 getamt(IxpMsg *m, Point *amt) {
    816 	char *s, *p;
    817 	long l;
    818 
    819 	s = msg_getword(m, 0);
    820 	if(s) {
    821 		p = strend(s, 2);
    822 		if(!strcmp(p, "px")) {
    823 			*p = '\0';
    824 			amt->x = 1;
    825 			amt->y = 1;
    826 		}
    827 
    828 		l = msg_getlong(s);
    829 		amt->x *= l;
    830 		amt->y *= l;
    831 	}
    832 }
    833 
    834 static char*
    835 msg_grow(View *v, IxpMsg *m) {
    836 	Client *c;
    837 	Frame *f;
    838 	Rectangle h, r;
    839 	Point amount;
    840 	int dir;
    841 
    842 	f = getframe(v, screen->idx, m);
    843 	c = f->client;
    844 	h = c->w.hints->aspect;
    845 
    846 	dir = getdirection(m);
    847 
    848 	amount.x = Dy(f->titlebar);
    849 	amount.y = Dy(f->titlebar);
    850 	if(amount.x < c->w.hints->inc.x)
    851 		amount.x = c->w.hints->inc.x;
    852 	if(amount.y < c->w.hints->inc.y)
    853 		amount.y = c->w.hints->inc.y;
    854 	getamt(m, &amount);
    855 
    856 	if (dir == LLEFT || dir == LRIGHT)
    857 		amount.y = h.min.x ? amount.x * h.min.y / h.min.x : 0;
    858 	else
    859 		amount.x = h.min.y ? amount.y * h.min.x / h.min.y : 0;
    860 
    861 	if(f->area->floating)
    862 		r = f->r;
    863 	else
    864 		r = f->colr;
    865 
    866 	if (dir == LLEFT || dir == LUP)
    867 		r.min = subpt(r.min, amount);
    868 	else if (dir == LRIGHT || dir == LDOWN)
    869 		r.max = addpt(r.max, amount);
    870 
    871 	if(f->area->floating)
    872 		float_resizeframe(f, r);
    873 	else
    874 		column_resizeframe(f, r);
    875 
    876 	return nil;
    877 }
    878 
    879 static char*
    880 msg_nudge(View *v, IxpMsg *m) {
    881 	Frame *f;
    882 	Rectangle r;
    883 	Point amount;
    884 	int dir;
    885 
    886 	f = getframe(v, screen->idx, m);
    887 	dir = getdirection(m);
    888 
    889 	amount.x = Dy(f->titlebar);
    890 	amount.y = Dy(f->titlebar);
    891 	getamt(m, &amount);
    892 
    893 	if(f->area->floating)
    894 		r = f->r;
    895 	else
    896 		r = f->colr;
    897 	switch(dir) {
    898 	case LLEFT:	r = rectaddpt(r, Pt(-amount.x, 0)); break;
    899 	case LRIGHT:	r = rectaddpt(r, Pt( amount.x, 0)); break;
    900 	case LUP:	r = rectaddpt(r, Pt(0, -amount.y)); break;
    901 	case LDOWN:	r = rectaddpt(r, Pt(0,  amount.y)); break;
    902 	default:	abort();
    903 	}
    904 
    905 	if(f->area->floating)
    906 		float_resizeframe(f, r);
    907 	else
    908 		column_resizeframe(f, r);
    909 	return nil;
    910 }
    911 
    912 void
    913 msg_parsecolors(IxpMsg *m, CTuple *col) {
    914 	CTuple tpl;
    915 	char n;
    916 
    917 	n = loadcolor(&tpl, m->pos, m->end);
    918 	m->pos += n;
    919 	if(n == 0 || msg_getword(m, nil))
    920 		error("bad color string");
    921 	*col = tpl;
    922 }
    923 
    924 char*
    925 msg_selectarea(Area *a, IxpMsg *m) {
    926 	Frame *f;
    927 	Area *ap;
    928 	View *v;
    929 	char *s;
    930 	ulong i;
    931 	int sym;
    932 
    933 	v = a->view;
    934 	s = msg_getword(m, Ebadvalue);
    935 	sym = getsym(s);
    936 
    937 	switch(sym) {
    938 	case LTOGGLE:
    939 		if(!a->floating)
    940 			ap = v->floating;
    941 		else if(v->revert && v->revert != a)
    942 			ap = v->revert;
    943 		else
    944 			ap = v->firstarea;
    945 		break;
    946 	case LLEFT:
    947 	case LRIGHT:
    948 	case LUP:
    949 	case LDOWN:
    950 	case LCLIENT:
    951 		return msg_selectframe(a, m, sym);
    952 	case LTILDE:
    953 		ap = v->floating;
    954 		break;
    955 	default:
    956 		/* XXX: Multihead */
    957 		ap = strarea(v, a->screen, s);
    958 		if(ap->floating)
    959 			return Ebadvalue;
    960 
    961 		if((s = msg_getword(m, 0))) {
    962 			i = msg_getulong(s);
    963 			for(f = ap->frame; f; f = f->anext)
    964 				if(--i == 0) break;
    965 			if(i != 0)
    966 				return Ebadvalue;
    967 			frame_focus(f);
    968 			return nil;
    969 		}
    970 	}
    971 
    972 	area_focus(ap);
    973 	return nil;
    974 }
    975 
    976 static char*
    977 msg_selectframe(Area *a, IxpMsg *m, int sym) {
    978 	Client *c;
    979 	Frame *f, *fp;
    980 	char *s;
    981 	bool stack;
    982 	ulong i, dy;
    983 
    984 	f = a->sel;
    985 	fp = f;
    986 
    987 	stack = false;
    988 	if(sym == LUP || sym == LDOWN)
    989 		if((s = msg_getword(m, 0)))
    990 			if(!strcmp(s, "stack"))
    991 				stack = true;
    992 			else
    993 				return Ebadvalue;
    994 
    995 	if(sym == LCLIENT) {
    996 		s = msg_getword(m, Ebadvalue);
    997 		i = msg_getulong(s);
    998 		c = win2client(i);
    999 		if(c == nil)
   1000 			return Ebadvalue;
   1001 		f = client_viewframe(c, a->view);
   1002 		if(!f)
   1003 			return Ebadvalue;
   1004 	}
   1005 	else if(!find(&a, &f, DIR(sym), true, stack))
   1006 		return Ebadvalue;
   1007 
   1008 	area_focus(a);
   1009 
   1010 	if(f != nil) {
   1011 		/* XXX */
   1012 		if(fp && fp->area == a)
   1013 		if(f->collapsed && !f->area->floating && f->area->mode == Coldefault) {
   1014 			dy = Dy(f->colr);
   1015 			f->colr.max.y = f->colr.min.y + Dy(fp->colr);
   1016 			fp->colr.max.y = fp->colr.min.y + dy;
   1017 			column_arrange(a, false);
   1018 		}
   1019 
   1020 		frame_focus(f);
   1021 		frame_restack(f, nil);
   1022 		if(f->view == selview)
   1023 			view_restack(a->view);
   1024 	}
   1025 	return nil;
   1026 }
   1027 
   1028 static char*
   1029 sendarea(Frame *f, Area *to, bool swap) {
   1030 	Client *c;
   1031 
   1032 	c = f->client;
   1033 	if(!to)
   1034 		return Ebadvalue;
   1035 
   1036 	if(!swap)
   1037 		area_moveto(to, f);
   1038 	else if(to->sel)
   1039 		frame_swap(f, to->sel);
   1040 	else
   1041 		return Ebadvalue;
   1042 
   1043 	frame_focus(client_viewframe(c, f->view));
   1044 	/* view_arrange(v); */
   1045 	view_update_all();
   1046 	return nil;
   1047 }
   1048 
   1049 char*
   1050 msg_sendclient(View *v, IxpMsg *m, bool swap) {
   1051 	Area *to, *a;
   1052 	Frame *f, *ff;
   1053 	Client *c;
   1054 	char *s;
   1055 	int sym;
   1056 
   1057 	c = strclient(v, msg_getword(m, 0));
   1058 	f = client_viewframe(c, v);
   1059 	if(f == nil)
   1060 		return Ebadvalue;
   1061 
   1062 	a = f->area;
   1063 	to = nil;
   1064 
   1065 	s = msg_getword(m, Ebadvalue);
   1066 	sym = getsym(s);
   1067 
   1068 	/* FIXME: Should use a helper function. */
   1069 	switch(sym) {
   1070 	case LUP:
   1071 	case LDOWN:
   1072 		return msg_sendframe(f, sym, swap);
   1073 	case LLEFT:
   1074 		if(a->floating)
   1075 			return Ebadvalue;
   1076 		to = a->prev;
   1077 		break;
   1078 	case LRIGHT:
   1079 		if(a->floating)
   1080 			return Ebadvalue;
   1081 		to = a->next;
   1082 		break;
   1083 	case LTOGGLE:
   1084 		if(!a->floating)
   1085 			to = v->floating;
   1086 		else if(f->column)
   1087 			to = view_findarea(v, f->screen, f->column, true);
   1088 		else
   1089 			to = view_findarea(v, v->selscreen, v->selcol, true);
   1090 		break;
   1091 	case LTILDE:
   1092 		if(a->floating)
   1093 			return Ebadvalue;
   1094 		to = v->floating;
   1095 		break;
   1096 	default:
   1097 		to = strarea(v, v->selscreen, s);
   1098 		// to = view_findarea(v, scrn, i, true);
   1099 		break;
   1100 	}
   1101 
   1102 
   1103 	if(!to && !swap) {
   1104 		/* XXX: Multihead - clean this up, move elsewhere. */
   1105 		if(!f->anext && f == f->area->frame) {
   1106 			ff = f;
   1107 			to = a;
   1108 			if(!find(&to, &ff, DIR(sym), false, false))
   1109 				return Ebadvalue;
   1110 		}
   1111 		else {
   1112 			to = (sym == LLEFT) ? nil : a;
   1113 			to = column_new(v, to, a->screen, 0);
   1114 		}
   1115 	}
   1116 
   1117 	return sendarea(f, to, swap);
   1118 }
   1119 
   1120 static char*
   1121 msg_sendframe(Frame *f, int sym, bool swap) {
   1122 	Client *c;
   1123 	Area *a;
   1124 	Frame *fp;
   1125 
   1126 	SET(fp);
   1127 	c = f->client;
   1128 
   1129 	a = f->area;
   1130 	fp = f;
   1131 	if(!find(&a, &fp, DIR(sym), false, false))
   1132 		return Ebadvalue;
   1133 	if(a != f->area)
   1134 		return sendarea(f, a, swap);
   1135 
   1136 	switch(sym) {
   1137 	case LUP:
   1138 		fp = f->aprev;
   1139 		if(!fp)
   1140 			return Ebadvalue;
   1141 		if(!swap)
   1142 			fp = fp->aprev;
   1143 		break;
   1144 	case LDOWN:
   1145 		fp = f->anext;
   1146 		if(!fp)
   1147 			return Ebadvalue;
   1148 		break;
   1149 	default:
   1150 		die("can't get here");
   1151 	}
   1152 
   1153 	if(swap)
   1154 		frame_swap(f, fp);
   1155 	else {
   1156 		frame_remove(f);
   1157 		frame_insert(f, fp);
   1158 	}
   1159 
   1160 	/* view_arrange(f->view); */
   1161 
   1162 	frame_focus(client_viewframe(c, f->view));
   1163 	view_update_all();
   1164 	return nil;
   1165 }
   1166 
   1167 void
   1168 warning(const char *fmt, ...) {
   1169 	va_list ap;
   1170 	char *s;
   1171 
   1172 	va_start(ap, fmt);
   1173 	s = vsmprint(fmt, ap);
   1174 	va_end(ap);
   1175 
   1176 	event("Warning %s\n", s);
   1177 	fprint(2, "%s: warning: %s\n", argv0, s);
   1178 	free(s);
   1179 }
   1180