xref: /plan9-contrib/sys/src/libcontrol/control.c (revision 75e4bb061c5a96f72ddf5c2f9a4bde6c5cf90ed0)
180ee5cbfSDavid du Colombier #include <u.h>
280ee5cbfSDavid du Colombier #include <libc.h>
380ee5cbfSDavid du Colombier #include <draw.h>
480ee5cbfSDavid du Colombier #include <thread.h>
580ee5cbfSDavid du Colombier #include <mouse.h>
680ee5cbfSDavid du Colombier #include <keyboard.h>
780ee5cbfSDavid du Colombier #include <control.h>
880ee5cbfSDavid du Colombier 
96f314b92SDavid du Colombier static int debug = 0;
106f314b92SDavid du Colombier 
119a747e4fSDavid du Colombier enum	/* alts */
129a747e4fSDavid du Colombier {
139a747e4fSDavid du Colombier 	AKey,
149a747e4fSDavid du Colombier 	AMouse,
159a747e4fSDavid du Colombier 	ACtl,
169a747e4fSDavid du Colombier 	AExit,
179a747e4fSDavid du Colombier 	NALT
189a747e4fSDavid du Colombier };
199a747e4fSDavid du Colombier 
2080ee5cbfSDavid du Colombier static Controlset **controlset;
2180ee5cbfSDavid du Colombier int	ncontrolset;
2280ee5cbfSDavid du Colombier int	ctldeletequits;
2380ee5cbfSDavid du Colombier 
2480ee5cbfSDavid du Colombier char *alignnames[Nalignments] = {
2580ee5cbfSDavid du Colombier 	[Aupperleft] =		"upperleft",
2680ee5cbfSDavid du Colombier 	[Auppercenter] =	"uppercenter",
2780ee5cbfSDavid du Colombier 	[Aupperright] =		"upperright",
2880ee5cbfSDavid du Colombier 	[Acenterleft] =		"centerleft",
2980ee5cbfSDavid du Colombier 	[Acenter] =		"center",
3080ee5cbfSDavid du Colombier 	[Acenterright] =	"centerright",
3180ee5cbfSDavid du Colombier 	[Alowerleft] =		"lowerleft",
3280ee5cbfSDavid du Colombier 	[Alowercenter] =	"lowercenter",
3380ee5cbfSDavid du Colombier 	[Alowerright] =		"lowerright",
3480ee5cbfSDavid du Colombier };
3580ee5cbfSDavid du Colombier 
369a747e4fSDavid du Colombier char *ctltypenames[Ntypes] = {
379a747e4fSDavid du Colombier 	[Ctlunknown] =		"unknown",
389a747e4fSDavid du Colombier 	[Ctlbox] =			"box",
399a747e4fSDavid du Colombier 	[Ctlbutton] =		"button",
409a747e4fSDavid du Colombier 	[Ctlentry] =		"entry",
419a747e4fSDavid du Colombier 	[Ctlkeyboard] =		"keyboard",
429a747e4fSDavid du Colombier 	[Ctllabel] =		"label",
439a747e4fSDavid du Colombier 	[Ctlmenu] =		"menu",
449a747e4fSDavid du Colombier 	[Ctlradio] =		"radio",
459a747e4fSDavid du Colombier 	[Ctlscribble] =		"scribble",
469a747e4fSDavid du Colombier 	[Ctlslider] =		"slider",
479a747e4fSDavid du Colombier 	[Ctltabs] =			"tabs",
489a747e4fSDavid du Colombier 	[Ctltext] =			"text",
499a747e4fSDavid du Colombier 	[Ctltextbutton] =	"textbutton",
506f314b92SDavid du Colombier 	[Ctltextbutton3] =	"textbutton3",
519a747e4fSDavid du Colombier 	[Ctlgroup] =		"group",		// divider between controls and metacontrols
529a747e4fSDavid du Colombier 	[Ctlboxbox] =		"boxbox",
539a747e4fSDavid du Colombier 	[Ctlcolumn] =		"column",
549a747e4fSDavid du Colombier 	[Ctlrow] =			"row",
559a747e4fSDavid du Colombier 	[Ctlstack] =		"stack",
569a747e4fSDavid du Colombier 	[Ctltab] =			"tab",
579a747e4fSDavid du Colombier };
589a747e4fSDavid du Colombier 
599a747e4fSDavid du Colombier static void	_ctlcmd(Controlset*, char*);
609a747e4fSDavid du Colombier static void	_ctlcontrol(Controlset*, char*);
619a747e4fSDavid du Colombier 
629a747e4fSDavid du Colombier static char*
_mkctlcmd(Control * c,char * fmt,va_list arg)639a747e4fSDavid du Colombier _mkctlcmd(Control *c, char *fmt, va_list arg)
649a747e4fSDavid du Colombier {
659a747e4fSDavid du Colombier 	char *name, *p, *both;
669a747e4fSDavid du Colombier 
679a747e4fSDavid du Colombier 	name = quotestrdup(c->name);
689a747e4fSDavid du Colombier 	if(name == nil)
699a747e4fSDavid du Colombier 		ctlerror("quotestrdup in ctlprint failed");
709a747e4fSDavid du Colombier 	p = vsmprint(fmt, arg);
719a747e4fSDavid du Colombier 	if(p == nil){
729a747e4fSDavid du Colombier 		free(name);
739a747e4fSDavid du Colombier 		ctlerror("vsmprint1 in ctlprint failed");
749a747e4fSDavid du Colombier 	}
759a747e4fSDavid du Colombier 	both = ctlmalloc(strlen(name)+strlen(p)+2);
769a747e4fSDavid du Colombier 	strcpy(both, name);
779a747e4fSDavid du Colombier 	strcat(both, " ");
789a747e4fSDavid du Colombier 	strcat(both, p);
799a747e4fSDavid du Colombier 	free(name);
809a747e4fSDavid du Colombier 	free(p);
819a747e4fSDavid du Colombier 	return both;
829a747e4fSDavid du Colombier }
839a747e4fSDavid du Colombier 
849a747e4fSDavid du Colombier int
ctlprint(Control * c,char * fmt,...)859a747e4fSDavid du Colombier ctlprint(Control *c, char *fmt, ...)
869a747e4fSDavid du Colombier {
879a747e4fSDavid du Colombier 	int n;
889a747e4fSDavid du Colombier 	char *p;
899a747e4fSDavid du Colombier 	va_list arg;
909a747e4fSDavid du Colombier 
919a747e4fSDavid du Colombier 	va_start(arg, fmt);
929a747e4fSDavid du Colombier 	p = _mkctlcmd(c, fmt, arg);
939a747e4fSDavid du Colombier 	va_end(arg);
949a747e4fSDavid du Colombier 	n = sendp(c->controlset->ctl, p);
959a747e4fSDavid du Colombier 	yield();
969a747e4fSDavid du Colombier 	return n;
979a747e4fSDavid du Colombier }
989a747e4fSDavid du Colombier 
999a747e4fSDavid du Colombier void
_ctlprint(Control * c,char * fmt,...)1009a747e4fSDavid du Colombier _ctlprint(Control *c, char *fmt, ...)
1019a747e4fSDavid du Colombier {
1029a747e4fSDavid du Colombier 	char *p;
1039a747e4fSDavid du Colombier 	va_list arg;
1049a747e4fSDavid du Colombier 
1059a747e4fSDavid du Colombier 	va_start(arg, fmt);
1069a747e4fSDavid du Colombier 	p = _mkctlcmd(c, fmt, arg);
1079a747e4fSDavid du Colombier 	va_end(arg);
1089a747e4fSDavid du Colombier 	_ctlcmd(c->controlset, p);
1099a747e4fSDavid du Colombier 	free(p);
1109a747e4fSDavid du Colombier }
1119a747e4fSDavid du Colombier 
1129a747e4fSDavid du Colombier int
_ctllookup(char * s,char * tab[],int ntab)1139a747e4fSDavid du Colombier _ctllookup(char *s, char *tab[], int ntab)
1149a747e4fSDavid du Colombier {
1159a747e4fSDavid du Colombier 	int i;
1169a747e4fSDavid du Colombier 
1176f314b92SDavid du Colombier 	for(i=0; i<ntab; i++)
1186f314b92SDavid du Colombier 		if(tab[i] != nil && strcmp(s, tab[i]) == 0)
1199a747e4fSDavid du Colombier 			return i;
1209a747e4fSDavid du Colombier 	return -1;
1219a747e4fSDavid du Colombier }
1229a747e4fSDavid du Colombier 
12380ee5cbfSDavid du Colombier static Control*
_newcontrol(Controlset * cs,uint n,char * name,char * type)1249a747e4fSDavid du Colombier _newcontrol(Controlset *cs, uint n, char *name, char *type)
12580ee5cbfSDavid du Colombier {
12680ee5cbfSDavid du Colombier 	Control *c;
12780ee5cbfSDavid du Colombier 
12880ee5cbfSDavid du Colombier 	for(c=cs->controls; c; c=c->next)
12980ee5cbfSDavid du Colombier 		if(strcmp(c->name, name) == 0){
13080ee5cbfSDavid du Colombier 			werrstr("control %q already defined", name);
13180ee5cbfSDavid du Colombier 			return nil;
13280ee5cbfSDavid du Colombier 		}
13380ee5cbfSDavid du Colombier 	c = ctlmalloc(n);
13480ee5cbfSDavid du Colombier 	c->screen = cs->screen;
13580ee5cbfSDavid du Colombier 	c->name = ctlstrdup(name);
1369a747e4fSDavid du Colombier 	c->type = _ctllookup(type, ctltypenames, Ntypes);
1379a747e4fSDavid du Colombier 	if (c->type < 0)
1389a747e4fSDavid du Colombier 		ctlerror("unknown type: %s", type);
139b7b24591SDavid du Colombier 	c->event = chancreate(sizeof(char*), 64);
14080ee5cbfSDavid du Colombier 	c->data = chancreate(sizeof(char*), 0);
1419a747e4fSDavid du Colombier 	c->size = Rect(1, 1, _Ctlmaxsize, _Ctlmaxsize);
1429a747e4fSDavid du Colombier 	c->hidden = 0;
1439a747e4fSDavid du Colombier 	c->ctl = nil;
1449a747e4fSDavid du Colombier 	c->mouse = nil;
1459a747e4fSDavid du Colombier 	c->key = nil;
1469a747e4fSDavid du Colombier 	c->exit = nil;
1479a747e4fSDavid du Colombier 	c->setsize = nil;
14880ee5cbfSDavid du Colombier 
14980ee5cbfSDavid du Colombier 	c->controlset = cs;
15080ee5cbfSDavid du Colombier 	c->next = cs->controls;
15180ee5cbfSDavid du Colombier 	cs->controls = c;
15280ee5cbfSDavid du Colombier 	return c;
15380ee5cbfSDavid du Colombier }
15480ee5cbfSDavid du Colombier 
1559a747e4fSDavid du Colombier static void
controlsetthread(void * v)1569a747e4fSDavid du Colombier controlsetthread(void *v)
1579a747e4fSDavid du Colombier {
1589a747e4fSDavid du Colombier 	Controlset *cs;
1599a747e4fSDavid du Colombier 	Mouse mouse;
1609a747e4fSDavid du Colombier 	Control *f;
1619a747e4fSDavid du Colombier 	int prevbut, n, i;
1629a747e4fSDavid du Colombier 	Alt alts[NALT+1];
1639a747e4fSDavid du Colombier 	char tmp[64], *str;
1649a747e4fSDavid du Colombier 	Rune buf[2][20], *rp;
1659a747e4fSDavid du Colombier 
1669a747e4fSDavid du Colombier 	cs = v;
1679a747e4fSDavid du Colombier 	snprint(tmp, sizeof tmp, "controlsetthread 0x%p", cs);
1689a747e4fSDavid du Colombier 	threadsetname(tmp);
1699a747e4fSDavid du Colombier 
1709a747e4fSDavid du Colombier 	alts[AKey].c = cs->kbdc;
1719a747e4fSDavid du Colombier 	alts[AKey].v = &rp;
1729a747e4fSDavid du Colombier 	alts[AKey].op = CHANRCV;
1739a747e4fSDavid du Colombier 	alts[AMouse].c = cs->mousec;
1749a747e4fSDavid du Colombier 	alts[AMouse].v = &mouse;
1759a747e4fSDavid du Colombier 	alts[AMouse].op = CHANRCV;
1769a747e4fSDavid du Colombier 	alts[ACtl].c = cs->ctl;
1779a747e4fSDavid du Colombier 	alts[ACtl].v = &str;
1789a747e4fSDavid du Colombier 	alts[ACtl].op = CHANRCV;
1799a747e4fSDavid du Colombier 	alts[AExit].c = cs->csexitc;
1809a747e4fSDavid du Colombier 	alts[AExit].v = nil;
1819a747e4fSDavid du Colombier 	alts[AExit].op = CHANRCV;
1829a747e4fSDavid du Colombier 	alts[NALT].op = CHANEND;
1839a747e4fSDavid du Colombier 
1849a747e4fSDavid du Colombier 	cs->focus = nil;
1859a747e4fSDavid du Colombier 	prevbut=0;
1869a747e4fSDavid du Colombier 	n = 0;
1879a747e4fSDavid du Colombier 	for(;;){
1889a747e4fSDavid du Colombier 		/* toggle so we can receive in one buffer while client processes the other */
1899a747e4fSDavid du Colombier 		alts[AKey].v = buf[n];
1909a747e4fSDavid du Colombier 		rp = buf[n];
1919a747e4fSDavid du Colombier 		n = 1-n;
1929a747e4fSDavid du Colombier 		switch(alt(alts)){
1939a747e4fSDavid du Colombier 		case AKey:
1949a747e4fSDavid du Colombier 			if(ctldeletequits && rp[0]=='\177')
1959a747e4fSDavid du Colombier 				ctlerror("delete");
1969a747e4fSDavid du Colombier 			for(i=1; i<nelem(buf[0])-1; i++)
1979a747e4fSDavid du Colombier 				if(nbrecv(cs->kbdc, rp+i) <= 0)
1989a747e4fSDavid du Colombier 					break;
1999a747e4fSDavid du Colombier 			rp[i] = L'\0';
2009a747e4fSDavid du Colombier 			if(cs->focus && cs->focus->key)
2019a747e4fSDavid du Colombier 				cs->focus->key(cs->focus, rp);
2029a747e4fSDavid du Colombier 			break;
2039a747e4fSDavid du Colombier 		case AMouse:
2049a747e4fSDavid du Colombier 			/* is this a focus change? */
2059a747e4fSDavid du Colombier 			if(prevbut)	/* don't change focus if button was down */
2069a747e4fSDavid du Colombier 				goto Send;
2079a747e4fSDavid du Colombier 			if(cs->focus!=nil && cs->focus->hidden == 0 && ptinrect(mouse.xy, cs->focus->rect))
2089a747e4fSDavid du Colombier 				goto Send;
2099a747e4fSDavid du Colombier 			if(cs->clicktotype == 0)
2109a747e4fSDavid du Colombier 				goto Change;
2119a747e4fSDavid du Colombier 			/* click to type: only change if button is down */
2129a747e4fSDavid du Colombier 			if(mouse.buttons == 0)
2139a747e4fSDavid du Colombier 				goto Send;
2149a747e4fSDavid du Colombier 		Change:
2159a747e4fSDavid du Colombier 			/* change of focus */
2169a747e4fSDavid du Colombier 			if(cs->focus != nil)
2179a747e4fSDavid du Colombier 				_ctlprint(cs->focus, "focus 0");
2189a747e4fSDavid du Colombier 			cs->focus = nil;
2199a747e4fSDavid du Colombier 			for(f=cs->actives; f!=nil; f=f->nextactive)
2209a747e4fSDavid du Colombier 				if(f->hidden == 0 && f->mouse && ptinrect(mouse.xy, f->rect)){
2219a747e4fSDavid du Colombier 					cs->focus = f;
2229a747e4fSDavid du Colombier 					_ctlprint(f, "focus 1");
2236f314b92SDavid du Colombier 					if (f->mouse) {
2246f314b92SDavid du Colombier 						if (debug) fprint(2, "f->mouse %s\n", f->name);
2259a747e4fSDavid du Colombier 						f->mouse(f, &mouse);
2266f314b92SDavid du Colombier 					}
2279a747e4fSDavid du Colombier 					break;
2289a747e4fSDavid du Colombier 				}
2299a747e4fSDavid du Colombier 		Send:
2306f314b92SDavid du Colombier 			if(cs->focus && cs->focus->mouse) {
2316f314b92SDavid du Colombier 				if (debug) fprint(2, "cs->focus->mouse %s\n", cs->focus->name);
2329a747e4fSDavid du Colombier 				cs->focus->mouse(cs->focus, &mouse);
2336f314b92SDavid du Colombier 			}
2349a747e4fSDavid du Colombier 			prevbut=mouse.buttons;
2359a747e4fSDavid du Colombier 			break;
2369a747e4fSDavid du Colombier 		case ACtl:
2379a747e4fSDavid du Colombier 			_ctlcontrol(cs, str);
2389a747e4fSDavid du Colombier 			free(str);
2399a747e4fSDavid du Colombier 			break;
2409a747e4fSDavid du Colombier 		case AExit:
2419a747e4fSDavid du Colombier 			threadexits(nil);
2429a747e4fSDavid du Colombier 		}
2439a747e4fSDavid du Colombier 	}
2449a747e4fSDavid du Colombier }
2459a747e4fSDavid du Colombier 
24680ee5cbfSDavid du Colombier Control*
_createctl(Controlset * cs,char * type,uint size,char * name)2479a747e4fSDavid du Colombier _createctl(Controlset *cs, char *type, uint size, char *name)
24880ee5cbfSDavid du Colombier {
24980ee5cbfSDavid du Colombier 	Control *c;
25080ee5cbfSDavid du Colombier 
2519a747e4fSDavid du Colombier 	c = _newcontrol(cs, size, name, type);
25280ee5cbfSDavid du Colombier 	if(c == nil)
2539a747e4fSDavid du Colombier 		ctlerror("can't create %s control %q: %r", type, name);
25480ee5cbfSDavid du Colombier 	return c;
25580ee5cbfSDavid du Colombier }
25680ee5cbfSDavid du Colombier 
25780ee5cbfSDavid du Colombier void
closecontrol(Control * c)25880ee5cbfSDavid du Colombier closecontrol(Control *c)
25980ee5cbfSDavid du Colombier {
26080ee5cbfSDavid du Colombier 	Control *prev, *p;
26180ee5cbfSDavid du Colombier 
26280ee5cbfSDavid du Colombier 	if(c == nil)
26380ee5cbfSDavid du Colombier 		return;
2649a747e4fSDavid du Colombier 	if (c == c->controlset->focus)
2659a747e4fSDavid du Colombier 		c->controlset->focus = nil;
2669a747e4fSDavid du Colombier 	if(c->exit)
2679a747e4fSDavid du Colombier 		c->exit(c);
26880ee5cbfSDavid du Colombier 
26980ee5cbfSDavid du Colombier 	prev = nil;
27080ee5cbfSDavid du Colombier 	for(p=c->controlset->controls; p; p=p->next){
27180ee5cbfSDavid du Colombier 		if(p == c)
27280ee5cbfSDavid du Colombier 			break;
27380ee5cbfSDavid du Colombier 		prev = p;
27480ee5cbfSDavid du Colombier 	}
27580ee5cbfSDavid du Colombier 	if(p == nil)
27680ee5cbfSDavid du Colombier 		ctlerror("closecontrol: no such control %q %p\n", c->name, c);
27780ee5cbfSDavid du Colombier 	if(prev == nil)
27880ee5cbfSDavid du Colombier 		c->controlset->controls = c->next;
27980ee5cbfSDavid du Colombier 	else
28080ee5cbfSDavid du Colombier 		prev->next = c->next;
28180ee5cbfSDavid du Colombier 
28280ee5cbfSDavid du Colombier 	/* is it active? if so, delete from active list */
28380ee5cbfSDavid du Colombier 	prev = nil;
28480ee5cbfSDavid du Colombier 	for(p=c->controlset->actives; p; p=p->nextactive){
28580ee5cbfSDavid du Colombier 		if(p == c)
28680ee5cbfSDavid du Colombier 			break;
28780ee5cbfSDavid du Colombier 		prev = p;
28880ee5cbfSDavid du Colombier 	}
28980ee5cbfSDavid du Colombier 	if(p != nil){
29080ee5cbfSDavid du Colombier 		if(prev == nil)
29180ee5cbfSDavid du Colombier 			c->controlset->actives = c->nextactive;
29280ee5cbfSDavid du Colombier 		else
29380ee5cbfSDavid du Colombier 			prev->nextactive = c->nextactive;
29480ee5cbfSDavid du Colombier 	}
29580ee5cbfSDavid du Colombier 
29680ee5cbfSDavid du Colombier 	if(!c->wevent)
29780ee5cbfSDavid du Colombier 		chanfree(c->event);
29880ee5cbfSDavid du Colombier 	if(!c->wdata)
29980ee5cbfSDavid du Colombier 		chanfree(c->data);
30080ee5cbfSDavid du Colombier 	free(c->name);
30180ee5cbfSDavid du Colombier 	free(c->format);
30280ee5cbfSDavid du Colombier 	free(c);
30380ee5cbfSDavid du Colombier }
30480ee5cbfSDavid du Colombier 
30580ee5cbfSDavid du Colombier Control*
controlcalled(char * name)30680ee5cbfSDavid du Colombier controlcalled(char *name)
30780ee5cbfSDavid du Colombier {
30880ee5cbfSDavid du Colombier 	Control *c;
30980ee5cbfSDavid du Colombier 	int i;
31080ee5cbfSDavid du Colombier 
31180ee5cbfSDavid du Colombier 	for(i=0; i<ncontrolset; i++)
31280ee5cbfSDavid du Colombier 		for(c=controlset[i]->controls; c; c=c->next)
31380ee5cbfSDavid du Colombier 			if(strcmp(c->name, name) == 0)
31480ee5cbfSDavid du Colombier 				return c;
31580ee5cbfSDavid du Colombier 	return nil;
31680ee5cbfSDavid du Colombier }
31780ee5cbfSDavid du Colombier 
31880ee5cbfSDavid du Colombier void
ctlerror(char * fmt,...)31980ee5cbfSDavid du Colombier ctlerror(char *fmt, ...)
32080ee5cbfSDavid du Colombier {
32180ee5cbfSDavid du Colombier 	va_list arg;
32280ee5cbfSDavid du Colombier 	char buf[256];
32380ee5cbfSDavid du Colombier 
32480ee5cbfSDavid du Colombier 	va_start(arg, fmt);
3259a747e4fSDavid du Colombier 	vfprint(2, fmt, arg);
32680ee5cbfSDavid du Colombier 	va_end(arg);
3279a747e4fSDavid du Colombier 	write(2, "\n", 1);
32880ee5cbfSDavid du Colombier 	threadexitsall(buf);
32980ee5cbfSDavid du Colombier }
33080ee5cbfSDavid du Colombier 
33180ee5cbfSDavid du Colombier Rune*
_ctlrunestr(char * s)33280ee5cbfSDavid du Colombier _ctlrunestr(char *s)
33380ee5cbfSDavid du Colombier {
33480ee5cbfSDavid du Colombier 	Rune *r, *ret;
33580ee5cbfSDavid du Colombier 
33680ee5cbfSDavid du Colombier 	ret = r = ctlmalloc((utflen(s)+1)*sizeof(Rune));
33780ee5cbfSDavid du Colombier 	while(*s != '\0')
33880ee5cbfSDavid du Colombier 		s += chartorune(r++, s);
33980ee5cbfSDavid du Colombier 	*r = L'\0';
34080ee5cbfSDavid du Colombier 	return ret;
34180ee5cbfSDavid du Colombier }
34280ee5cbfSDavid du Colombier 
34380ee5cbfSDavid du Colombier char*
_ctlstrrune(Rune * r)34480ee5cbfSDavid du Colombier _ctlstrrune(Rune *r)
34580ee5cbfSDavid du Colombier {
346*75e4bb06SDavid du Colombier 	int nb;
34780ee5cbfSDavid du Colombier 	char *s;
348*75e4bb06SDavid du Colombier 
349*75e4bb06SDavid du Colombier 	nb = runestrlen(r)*UTFmax+1;
350*75e4bb06SDavid du Colombier 	s = ctlmalloc(nb);
351*75e4bb06SDavid du Colombier 	snprint(s, nb, "%S", r);
35280ee5cbfSDavid du Colombier 	return s;
35380ee5cbfSDavid du Colombier }
35480ee5cbfSDavid du Colombier 
35580ee5cbfSDavid du Colombier void*
ctlmalloc(uint n)35680ee5cbfSDavid du Colombier ctlmalloc(uint n)
35780ee5cbfSDavid du Colombier {
35880ee5cbfSDavid du Colombier 	void *p;
35980ee5cbfSDavid du Colombier 
36080ee5cbfSDavid du Colombier 	p = mallocz(n, 1);
36180ee5cbfSDavid du Colombier 	if(p == nil)
36280ee5cbfSDavid du Colombier 		ctlerror("control allocation failed: %r");
36380ee5cbfSDavid du Colombier 	return p;
36480ee5cbfSDavid du Colombier }
36580ee5cbfSDavid du Colombier 
36680ee5cbfSDavid du Colombier void*
ctlrealloc(void * p,uint n)36780ee5cbfSDavid du Colombier ctlrealloc(void *p, uint n)
36880ee5cbfSDavid du Colombier {
36980ee5cbfSDavid du Colombier 	p = realloc(p, n);
37080ee5cbfSDavid du Colombier 	if(p == nil)
37180ee5cbfSDavid du Colombier 		ctlerror("control reallocation failed: %r");
37280ee5cbfSDavid du Colombier 	return p;
37380ee5cbfSDavid du Colombier }
37480ee5cbfSDavid du Colombier 
37580ee5cbfSDavid du Colombier char*
ctlstrdup(char * s)37680ee5cbfSDavid du Colombier ctlstrdup(char *s)
37780ee5cbfSDavid du Colombier {
37880ee5cbfSDavid du Colombier 	char *t;
37980ee5cbfSDavid du Colombier 
38080ee5cbfSDavid du Colombier 	t = strdup(s);
38180ee5cbfSDavid du Colombier 	if(t == nil)
38280ee5cbfSDavid du Colombier 		ctlerror("control strdup(%q) failed: %r", s);
38380ee5cbfSDavid du Colombier 	return t;
38480ee5cbfSDavid du Colombier }
38580ee5cbfSDavid du Colombier 
38680ee5cbfSDavid du Colombier static void
ctokenize(char * s,CParse * cp)38780ee5cbfSDavid du Colombier ctokenize(char *s, CParse *cp)
38880ee5cbfSDavid du Colombier {
38980ee5cbfSDavid du Colombier 	snprint(cp->str, sizeof cp->str, "%s", s);
3909a747e4fSDavid du Colombier 	cp->args = cp->pargs;
3919a747e4fSDavid du Colombier 	cp->nargs = tokenize(s, cp->args, nelem(cp->pargs));
39280ee5cbfSDavid du Colombier }
39380ee5cbfSDavid du Colombier 
394ac57dd0bSDavid du Colombier static int
ctlparse(CParse * cp,char * s,int hasreceiver)3959a747e4fSDavid du Colombier ctlparse(CParse *cp, char *s, int hasreceiver)
39680ee5cbfSDavid du Colombier {
3979a747e4fSDavid du Colombier 	int i;
39880ee5cbfSDavid du Colombier 	char *t;
39980ee5cbfSDavid du Colombier 
40080ee5cbfSDavid du Colombier 	/* keep original string for good error messages */
4019a747e4fSDavid du Colombier 	strncpy(cp->str, s, sizeof cp->str);
4029a747e4fSDavid du Colombier 	cp->str[sizeof cp->str - 1] = '\0';
40380ee5cbfSDavid du Colombier 	ctokenize(s, cp);
40480ee5cbfSDavid du Colombier 	if(cp->nargs == 0)
40580ee5cbfSDavid du Colombier 		return -1;
40680ee5cbfSDavid du Colombier 	/* strip leading sender name if present */
40780ee5cbfSDavid du Colombier 	cp->sender = nil;
40880ee5cbfSDavid du Colombier 	i = strlen(cp->args[0])-1;
40980ee5cbfSDavid du Colombier 	if(cp->args[0][i] == ':'){
41080ee5cbfSDavid du Colombier 		cp->sender = cp->args[0];
41180ee5cbfSDavid du Colombier 		cp->sender[i] = '\0';
4129a747e4fSDavid du Colombier 		cp->args++;
41380ee5cbfSDavid du Colombier 		cp->nargs--;
41480ee5cbfSDavid du Colombier 	}
4159a747e4fSDavid du Colombier 	if(hasreceiver){
4169a747e4fSDavid du Colombier 		if(cp->nargs-- == 0)
4179a747e4fSDavid du Colombier 			return -1;
4189a747e4fSDavid du Colombier 		cp->receiver = *cp->args++;
4199a747e4fSDavid du Colombier 	}else
4209a747e4fSDavid du Colombier 		cp->receiver = nil;
4219a747e4fSDavid du Colombier 	for(i=0; i<cp->nargs; i++){
42280ee5cbfSDavid du Colombier 		t = cp->args[i];
42380ee5cbfSDavid du Colombier 		while(*t == '[')	/* %R gives [0 0] [1 1]; atoi will stop at closing ] */
42480ee5cbfSDavid du Colombier 			t++;
42580ee5cbfSDavid du Colombier 		cp->iargs[i] = atoi(t);
42680ee5cbfSDavid du Colombier 	}
4279a747e4fSDavid du Colombier 	return cp->nargs;
42880ee5cbfSDavid du Colombier }
42980ee5cbfSDavid du Colombier 
43080ee5cbfSDavid du Colombier void
_ctlargcount(Control * c,CParse * cp,int n)43180ee5cbfSDavid du Colombier _ctlargcount(Control *c, CParse *cp, int n)
43280ee5cbfSDavid du Colombier {
43380ee5cbfSDavid du Colombier 	if(cp->nargs != n)
43480ee5cbfSDavid du Colombier 		ctlerror("%q: wrong argument count in '%s'", c->name, cp->str);
43580ee5cbfSDavid du Colombier }
43680ee5cbfSDavid du Colombier 
4379a747e4fSDavid du Colombier static void
_ctlcmd(Controlset * cs,char * s)4389a747e4fSDavid du Colombier _ctlcmd(Controlset *cs, char*s)
4399a747e4fSDavid du Colombier {
4409a747e4fSDavid du Colombier 	CParse cp;
4419a747e4fSDavid du Colombier 	char	*rcvrs[32];
4429a747e4fSDavid du Colombier 	int	ircvrs[32], n, i, hit;
4439a747e4fSDavid du Colombier 	Control *c;
4449a747e4fSDavid du Colombier 
4459a747e4fSDavid du Colombier //	fprint(2, "_ctlcmd: %s\n", s);
4469a747e4fSDavid du Colombier 	cp.args = cp.pargs;
4479a747e4fSDavid du Colombier 	if (ctlparse(&cp, s, 1) < 0)
4489a747e4fSDavid du Colombier 		ctlerror("bad command string: %q", cp.str);
4499a747e4fSDavid du Colombier 	if (cp.nargs == 0 && strcmp(cp.receiver, "sync") == 0){
4509a747e4fSDavid du Colombier 		chanprint(cs->data, "sync");
4519a747e4fSDavid du Colombier 		return;
4529a747e4fSDavid du Colombier 	}
4539a747e4fSDavid du Colombier 	if (cp.nargs == 0)
4549a747e4fSDavid du Colombier 		ctlerror("no command in command string: %q", cp.str);
4559a747e4fSDavid du Colombier 
4569a747e4fSDavid du Colombier 	n = tokenize(cp.receiver, rcvrs, nelem(rcvrs));
4579a747e4fSDavid du Colombier 
4589a747e4fSDavid du Colombier 	// lookup type names: a receiver can be a named type or a named control
4599a747e4fSDavid du Colombier 	for (i = 0; i < n; i++)
4609a747e4fSDavid du Colombier 		ircvrs[i] = _ctllookup(rcvrs[i], ctltypenames, Ntypes);
4619a747e4fSDavid du Colombier 
4629a747e4fSDavid du Colombier 	for(c = cs->controls; c != nil; c = c->next){
4639a747e4fSDavid du Colombier 		/* if a control matches on more than one receiver element,
4649a747e4fSDavid du Colombier 		 * make sure it gets processed once; hence loop through controls
4659a747e4fSDavid du Colombier 		 * in the outer loop
4669a747e4fSDavid du Colombier 		 */
4679a747e4fSDavid du Colombier 		hit = 0;
4689a747e4fSDavid du Colombier 		for (i = 0; i < n; i++)
4699a747e4fSDavid du Colombier 			if(strcmp(c->name, rcvrs[i]) == 0 || c->type == ircvrs[i])
4709a747e4fSDavid du Colombier 				hit++;
4719a747e4fSDavid du Colombier 		if (hit && c->ctl)
4729a747e4fSDavid du Colombier 			c->ctl(c, &cp);
4739a747e4fSDavid du Colombier 	}
4749a747e4fSDavid du Colombier }
4759a747e4fSDavid du Colombier 
4769a747e4fSDavid du Colombier static void
_ctlcontrol(Controlset * cs,char * s)4779a747e4fSDavid du Colombier _ctlcontrol(Controlset *cs, char *s)
47880ee5cbfSDavid du Colombier {
47980ee5cbfSDavid du Colombier 	char *lines[16];
48080ee5cbfSDavid du Colombier 	int i, n;
48180ee5cbfSDavid du Colombier 	char *l;
48280ee5cbfSDavid du Colombier 
4839a747e4fSDavid du Colombier //	fprint(2, "_ctlcontrol: %s\n", s);
4849a747e4fSDavid du Colombier 	n = gettokens(s, lines, nelem(lines), "\n");
48580ee5cbfSDavid du Colombier 	for(i=0; i<n; i++){
48680ee5cbfSDavid du Colombier 		l = lines[i];
48780ee5cbfSDavid du Colombier 		while(*l==' ' || *l=='\t')
48880ee5cbfSDavid du Colombier 			l++;
48980ee5cbfSDavid du Colombier 		if(*l != '\0')
4909a747e4fSDavid du Colombier 			_ctlcmd(cs, l);
49180ee5cbfSDavid du Colombier 	}
49280ee5cbfSDavid du Colombier }
49380ee5cbfSDavid du Colombier 
49480ee5cbfSDavid du Colombier Rune*
_ctlgetsnarf(void)49580ee5cbfSDavid du Colombier _ctlgetsnarf(void)
49680ee5cbfSDavid du Colombier {
49780ee5cbfSDavid du Colombier 	int i, n;
49880ee5cbfSDavid du Colombier 	char *sn, buf[512];
49980ee5cbfSDavid du Colombier 	Rune *snarf;
50080ee5cbfSDavid du Colombier 
50180ee5cbfSDavid du Colombier 	if(_ctlsnarffd < 0)
50280ee5cbfSDavid du Colombier 		return nil;
50380ee5cbfSDavid du Colombier 	sn = nil;
50480ee5cbfSDavid du Colombier 	i = 0;
50580ee5cbfSDavid du Colombier 	seek(_ctlsnarffd, 0, 0);
50680ee5cbfSDavid du Colombier 	while((n = read(_ctlsnarffd, buf, sizeof buf)) > 0){
50780ee5cbfSDavid du Colombier 		sn = ctlrealloc(sn, i+n+1);
50880ee5cbfSDavid du Colombier 		memmove(sn+i, buf, n);
50980ee5cbfSDavid du Colombier 		i += n;
51080ee5cbfSDavid du Colombier 		sn[i] = 0;
51180ee5cbfSDavid du Colombier 	}
51280ee5cbfSDavid du Colombier 	snarf = nil;
51380ee5cbfSDavid du Colombier 	if(i > 0){
51480ee5cbfSDavid du Colombier 		snarf = _ctlrunestr(sn);
51580ee5cbfSDavid du Colombier 		free(sn);
51680ee5cbfSDavid du Colombier 	}
51780ee5cbfSDavid du Colombier 	return snarf;
51880ee5cbfSDavid du Colombier }
51980ee5cbfSDavid du Colombier 
52080ee5cbfSDavid du Colombier void
_ctlputsnarf(Rune * snarf)52180ee5cbfSDavid du Colombier _ctlputsnarf(Rune *snarf)
52280ee5cbfSDavid du Colombier {
52380ee5cbfSDavid du Colombier 	int fd, i, n, nsnarf;
52480ee5cbfSDavid du Colombier 
52580ee5cbfSDavid du Colombier 	if(_ctlsnarffd<0 || snarf[0]==0)
52680ee5cbfSDavid du Colombier 		return;
52780ee5cbfSDavid du Colombier 	fd = open("/dev/snarf", OWRITE);
52880ee5cbfSDavid du Colombier 	if(fd < 0)
52980ee5cbfSDavid du Colombier 		return;
53080ee5cbfSDavid du Colombier 	nsnarf = runestrlen(snarf);
53180ee5cbfSDavid du Colombier 	/* snarf buffer could be huge, so fprint will truncate; do it in blocks */
53280ee5cbfSDavid du Colombier 	for(i=0; i<nsnarf; i+=n){
53380ee5cbfSDavid du Colombier 		n = nsnarf-i;
53480ee5cbfSDavid du Colombier 		if(n >= 256)
53580ee5cbfSDavid du Colombier 			n = 256;
5369a747e4fSDavid du Colombier 		if(fprint(fd, "%.*S", n, snarf+i) < 0)
53780ee5cbfSDavid du Colombier 			break;
53880ee5cbfSDavid du Colombier 	}
53980ee5cbfSDavid du Colombier 	close(fd);
54080ee5cbfSDavid du Colombier }
54180ee5cbfSDavid du Colombier 
54280ee5cbfSDavid du Colombier int
_ctlalignment(char * s)54380ee5cbfSDavid du Colombier _ctlalignment(char *s)
54480ee5cbfSDavid du Colombier {
54580ee5cbfSDavid du Colombier 	int i;
54680ee5cbfSDavid du Colombier 
5479a747e4fSDavid du Colombier 	i = _ctllookup(s, alignnames, Nalignments);
5489a747e4fSDavid du Colombier 	if (i < 0)
54980ee5cbfSDavid du Colombier 		ctlerror("unknown alignment: %s", s);
5509a747e4fSDavid du Colombier 	return i;
55180ee5cbfSDavid du Colombier }
55280ee5cbfSDavid du Colombier 
55380ee5cbfSDavid du Colombier Point
_ctlalignpoint(Rectangle r,int dx,int dy,int align)55480ee5cbfSDavid du Colombier _ctlalignpoint(Rectangle r, int dx, int dy, int align)
55580ee5cbfSDavid du Colombier {
55680ee5cbfSDavid du Colombier 	Point p;
55780ee5cbfSDavid du Colombier 
55880ee5cbfSDavid du Colombier 	p = r.min;	/* in case of trouble */
55980ee5cbfSDavid du Colombier 	switch(align%3){
56080ee5cbfSDavid du Colombier 	case 0:	/* left */
56180ee5cbfSDavid du Colombier 		p.x = r.min.x;
56280ee5cbfSDavid du Colombier 		break;
56380ee5cbfSDavid du Colombier 	case 1:	/* center */
56480ee5cbfSDavid du Colombier 		p.x = r.min.x+(Dx(r)-dx)/2;
56580ee5cbfSDavid du Colombier 		break;
56680ee5cbfSDavid du Colombier 	case 2:	/* right */
56780ee5cbfSDavid du Colombier 		p.x = r.max.x-dx;
56880ee5cbfSDavid du Colombier 		break;
56980ee5cbfSDavid du Colombier 	}
57080ee5cbfSDavid du Colombier 	switch((align/3)%3){
57180ee5cbfSDavid du Colombier 	case 0:	/* top */
57280ee5cbfSDavid du Colombier 		p.y = r.min.y;
57380ee5cbfSDavid du Colombier 		break;
57480ee5cbfSDavid du Colombier 	case 1:	/* center */
57580ee5cbfSDavid du Colombier 		p.y = r.min.y+(Dy(r)-dy)/2;
57680ee5cbfSDavid du Colombier 		break;
57780ee5cbfSDavid du Colombier 	case 2:	/* bottom */
57880ee5cbfSDavid du Colombier 		p.y = r.max.y - dy;
57980ee5cbfSDavid du Colombier 		break;
58080ee5cbfSDavid du Colombier 	}
58180ee5cbfSDavid du Colombier 	return p;
58280ee5cbfSDavid du Colombier }
58380ee5cbfSDavid du Colombier 
58480ee5cbfSDavid du Colombier void
controlwire(Control * cfrom,char * name,Channel * chan)5859a747e4fSDavid du Colombier controlwire(Control *cfrom, char *name, Channel *chan)
58680ee5cbfSDavid du Colombier {
58780ee5cbfSDavid du Colombier 	Channel **p;
58880ee5cbfSDavid du Colombier 
58980ee5cbfSDavid du Colombier 	p = nil;
5909a747e4fSDavid du Colombier 	if(strcmp(name, "event") == 0){
5919a747e4fSDavid du Colombier 		p = &cfrom->event;
5929a747e4fSDavid du Colombier 		cfrom->wevent = 1;
5939a747e4fSDavid du Colombier 	}else if(strcmp(name, "data") == 0){
5949a747e4fSDavid du Colombier 		p = &cfrom->data;
5959a747e4fSDavid du Colombier 		cfrom->wdata = 1;
59680ee5cbfSDavid du Colombier 	}else
5979a747e4fSDavid du Colombier 		ctlerror("%q: unknown controlwire channel %s", cfrom->name, name);
59880ee5cbfSDavid du Colombier 	chanfree(*p);
5999a747e4fSDavid du Colombier 	*p = chan;
60080ee5cbfSDavid du Colombier }
60180ee5cbfSDavid du Colombier 
60280ee5cbfSDavid du Colombier void
_ctlfocus(Control * me,int set)6039a747e4fSDavid du Colombier _ctlfocus(Control *me, int set)
60480ee5cbfSDavid du Colombier {
60580ee5cbfSDavid du Colombier 	Controlset *cs;
60680ee5cbfSDavid du Colombier 
6079a747e4fSDavid du Colombier 	cs = me->controlset;
6089a747e4fSDavid du Colombier 	if(set){
6099a747e4fSDavid du Colombier 		if(cs->focus == me)
61080ee5cbfSDavid du Colombier 			return;
6119a747e4fSDavid du Colombier 		if(cs->focus != nil)
6129a747e4fSDavid du Colombier 			_ctlprint(cs->focus, "focus 0");
6139a747e4fSDavid du Colombier 		cs->focus = me;
6149a747e4fSDavid du Colombier 	}else{
6159a747e4fSDavid du Colombier 		if(cs->focus != me)
6169a747e4fSDavid du Colombier 			return;
6179a747e4fSDavid du Colombier 		cs->focus = nil;
61880ee5cbfSDavid du Colombier 	}
61980ee5cbfSDavid du Colombier }
62080ee5cbfSDavid du Colombier 
62180ee5cbfSDavid du Colombier static void
resizethread(void * v)62280ee5cbfSDavid du Colombier resizethread(void *v)
62380ee5cbfSDavid du Colombier {
62480ee5cbfSDavid du Colombier 	Controlset *cs;
62580ee5cbfSDavid du Colombier 	char buf[64];
62680ee5cbfSDavid du Colombier 	Alt alts[3];
62780ee5cbfSDavid du Colombier 
62880ee5cbfSDavid du Colombier 	cs = v;
62980ee5cbfSDavid du Colombier 	snprint(buf, sizeof buf, "resizethread0x%p", cs);
63080ee5cbfSDavid du Colombier 	threadsetname(buf);
63180ee5cbfSDavid du Colombier 
63280ee5cbfSDavid du Colombier 	alts[0].c = cs->resizec;
63380ee5cbfSDavid du Colombier 	alts[0].v = nil;
63480ee5cbfSDavid du Colombier 	alts[0].op = CHANRCV;
63580ee5cbfSDavid du Colombier 	alts[1].c = cs->resizeexitc;
63680ee5cbfSDavid du Colombier 	alts[1].v = nil;
63780ee5cbfSDavid du Colombier 	alts[1].op = CHANRCV;
63880ee5cbfSDavid du Colombier 	alts[2].op = CHANEND;
63980ee5cbfSDavid du Colombier 
64080ee5cbfSDavid du Colombier 	for(;;){
64180ee5cbfSDavid du Colombier 		switch(alt(alts)){
64280ee5cbfSDavid du Colombier 		case 0:
64380ee5cbfSDavid du Colombier 			resizecontrolset(cs);
64480ee5cbfSDavid du Colombier 			break;
64580ee5cbfSDavid du Colombier 		case 1:
64680ee5cbfSDavid du Colombier 			return;
64780ee5cbfSDavid du Colombier 		}
64880ee5cbfSDavid du Colombier 	}
64980ee5cbfSDavid du Colombier }
65080ee5cbfSDavid du Colombier 
65180ee5cbfSDavid du Colombier void
activate(Control * a)65280ee5cbfSDavid du Colombier activate(Control *a)
65380ee5cbfSDavid du Colombier {
65480ee5cbfSDavid du Colombier 	Control *c;
65580ee5cbfSDavid du Colombier 
65680ee5cbfSDavid du Colombier 	for(c=a->controlset->actives; c; c=c->nextactive)
65780ee5cbfSDavid du Colombier 		if(c == a)
65880ee5cbfSDavid du Colombier 			ctlerror("%q already active\n", a->name);
6599a747e4fSDavid du Colombier 
6609a747e4fSDavid du Colombier 	if (a->activate){
6619a747e4fSDavid du Colombier 		a->activate(a, 1);
6629a747e4fSDavid du Colombier 		return;
6639a747e4fSDavid du Colombier 	}
6649a747e4fSDavid du Colombier 	/* prepend */
66580ee5cbfSDavid du Colombier 	a->nextactive = a->controlset->actives;
66680ee5cbfSDavid du Colombier 	a->controlset->actives = a;
66780ee5cbfSDavid du Colombier }
66880ee5cbfSDavid du Colombier 
66980ee5cbfSDavid du Colombier void
deactivate(Control * a)67080ee5cbfSDavid du Colombier deactivate(Control *a)
67180ee5cbfSDavid du Colombier {
67280ee5cbfSDavid du Colombier 	Control *c, *prev;
67380ee5cbfSDavid du Colombier 
6749a747e4fSDavid du Colombier 	/* if group, first deactivate kids, then self */
6759a747e4fSDavid du Colombier 	if (a->activate){
6769a747e4fSDavid du Colombier 		a->activate(a, 0);
6779a747e4fSDavid du Colombier 		return;
6789a747e4fSDavid du Colombier 	}
67980ee5cbfSDavid du Colombier 	prev = nil;
68080ee5cbfSDavid du Colombier 	for(c=a->controlset->actives; c; c=c->nextactive){
68180ee5cbfSDavid du Colombier 		if(c == a){
68280ee5cbfSDavid du Colombier 			if(a->controlset->focus == a)
68380ee5cbfSDavid du Colombier 				a->controlset->focus = nil;
68480ee5cbfSDavid du Colombier 			if(prev != nil)
68580ee5cbfSDavid du Colombier 				prev->nextactive = a->nextactive;
68680ee5cbfSDavid du Colombier 			else
68780ee5cbfSDavid du Colombier 				a->controlset->actives = a->nextactive;
68880ee5cbfSDavid du Colombier 			return;
68980ee5cbfSDavid du Colombier 		}
69080ee5cbfSDavid du Colombier 		prev = c;
69180ee5cbfSDavid du Colombier 	}
69280ee5cbfSDavid du Colombier 	ctlerror("%q not active\n", a->name);
69380ee5cbfSDavid du Colombier }
69480ee5cbfSDavid du Colombier 
69580ee5cbfSDavid du Colombier static struct
69680ee5cbfSDavid du Colombier {
69780ee5cbfSDavid du Colombier 	char	*name;
69880ee5cbfSDavid du Colombier 	ulong	color;
69980ee5cbfSDavid du Colombier }coltab[] = {
70080ee5cbfSDavid du Colombier 	"red",			DRed,
70180ee5cbfSDavid du Colombier 	"green",			DGreen,
70280ee5cbfSDavid du Colombier 	"blue",			DBlue,
70380ee5cbfSDavid du Colombier 	"cyan",			DCyan,
70480ee5cbfSDavid du Colombier 	"magenta",		DMagenta,
70580ee5cbfSDavid du Colombier 	"yellow",			DYellow,
70680ee5cbfSDavid du Colombier 	"paleyellow",		DPaleyellow,
70780ee5cbfSDavid du Colombier 	"darkyellow",		DDarkyellow,
70880ee5cbfSDavid du Colombier 	"darkgreen",		DDarkgreen,
70980ee5cbfSDavid du Colombier 	"palegreen",		DPalegreen,
71080ee5cbfSDavid du Colombier 	"medgreen",		DMedgreen,
71180ee5cbfSDavid du Colombier 	"darkblue",		DDarkblue,
71280ee5cbfSDavid du Colombier 	"palebluegreen",	DPalebluegreen,
71380ee5cbfSDavid du Colombier 	"paleblue",		DPaleblue,
71480ee5cbfSDavid du Colombier 	"bluegreen",		DBluegreen,
71580ee5cbfSDavid du Colombier 	"greygreen",		DGreygreen,
71680ee5cbfSDavid du Colombier 	"palegreygreen",	DPalegreygreen,
71780ee5cbfSDavid du Colombier 	"yellowgreen",		DYellowgreen,
71880ee5cbfSDavid du Colombier 	"medblue",		DMedblue,
71980ee5cbfSDavid du Colombier 	"greyblue",		DGreyblue,
72080ee5cbfSDavid du Colombier 	"palegreyblue",		DPalegreyblue,
72180ee5cbfSDavid du Colombier 	"purpleblue",		DPurpleblue,
72280ee5cbfSDavid du Colombier 	nil,	0
72380ee5cbfSDavid du Colombier };
72480ee5cbfSDavid du Colombier 
72580ee5cbfSDavid du Colombier void
initcontrols(void)72680ee5cbfSDavid du Colombier initcontrols(void)
72780ee5cbfSDavid du Colombier {
72880ee5cbfSDavid du Colombier 	int i;
72980ee5cbfSDavid du Colombier 	Image *im;
73080ee5cbfSDavid du Colombier 
73180ee5cbfSDavid du Colombier 	quotefmtinstall();
73280ee5cbfSDavid du Colombier 	namectlimage(display->opaque, "opaque");
73380ee5cbfSDavid du Colombier 	namectlimage(display->transparent, "transparent");
73480ee5cbfSDavid du Colombier 	namectlimage(display->white, "white");
73580ee5cbfSDavid du Colombier 	namectlimage(display->black, "black");
73680ee5cbfSDavid du Colombier 	for(i=0; coltab[i].name!=nil; i++){
73780ee5cbfSDavid du Colombier 		im = allocimage(display, Rect(0,0,1,1), RGB24, 1, coltab[i].color);
73880ee5cbfSDavid du Colombier 		namectlimage(im, coltab[i].name);
73980ee5cbfSDavid du Colombier 	}
74080ee5cbfSDavid du Colombier 	namectlfont(font, "font");
74180ee5cbfSDavid du Colombier 	_ctlsnarffd = open("/dev/snarf", OREAD);
74280ee5cbfSDavid du Colombier }
74380ee5cbfSDavid du Colombier 
74480ee5cbfSDavid du Colombier Controlset*
newcontrolset(Image * im,Channel * kbdc,Channel * mousec,Channel * resizec)74580ee5cbfSDavid du Colombier newcontrolset(Image *im, Channel *kbdc, Channel *mousec, Channel *resizec)
74680ee5cbfSDavid du Colombier {
74780ee5cbfSDavid du Colombier 	Controlset *cs;
74880ee5cbfSDavid du Colombier 
74980ee5cbfSDavid du Colombier 	if(im == nil)
75080ee5cbfSDavid du Colombier 		im = screen;
75180ee5cbfSDavid du Colombier 	if((mousec==nil && resizec!=nil) || (mousec!=nil && resizec==nil))
75280ee5cbfSDavid du Colombier 		ctlerror("must specify either or both of mouse and resize channels");
75380ee5cbfSDavid du Colombier 
75480ee5cbfSDavid du Colombier 	cs = ctlmalloc(sizeof(Controlset));
75580ee5cbfSDavid du Colombier 	cs->screen = im;
75680ee5cbfSDavid du Colombier 
75780ee5cbfSDavid du Colombier 	if(kbdc == nil){
75880ee5cbfSDavid du Colombier 		cs->keyboardctl = initkeyboard(nil);
75980ee5cbfSDavid du Colombier 		if(cs->keyboardctl == nil)
76080ee5cbfSDavid du Colombier 			ctlerror("can't initialize keyboard: %r");
76180ee5cbfSDavid du Colombier 		kbdc = cs->keyboardctl->c;
76280ee5cbfSDavid du Colombier 	}
76380ee5cbfSDavid du Colombier 	cs ->kbdc = kbdc;
76480ee5cbfSDavid du Colombier 
76580ee5cbfSDavid du Colombier 	if(mousec == nil){
76680ee5cbfSDavid du Colombier 		cs->mousectl = initmouse(nil, im);
76780ee5cbfSDavid du Colombier 		if(cs->mousectl == nil)
76880ee5cbfSDavid du Colombier 			ctlerror("can't initialize mouse: %r");
76980ee5cbfSDavid du Colombier 		mousec = cs->mousectl->c;
77080ee5cbfSDavid du Colombier 		resizec = cs->mousectl->resizec;
77180ee5cbfSDavid du Colombier 	}
77280ee5cbfSDavid du Colombier 	cs->mousec = mousec;
77380ee5cbfSDavid du Colombier 	cs->resizec = resizec;
774b7b24591SDavid du Colombier 	cs->ctl = chancreate(sizeof(char*), 64);	/* buffer to prevent deadlock */
7759a747e4fSDavid du Colombier 	cs->data = chancreate(sizeof(char*), 0);
77680ee5cbfSDavid du Colombier 	cs->resizeexitc = chancreate(sizeof(int), 0);
7779a747e4fSDavid du Colombier 	cs->csexitc = chancreate(sizeof(int), 0);
77880ee5cbfSDavid du Colombier 
7799a747e4fSDavid du Colombier 	threadcreate(resizethread, cs, 32*1024);
7809a747e4fSDavid du Colombier 	threadcreate(controlsetthread, cs, 32*1024);
78180ee5cbfSDavid du Colombier 
78280ee5cbfSDavid du Colombier 	controlset = ctlrealloc(controlset, (ncontrolset+1)*sizeof(Controlset*));
78380ee5cbfSDavid du Colombier 	controlset[ncontrolset++] = cs;
78480ee5cbfSDavid du Colombier 	return cs;
78580ee5cbfSDavid du Colombier }
78680ee5cbfSDavid du Colombier 
78780ee5cbfSDavid du Colombier void
closecontrolset(Controlset * cs)78880ee5cbfSDavid du Colombier closecontrolset(Controlset *cs)
78980ee5cbfSDavid du Colombier {
79080ee5cbfSDavid du Colombier 	int i;
79180ee5cbfSDavid du Colombier 
7929a747e4fSDavid du Colombier 	sendul(cs->resizeexitc, 0);
7939a747e4fSDavid du Colombier 	chanfree(cs->resizeexitc);
7949a747e4fSDavid du Colombier 	sendul(cs->csexitc, 0);
7959a747e4fSDavid du Colombier 	chanfree(cs->csexitc);
7969a747e4fSDavid du Colombier 	chanfree(cs->ctl);
7979a747e4fSDavid du Colombier 	chanfree(cs->data);
7989a747e4fSDavid du Colombier 
79980ee5cbfSDavid du Colombier 	for(i=0; i<ncontrolset; i++)
80080ee5cbfSDavid du Colombier 		if(cs == controlset[i]){
80180ee5cbfSDavid du Colombier 			memmove(controlset+i, controlset+i+1, (ncontrolset-(i+1))*sizeof(Controlset*));
80280ee5cbfSDavid du Colombier 			ncontrolset--;
80380ee5cbfSDavid du Colombier 			goto Found;
80480ee5cbfSDavid du Colombier 		}
80580ee5cbfSDavid du Colombier 
80680ee5cbfSDavid du Colombier 	if(i == ncontrolset)
80780ee5cbfSDavid du Colombier 		ctlerror("closecontrolset: control set not found");
80880ee5cbfSDavid du Colombier 
80980ee5cbfSDavid du Colombier     Found:
81080ee5cbfSDavid du Colombier 	while(cs->controls != nil)
81180ee5cbfSDavid du Colombier 		closecontrol(cs->controls);
81280ee5cbfSDavid du Colombier }
813