xref: /plan9/sys/src/libcontrol/control.c (revision ac57dd0bdfb9d49ce3ebb32937bb07f2cec3da6c)
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 
99a747e4fSDavid du Colombier enum	/* alts */
109a747e4fSDavid du Colombier {
119a747e4fSDavid du Colombier 	AKey,
129a747e4fSDavid du Colombier 	AMouse,
139a747e4fSDavid du Colombier 	ACtl,
149a747e4fSDavid du Colombier 	AExit,
159a747e4fSDavid du Colombier 	NALT
169a747e4fSDavid du Colombier };
179a747e4fSDavid du Colombier 
1880ee5cbfSDavid du Colombier static Controlset **controlset;
1980ee5cbfSDavid du Colombier int	ncontrolset;
2080ee5cbfSDavid du Colombier int	ctldeletequits;
2180ee5cbfSDavid du Colombier 
2280ee5cbfSDavid du Colombier char *alignnames[Nalignments] = {
2380ee5cbfSDavid du Colombier 	[Aupperleft] =		"upperleft",
2480ee5cbfSDavid du Colombier 	[Auppercenter] =	"uppercenter",
2580ee5cbfSDavid du Colombier 	[Aupperright] =		"upperright",
2680ee5cbfSDavid du Colombier 	[Acenterleft] =		"centerleft",
2780ee5cbfSDavid du Colombier 	[Acenter] =		"center",
2880ee5cbfSDavid du Colombier 	[Acenterright] =	"centerright",
2980ee5cbfSDavid du Colombier 	[Alowerleft] =		"lowerleft",
3080ee5cbfSDavid du Colombier 	[Alowercenter] =	"lowercenter",
3180ee5cbfSDavid du Colombier 	[Alowerright] =		"lowerright",
3280ee5cbfSDavid du Colombier };
3380ee5cbfSDavid du Colombier 
349a747e4fSDavid du Colombier char *ctltypenames[Ntypes] = {
359a747e4fSDavid du Colombier 	[Ctlunknown] =		"unknown",
369a747e4fSDavid du Colombier 	[Ctlbox] =			"box",
379a747e4fSDavid du Colombier 	[Ctlbutton] =		"button",
389a747e4fSDavid du Colombier 	[Ctlentry] =		"entry",
399a747e4fSDavid du Colombier 	[Ctlkeyboard] =		"keyboard",
409a747e4fSDavid du Colombier 	[Ctllabel] =		"label",
419a747e4fSDavid du Colombier 	[Ctlmenu] =		"menu",
429a747e4fSDavid du Colombier 	[Ctlradio] =		"radio",
439a747e4fSDavid du Colombier 	[Ctlscribble] =		"scribble",
449a747e4fSDavid du Colombier 	[Ctlslider] =		"slider",
459a747e4fSDavid du Colombier 	[Ctltabs] =			"tabs",
469a747e4fSDavid du Colombier 	[Ctltext] =			"text",
479a747e4fSDavid du Colombier 	[Ctltextbutton] =	"textbutton",
489a747e4fSDavid du Colombier 	[Ctlgroup] =		"group",		// divider between controls and metacontrols
499a747e4fSDavid du Colombier 	[Ctlboxbox] =		"boxbox",
509a747e4fSDavid du Colombier 	[Ctlcolumn] =		"column",
519a747e4fSDavid du Colombier 	[Ctlrow] =			"row",
529a747e4fSDavid du Colombier 	[Ctlstack] =		"stack",
539a747e4fSDavid du Colombier 	[Ctltab] =			"tab",
549a747e4fSDavid du Colombier };
559a747e4fSDavid du Colombier 
569a747e4fSDavid du Colombier static void	_ctlcmd(Controlset*, char*);
579a747e4fSDavid du Colombier static void	_ctlcontrol(Controlset*, char*);
589a747e4fSDavid du Colombier 
599a747e4fSDavid du Colombier static char*
609a747e4fSDavid du Colombier _mkctlcmd(Control *c, char *fmt, va_list arg)
619a747e4fSDavid du Colombier {
629a747e4fSDavid du Colombier 	char *name, *p, *both;
639a747e4fSDavid du Colombier 
649a747e4fSDavid du Colombier 	name = quotestrdup(c->name);
659a747e4fSDavid du Colombier 	if(name == nil)
669a747e4fSDavid du Colombier 		ctlerror("quotestrdup in ctlprint failed");
679a747e4fSDavid du Colombier 	p = vsmprint(fmt, arg);
689a747e4fSDavid du Colombier 	if(p == nil){
699a747e4fSDavid du Colombier 		free(name);
709a747e4fSDavid du Colombier 		ctlerror("vsmprint1 in ctlprint failed");
719a747e4fSDavid du Colombier 	}
729a747e4fSDavid du Colombier 	both = ctlmalloc(strlen(name)+strlen(p)+2);
739a747e4fSDavid du Colombier 	strcpy(both, name);
749a747e4fSDavid du Colombier 	strcat(both, " ");
759a747e4fSDavid du Colombier 	strcat(both, p);
769a747e4fSDavid du Colombier 	free(name);
779a747e4fSDavid du Colombier 	free(p);
789a747e4fSDavid du Colombier 	return both;
799a747e4fSDavid du Colombier }
809a747e4fSDavid du Colombier 
819a747e4fSDavid du Colombier int
829a747e4fSDavid du Colombier ctlprint(Control *c, char *fmt, ...)
839a747e4fSDavid du Colombier {
849a747e4fSDavid du Colombier 	int n;
859a747e4fSDavid du Colombier 	char *p;
869a747e4fSDavid du Colombier 	va_list arg;
879a747e4fSDavid du Colombier 
889a747e4fSDavid du Colombier 	va_start(arg, fmt);
899a747e4fSDavid du Colombier 	p = _mkctlcmd(c, fmt, arg);
909a747e4fSDavid du Colombier 	va_end(arg);
919a747e4fSDavid du Colombier 	n = sendp(c->controlset->ctl, p);
929a747e4fSDavid du Colombier 	yield();
939a747e4fSDavid du Colombier 	return n;
949a747e4fSDavid du Colombier }
959a747e4fSDavid du Colombier 
969a747e4fSDavid du Colombier void
979a747e4fSDavid du Colombier _ctlprint(Control *c, char *fmt, ...)
989a747e4fSDavid du Colombier {
999a747e4fSDavid du Colombier 	char *p;
1009a747e4fSDavid du Colombier 	va_list arg;
1019a747e4fSDavid du Colombier 
1029a747e4fSDavid du Colombier 	va_start(arg, fmt);
1039a747e4fSDavid du Colombier 	p = _mkctlcmd(c, fmt, arg);
1049a747e4fSDavid du Colombier 	va_end(arg);
1059a747e4fSDavid du Colombier 	_ctlcmd(c->controlset, p);
1069a747e4fSDavid du Colombier 	free(p);
1079a747e4fSDavid du Colombier }
1089a747e4fSDavid du Colombier 
1099a747e4fSDavid du Colombier int
1109a747e4fSDavid du Colombier _ctllookup(char *s, char *tab[], int ntab)
1119a747e4fSDavid du Colombier {
1129a747e4fSDavid du Colombier 	int i;
1139a747e4fSDavid du Colombier 
1149a747e4fSDavid du Colombier 	for(i=0; i<ntab && tab[i] != nil; i++)
1159a747e4fSDavid du Colombier 		if(strcmp(s, tab[i]) == 0)
1169a747e4fSDavid du Colombier 			return i;
1179a747e4fSDavid du Colombier 	return -1;
1189a747e4fSDavid du Colombier }
1199a747e4fSDavid du Colombier 
12080ee5cbfSDavid du Colombier static Control*
1219a747e4fSDavid du Colombier _newcontrol(Controlset *cs, uint n, char *name, char *type)
12280ee5cbfSDavid du Colombier {
12380ee5cbfSDavid du Colombier 	Control *c;
12480ee5cbfSDavid du Colombier 
12580ee5cbfSDavid du Colombier 	for(c=cs->controls; c; c=c->next)
12680ee5cbfSDavid du Colombier 		if(strcmp(c->name, name) == 0){
12780ee5cbfSDavid du Colombier 			werrstr("control %q already defined", name);
12880ee5cbfSDavid du Colombier 			return nil;
12980ee5cbfSDavid du Colombier 		}
13080ee5cbfSDavid du Colombier 	c = ctlmalloc(n);
13180ee5cbfSDavid du Colombier 	c->screen = cs->screen;
13280ee5cbfSDavid du Colombier 	c->name = ctlstrdup(name);
1339a747e4fSDavid du Colombier 	c->type = _ctllookup(type, ctltypenames, Ntypes);
1349a747e4fSDavid du Colombier 	if (c->type < 0)
1359a747e4fSDavid du Colombier 		ctlerror("unknown type: %s", type);
136b7b24591SDavid du Colombier 	c->event = chancreate(sizeof(char*), 64);
13780ee5cbfSDavid du Colombier 	c->data = chancreate(sizeof(char*), 0);
1389a747e4fSDavid du Colombier 	c->size = Rect(1, 1, _Ctlmaxsize, _Ctlmaxsize);
1399a747e4fSDavid du Colombier 	c->hidden = 0;
1409a747e4fSDavid du Colombier 	c->ctl = nil;
1419a747e4fSDavid du Colombier 	c->mouse = nil;
1429a747e4fSDavid du Colombier 	c->key = nil;
1439a747e4fSDavid du Colombier 	c->exit = nil;
1449a747e4fSDavid du Colombier 	c->setsize = nil;
14580ee5cbfSDavid du Colombier 
14680ee5cbfSDavid du Colombier 	c->controlset = cs;
14780ee5cbfSDavid du Colombier 	c->next = cs->controls;
14880ee5cbfSDavid du Colombier 	cs->controls = c;
14980ee5cbfSDavid du Colombier 	return c;
15080ee5cbfSDavid du Colombier }
15180ee5cbfSDavid du Colombier 
1529a747e4fSDavid du Colombier static void
1539a747e4fSDavid du Colombier controlsetthread(void *v)
1549a747e4fSDavid du Colombier {
1559a747e4fSDavid du Colombier 	Controlset *cs;
1569a747e4fSDavid du Colombier 	Mouse mouse;
1579a747e4fSDavid du Colombier 	Control *f;
1589a747e4fSDavid du Colombier 	int prevbut, n, i;
1599a747e4fSDavid du Colombier 	Alt alts[NALT+1];
1609a747e4fSDavid du Colombier 	char tmp[64], *str;
1619a747e4fSDavid du Colombier 	Rune buf[2][20], *rp;
1629a747e4fSDavid du Colombier 
1639a747e4fSDavid du Colombier 	cs = v;
1649a747e4fSDavid du Colombier 	snprint(tmp, sizeof tmp, "controlsetthread 0x%p", cs);
1659a747e4fSDavid du Colombier 	threadsetname(tmp);
1669a747e4fSDavid du Colombier 
1679a747e4fSDavid du Colombier 	alts[AKey].c = cs->kbdc;
1689a747e4fSDavid du Colombier 	alts[AKey].v = &rp;
1699a747e4fSDavid du Colombier 	alts[AKey].op = CHANRCV;
1709a747e4fSDavid du Colombier 	alts[AMouse].c = cs->mousec;
1719a747e4fSDavid du Colombier 	alts[AMouse].v = &mouse;
1729a747e4fSDavid du Colombier 	alts[AMouse].op = CHANRCV;
1739a747e4fSDavid du Colombier 	alts[ACtl].c = cs->ctl;
1749a747e4fSDavid du Colombier 	alts[ACtl].v = &str;
1759a747e4fSDavid du Colombier 	alts[ACtl].op = CHANRCV;
1769a747e4fSDavid du Colombier 	alts[AExit].c = cs->csexitc;
1779a747e4fSDavid du Colombier 	alts[AExit].v = nil;
1789a747e4fSDavid du Colombier 	alts[AExit].op = CHANRCV;
1799a747e4fSDavid du Colombier 	alts[NALT].op = CHANEND;
1809a747e4fSDavid du Colombier 
1819a747e4fSDavid du Colombier 	cs->focus = nil;
1829a747e4fSDavid du Colombier 	prevbut=0;
1839a747e4fSDavid du Colombier 	n = 0;
1849a747e4fSDavid du Colombier 	for(;;){
1859a747e4fSDavid du Colombier 		/* toggle so we can receive in one buffer while client processes the other */
1869a747e4fSDavid du Colombier 		alts[AKey].v = buf[n];
1879a747e4fSDavid du Colombier 		rp = buf[n];
1889a747e4fSDavid du Colombier 		n = 1-n;
1899a747e4fSDavid du Colombier 		switch(alt(alts)){
1909a747e4fSDavid du Colombier 		case AKey:
1919a747e4fSDavid du Colombier 			if(ctldeletequits && rp[0]=='\177')
1929a747e4fSDavid du Colombier 				ctlerror("delete");
1939a747e4fSDavid du Colombier 			for(i=1; i<nelem(buf[0])-1; i++)
1949a747e4fSDavid du Colombier 				if(nbrecv(cs->kbdc, rp+i) <= 0)
1959a747e4fSDavid du Colombier 					break;
1969a747e4fSDavid du Colombier 			rp[i] = L'\0';
1979a747e4fSDavid du Colombier 			if(cs->focus && cs->focus->key)
1989a747e4fSDavid du Colombier 				cs->focus->key(cs->focus, rp);
1999a747e4fSDavid du Colombier 			break;
2009a747e4fSDavid du Colombier 		case AMouse:
2019a747e4fSDavid du Colombier 			/* is this a focus change? */
2029a747e4fSDavid du Colombier 			if(prevbut)	/* don't change focus if button was down */
2039a747e4fSDavid du Colombier 				goto Send;
2049a747e4fSDavid du Colombier 			if(cs->focus!=nil && cs->focus->hidden == 0 && ptinrect(mouse.xy, cs->focus->rect))
2059a747e4fSDavid du Colombier 				goto Send;
2069a747e4fSDavid du Colombier 			if(cs->clicktotype == 0)
2079a747e4fSDavid du Colombier 				goto Change;
2089a747e4fSDavid du Colombier 			/* click to type: only change if button is down */
2099a747e4fSDavid du Colombier 			if(mouse.buttons == 0)
2109a747e4fSDavid du Colombier 				goto Send;
2119a747e4fSDavid du Colombier 		Change:
2129a747e4fSDavid du Colombier 			/* change of focus */
2139a747e4fSDavid du Colombier 			if(cs->focus != nil)
2149a747e4fSDavid du Colombier 				_ctlprint(cs->focus, "focus 0");
2159a747e4fSDavid du Colombier 			cs->focus = nil;
2169a747e4fSDavid du Colombier 			for(f=cs->actives; f!=nil; f=f->nextactive)
2179a747e4fSDavid du Colombier 				if(f->hidden == 0 && f->mouse && ptinrect(mouse.xy, f->rect)){
2189a747e4fSDavid du Colombier 					cs->focus = f;
2199a747e4fSDavid du Colombier 					_ctlprint(f, "focus 1");
2209a747e4fSDavid du Colombier 					if (f->mouse)
2219a747e4fSDavid du Colombier 						f->mouse(f, &mouse);
2229a747e4fSDavid du Colombier 					break;
2239a747e4fSDavid du Colombier 				}
2249a747e4fSDavid du Colombier 		Send:
2259a747e4fSDavid du Colombier 			if(cs->focus && cs->focus->mouse)
2269a747e4fSDavid du Colombier 				cs->focus->mouse(cs->focus, &mouse);
2279a747e4fSDavid du Colombier 			prevbut=mouse.buttons;
2289a747e4fSDavid du Colombier 			break;
2299a747e4fSDavid du Colombier 		case ACtl:
2309a747e4fSDavid du Colombier 			_ctlcontrol(cs, str);
2319a747e4fSDavid du Colombier 			free(str);
2329a747e4fSDavid du Colombier 			break;
2339a747e4fSDavid du Colombier 		case AExit:
2349a747e4fSDavid du Colombier 			threadexits(nil);
2359a747e4fSDavid du Colombier 		}
2369a747e4fSDavid du Colombier 	}
2379a747e4fSDavid du Colombier }
2389a747e4fSDavid du Colombier 
23980ee5cbfSDavid du Colombier Control*
2409a747e4fSDavid du Colombier _createctl(Controlset *cs, char *type, uint size, char *name)
24180ee5cbfSDavid du Colombier {
24280ee5cbfSDavid du Colombier 	Control *c;
24380ee5cbfSDavid du Colombier 
2449a747e4fSDavid du Colombier 	c = _newcontrol(cs, size, name, type);
24580ee5cbfSDavid du Colombier 	if(c == nil)
2469a747e4fSDavid du Colombier 		ctlerror("can't create %s control %q: %r", type, name);
24780ee5cbfSDavid du Colombier 	return c;
24880ee5cbfSDavid du Colombier }
24980ee5cbfSDavid du Colombier 
25080ee5cbfSDavid du Colombier void
25180ee5cbfSDavid du Colombier closecontrol(Control *c)
25280ee5cbfSDavid du Colombier {
25380ee5cbfSDavid du Colombier 	Control *prev, *p;
25480ee5cbfSDavid du Colombier 
25580ee5cbfSDavid du Colombier 	if(c == nil)
25680ee5cbfSDavid du Colombier 		return;
2579a747e4fSDavid du Colombier 	if (c == c->controlset->focus)
2589a747e4fSDavid du Colombier 		c->controlset->focus = nil;
2599a747e4fSDavid du Colombier 	if(c->exit)
2609a747e4fSDavid du Colombier 		c->exit(c);
26180ee5cbfSDavid du Colombier 
26280ee5cbfSDavid du Colombier 	prev = nil;
26380ee5cbfSDavid du Colombier 	for(p=c->controlset->controls; p; p=p->next){
26480ee5cbfSDavid du Colombier 		if(p == c)
26580ee5cbfSDavid du Colombier 			break;
26680ee5cbfSDavid du Colombier 		prev = p;
26780ee5cbfSDavid du Colombier 	}
26880ee5cbfSDavid du Colombier 	if(p == nil)
26980ee5cbfSDavid du Colombier 		ctlerror("closecontrol: no such control %q %p\n", c->name, c);
27080ee5cbfSDavid du Colombier 	if(prev == nil)
27180ee5cbfSDavid du Colombier 		c->controlset->controls = c->next;
27280ee5cbfSDavid du Colombier 	else
27380ee5cbfSDavid du Colombier 		prev->next = c->next;
27480ee5cbfSDavid du Colombier 
27580ee5cbfSDavid du Colombier 	/* is it active? if so, delete from active list */
27680ee5cbfSDavid du Colombier 	prev = nil;
27780ee5cbfSDavid du Colombier 	for(p=c->controlset->actives; p; p=p->nextactive){
27880ee5cbfSDavid du Colombier 		if(p == c)
27980ee5cbfSDavid du Colombier 			break;
28080ee5cbfSDavid du Colombier 		prev = p;
28180ee5cbfSDavid du Colombier 	}
28280ee5cbfSDavid du Colombier 	if(p != nil){
28380ee5cbfSDavid du Colombier 		if(prev == nil)
28480ee5cbfSDavid du Colombier 			c->controlset->actives = c->nextactive;
28580ee5cbfSDavid du Colombier 		else
28680ee5cbfSDavid du Colombier 			prev->nextactive = c->nextactive;
28780ee5cbfSDavid du Colombier 	}
28880ee5cbfSDavid du Colombier 
28980ee5cbfSDavid du Colombier 	if(!c->wevent)
29080ee5cbfSDavid du Colombier 		chanfree(c->event);
29180ee5cbfSDavid du Colombier 	if(!c->wdata)
29280ee5cbfSDavid du Colombier 		chanfree(c->data);
29380ee5cbfSDavid du Colombier 	free(c->name);
29480ee5cbfSDavid du Colombier 	free(c->format);
29580ee5cbfSDavid du Colombier 	free(c);
29680ee5cbfSDavid du Colombier }
29780ee5cbfSDavid du Colombier 
29880ee5cbfSDavid du Colombier Control*
29980ee5cbfSDavid du Colombier controlcalled(char *name)
30080ee5cbfSDavid du Colombier {
30180ee5cbfSDavid du Colombier 	Control *c;
30280ee5cbfSDavid du Colombier 	int i;
30380ee5cbfSDavid du Colombier 
30480ee5cbfSDavid du Colombier 	for(i=0; i<ncontrolset; i++)
30580ee5cbfSDavid du Colombier 		for(c=controlset[i]->controls; c; c=c->next)
30680ee5cbfSDavid du Colombier 			if(strcmp(c->name, name) == 0)
30780ee5cbfSDavid du Colombier 				return c;
30880ee5cbfSDavid du Colombier 	return nil;
30980ee5cbfSDavid du Colombier }
31080ee5cbfSDavid du Colombier 
31180ee5cbfSDavid du Colombier void
31280ee5cbfSDavid du Colombier ctlerror(char *fmt, ...)
31380ee5cbfSDavid du Colombier {
31480ee5cbfSDavid du Colombier 	va_list arg;
31580ee5cbfSDavid du Colombier 	char buf[256];
31680ee5cbfSDavid du Colombier 
31780ee5cbfSDavid du Colombier 	va_start(arg, fmt);
3189a747e4fSDavid du Colombier 	vfprint(2, fmt, arg);
31980ee5cbfSDavid du Colombier 	va_end(arg);
3209a747e4fSDavid du Colombier 	write(2, "\n", 1);
32180ee5cbfSDavid du Colombier 	threadexitsall(buf);
32280ee5cbfSDavid du Colombier }
32380ee5cbfSDavid du Colombier 
32480ee5cbfSDavid du Colombier Rune*
32580ee5cbfSDavid du Colombier _ctlrunestr(char *s)
32680ee5cbfSDavid du Colombier {
32780ee5cbfSDavid du Colombier 	Rune *r, *ret;
32880ee5cbfSDavid du Colombier 
32980ee5cbfSDavid du Colombier 	ret = r = ctlmalloc((utflen(s)+1)*sizeof(Rune));
33080ee5cbfSDavid du Colombier 	while(*s != '\0')
33180ee5cbfSDavid du Colombier 		s += chartorune(r++, s);
33280ee5cbfSDavid du Colombier 	*r = L'\0';
33380ee5cbfSDavid du Colombier 	return ret;
33480ee5cbfSDavid du Colombier }
33580ee5cbfSDavid du Colombier 
33680ee5cbfSDavid du Colombier char*
33780ee5cbfSDavid du Colombier _ctlstrrune(Rune *r)
33880ee5cbfSDavid du Colombier {
33980ee5cbfSDavid du Colombier 	char *s;
34080ee5cbfSDavid du Colombier 	s = ctlmalloc(runestrlen(r)*UTFmax+1);
34180ee5cbfSDavid du Colombier 	sprint(s, "%S", r);
34280ee5cbfSDavid du Colombier 	return s;
34380ee5cbfSDavid du Colombier }
34480ee5cbfSDavid du Colombier 
34580ee5cbfSDavid du Colombier void*
34680ee5cbfSDavid du Colombier ctlmalloc(uint n)
34780ee5cbfSDavid du Colombier {
34880ee5cbfSDavid du Colombier 	void *p;
34980ee5cbfSDavid du Colombier 
35080ee5cbfSDavid du Colombier 	p = mallocz(n, 1);
35180ee5cbfSDavid du Colombier 	if(p == nil)
35280ee5cbfSDavid du Colombier 		ctlerror("control allocation failed: %r");
35380ee5cbfSDavid du Colombier 	return p;
35480ee5cbfSDavid du Colombier }
35580ee5cbfSDavid du Colombier 
35680ee5cbfSDavid du Colombier void*
35780ee5cbfSDavid du Colombier ctlrealloc(void *p, uint n)
35880ee5cbfSDavid du Colombier {
35980ee5cbfSDavid du Colombier 	p = realloc(p, n);
36080ee5cbfSDavid du Colombier 	if(p == nil)
36180ee5cbfSDavid du Colombier 		ctlerror("control reallocation failed: %r");
36280ee5cbfSDavid du Colombier 	return p;
36380ee5cbfSDavid du Colombier }
36480ee5cbfSDavid du Colombier 
36580ee5cbfSDavid du Colombier char*
36680ee5cbfSDavid du Colombier ctlstrdup(char *s)
36780ee5cbfSDavid du Colombier {
36880ee5cbfSDavid du Colombier 	char *t;
36980ee5cbfSDavid du Colombier 
37080ee5cbfSDavid du Colombier 	t = strdup(s);
37180ee5cbfSDavid du Colombier 	if(t == nil)
37280ee5cbfSDavid du Colombier 		ctlerror("control strdup(%q) failed: %r", s);
37380ee5cbfSDavid du Colombier 	return t;
37480ee5cbfSDavid du Colombier }
37580ee5cbfSDavid du Colombier 
37680ee5cbfSDavid du Colombier static void
37780ee5cbfSDavid du Colombier ctokenize(char *s, CParse *cp)
37880ee5cbfSDavid du Colombier {
37980ee5cbfSDavid du Colombier 	snprint(cp->str, sizeof cp->str, "%s", s);
3809a747e4fSDavid du Colombier 	cp->args = cp->pargs;
3819a747e4fSDavid du Colombier 	cp->nargs = tokenize(s, cp->args, nelem(cp->pargs));
38280ee5cbfSDavid du Colombier }
38380ee5cbfSDavid du Colombier 
384*ac57dd0bSDavid du Colombier static int
3859a747e4fSDavid du Colombier ctlparse(CParse *cp, char *s, int hasreceiver)
38680ee5cbfSDavid du Colombier {
3879a747e4fSDavid du Colombier 	int i;
38880ee5cbfSDavid du Colombier 	char *t;
38980ee5cbfSDavid du Colombier 
39080ee5cbfSDavid du Colombier 	/* keep original string for good error messages */
3919a747e4fSDavid du Colombier 	strncpy(cp->str, s, sizeof cp->str);
3929a747e4fSDavid du Colombier 	cp->str[sizeof cp->str - 1] = '\0';
39380ee5cbfSDavid du Colombier 	ctokenize(s, cp);
39480ee5cbfSDavid du Colombier 	if(cp->nargs == 0)
39580ee5cbfSDavid du Colombier 		return -1;
39680ee5cbfSDavid du Colombier 	/* strip leading sender name if present */
39780ee5cbfSDavid du Colombier 	cp->sender = nil;
39880ee5cbfSDavid du Colombier 	i = strlen(cp->args[0])-1;
39980ee5cbfSDavid du Colombier 	if(cp->args[0][i] == ':'){
40080ee5cbfSDavid du Colombier 		cp->sender = cp->args[0];
40180ee5cbfSDavid du Colombier 		cp->sender[i] = '\0';
4029a747e4fSDavid du Colombier 		cp->args++;
40380ee5cbfSDavid du Colombier 		cp->nargs--;
40480ee5cbfSDavid du Colombier 	}
4059a747e4fSDavid du Colombier 	if(hasreceiver){
4069a747e4fSDavid du Colombier 		if(cp->nargs-- == 0)
4079a747e4fSDavid du Colombier 			return -1;
4089a747e4fSDavid du Colombier 		cp->receiver = *cp->args++;
4099a747e4fSDavid du Colombier 	}else
4109a747e4fSDavid du Colombier 		cp->receiver = nil;
4119a747e4fSDavid du Colombier 	for(i=0; i<cp->nargs; i++){
41280ee5cbfSDavid du Colombier 		t = cp->args[i];
41380ee5cbfSDavid du Colombier 		while(*t == '[')	/* %R gives [0 0] [1 1]; atoi will stop at closing ] */
41480ee5cbfSDavid du Colombier 			t++;
41580ee5cbfSDavid du Colombier 		cp->iargs[i] = atoi(t);
41680ee5cbfSDavid du Colombier 	}
4179a747e4fSDavid du Colombier 	return cp->nargs;
41880ee5cbfSDavid du Colombier }
41980ee5cbfSDavid du Colombier 
42080ee5cbfSDavid du Colombier void
42180ee5cbfSDavid du Colombier _ctlargcount(Control *c, CParse *cp, int n)
42280ee5cbfSDavid du Colombier {
42380ee5cbfSDavid du Colombier 	if(cp->nargs != n)
42480ee5cbfSDavid du Colombier 		ctlerror("%q: wrong argument count in '%s'", c->name, cp->str);
42580ee5cbfSDavid du Colombier }
42680ee5cbfSDavid du Colombier 
4279a747e4fSDavid du Colombier static void
4289a747e4fSDavid du Colombier _ctlcmd(Controlset *cs, char*s)
4299a747e4fSDavid du Colombier {
4309a747e4fSDavid du Colombier 	CParse cp;
4319a747e4fSDavid du Colombier 	char	*rcvrs[32];
4329a747e4fSDavid du Colombier 	int	ircvrs[32], n, i, hit;
4339a747e4fSDavid du Colombier 	Control *c;
4349a747e4fSDavid du Colombier 
4359a747e4fSDavid du Colombier //	fprint(2, "_ctlcmd: %s\n", s);
4369a747e4fSDavid du Colombier 	cp.args = cp.pargs;
4379a747e4fSDavid du Colombier 	if (ctlparse(&cp, s, 1) < 0)
4389a747e4fSDavid du Colombier 		ctlerror("bad command string: %q", cp.str);
4399a747e4fSDavid du Colombier 	if (cp.nargs == 0 && strcmp(cp.receiver, "sync") == 0){
4409a747e4fSDavid du Colombier 		chanprint(cs->data, "sync");
4419a747e4fSDavid du Colombier 		return;
4429a747e4fSDavid du Colombier 	}
4439a747e4fSDavid du Colombier 	if (cp.nargs == 0)
4449a747e4fSDavid du Colombier 		ctlerror("no command in command string: %q", cp.str);
4459a747e4fSDavid du Colombier 
4469a747e4fSDavid du Colombier 	n = tokenize(cp.receiver, rcvrs, nelem(rcvrs));
4479a747e4fSDavid du Colombier 
4489a747e4fSDavid du Colombier 	// lookup type names: a receiver can be a named type or a named control
4499a747e4fSDavid du Colombier 	for (i = 0; i < n; i++)
4509a747e4fSDavid du Colombier 		ircvrs[i] = _ctllookup(rcvrs[i], ctltypenames, Ntypes);
4519a747e4fSDavid du Colombier 
4529a747e4fSDavid du Colombier 	for(c = cs->controls; c != nil; c = c->next){
4539a747e4fSDavid du Colombier 		/* if a control matches on more than one receiver element,
4549a747e4fSDavid du Colombier 		 * make sure it gets processed once; hence loop through controls
4559a747e4fSDavid du Colombier 		 * in the outer loop
4569a747e4fSDavid du Colombier 		 */
4579a747e4fSDavid du Colombier 		hit = 0;
4589a747e4fSDavid du Colombier 		for (i = 0; i < n; i++)
4599a747e4fSDavid du Colombier 			if(strcmp(c->name, rcvrs[i]) == 0 || c->type == ircvrs[i])
4609a747e4fSDavid du Colombier 				hit++;
4619a747e4fSDavid du Colombier 		if (hit && c->ctl)
4629a747e4fSDavid du Colombier 			c->ctl(c, &cp);
4639a747e4fSDavid du Colombier 	}
4649a747e4fSDavid du Colombier }
4659a747e4fSDavid du Colombier 
4669a747e4fSDavid du Colombier static void
4679a747e4fSDavid du Colombier _ctlcontrol(Controlset *cs, char *s)
46880ee5cbfSDavid du Colombier {
46980ee5cbfSDavid du Colombier 	char *lines[16];
47080ee5cbfSDavid du Colombier 	int i, n;
47180ee5cbfSDavid du Colombier 	char *l;
47280ee5cbfSDavid du Colombier 
4739a747e4fSDavid du Colombier //	fprint(2, "_ctlcontrol: %s\n", s);
4749a747e4fSDavid du Colombier 	n = gettokens(s, lines, nelem(lines), "\n");
47580ee5cbfSDavid du Colombier 	for(i=0; i<n; i++){
47680ee5cbfSDavid du Colombier 		l = lines[i];
47780ee5cbfSDavid du Colombier 		while(*l==' ' || *l=='\t')
47880ee5cbfSDavid du Colombier 			l++;
47980ee5cbfSDavid du Colombier 		if(*l != '\0')
4809a747e4fSDavid du Colombier 			_ctlcmd(cs, l);
48180ee5cbfSDavid du Colombier 	}
48280ee5cbfSDavid du Colombier }
48380ee5cbfSDavid du Colombier 
48480ee5cbfSDavid du Colombier Rune*
48580ee5cbfSDavid du Colombier _ctlgetsnarf(void)
48680ee5cbfSDavid du Colombier {
48780ee5cbfSDavid du Colombier 	int i, n;
48880ee5cbfSDavid du Colombier 	char *sn, buf[512];
48980ee5cbfSDavid du Colombier 	Rune *snarf;
49080ee5cbfSDavid du Colombier 
49180ee5cbfSDavid du Colombier 	if(_ctlsnarffd < 0)
49280ee5cbfSDavid du Colombier 		return nil;
49380ee5cbfSDavid du Colombier 	sn = nil;
49480ee5cbfSDavid du Colombier 	i = 0;
49580ee5cbfSDavid du Colombier 	seek(_ctlsnarffd, 0, 0);
49680ee5cbfSDavid du Colombier 	while((n = read(_ctlsnarffd, buf, sizeof buf)) > 0){
49780ee5cbfSDavid du Colombier 		sn = ctlrealloc(sn, i+n+1);
49880ee5cbfSDavid du Colombier 		memmove(sn+i, buf, n);
49980ee5cbfSDavid du Colombier 		i += n;
50080ee5cbfSDavid du Colombier 		sn[i] = 0;
50180ee5cbfSDavid du Colombier 	}
50280ee5cbfSDavid du Colombier 	snarf = nil;
50380ee5cbfSDavid du Colombier 	if(i > 0){
50480ee5cbfSDavid du Colombier 		snarf = _ctlrunestr(sn);
50580ee5cbfSDavid du Colombier 		free(sn);
50680ee5cbfSDavid du Colombier 	}
50780ee5cbfSDavid du Colombier 	return snarf;
50880ee5cbfSDavid du Colombier }
50980ee5cbfSDavid du Colombier 
51080ee5cbfSDavid du Colombier void
51180ee5cbfSDavid du Colombier _ctlputsnarf(Rune *snarf)
51280ee5cbfSDavid du Colombier {
51380ee5cbfSDavid du Colombier 	int fd, i, n, nsnarf;
51480ee5cbfSDavid du Colombier 
51580ee5cbfSDavid du Colombier 	if(_ctlsnarffd<0 || snarf[0]==0)
51680ee5cbfSDavid du Colombier 		return;
51780ee5cbfSDavid du Colombier 	fd = open("/dev/snarf", OWRITE);
51880ee5cbfSDavid du Colombier 	if(fd < 0)
51980ee5cbfSDavid du Colombier 		return;
52080ee5cbfSDavid du Colombier 	nsnarf = runestrlen(snarf);
52180ee5cbfSDavid du Colombier 	/* snarf buffer could be huge, so fprint will truncate; do it in blocks */
52280ee5cbfSDavid du Colombier 	for(i=0; i<nsnarf; i+=n){
52380ee5cbfSDavid du Colombier 		n = nsnarf-i;
52480ee5cbfSDavid du Colombier 		if(n >= 256)
52580ee5cbfSDavid du Colombier 			n = 256;
5269a747e4fSDavid du Colombier 		if(fprint(fd, "%.*S", n, snarf+i) < 0)
52780ee5cbfSDavid du Colombier 			break;
52880ee5cbfSDavid du Colombier 	}
52980ee5cbfSDavid du Colombier 	close(fd);
53080ee5cbfSDavid du Colombier }
53180ee5cbfSDavid du Colombier 
53280ee5cbfSDavid du Colombier int
53380ee5cbfSDavid du Colombier _ctlalignment(char *s)
53480ee5cbfSDavid du Colombier {
53580ee5cbfSDavid du Colombier 	int i;
53680ee5cbfSDavid du Colombier 
5379a747e4fSDavid du Colombier 	i = _ctllookup(s, alignnames, Nalignments);
5389a747e4fSDavid du Colombier 	if (i < 0)
53980ee5cbfSDavid du Colombier 		ctlerror("unknown alignment: %s", s);
5409a747e4fSDavid du Colombier 	return i;
54180ee5cbfSDavid du Colombier }
54280ee5cbfSDavid du Colombier 
54380ee5cbfSDavid du Colombier Point
54480ee5cbfSDavid du Colombier _ctlalignpoint(Rectangle r, int dx, int dy, int align)
54580ee5cbfSDavid du Colombier {
54680ee5cbfSDavid du Colombier 	Point p;
54780ee5cbfSDavid du Colombier 
54880ee5cbfSDavid du Colombier 	p = r.min;	/* in case of trouble */
54980ee5cbfSDavid du Colombier 	switch(align%3){
55080ee5cbfSDavid du Colombier 	case 0:	/* left */
55180ee5cbfSDavid du Colombier 		p.x = r.min.x;
55280ee5cbfSDavid du Colombier 		break;
55380ee5cbfSDavid du Colombier 	case 1:	/* center */
55480ee5cbfSDavid du Colombier 		p.x = r.min.x+(Dx(r)-dx)/2;
55580ee5cbfSDavid du Colombier 		break;
55680ee5cbfSDavid du Colombier 	case 2:	/* right */
55780ee5cbfSDavid du Colombier 		p.x = r.max.x-dx;
55880ee5cbfSDavid du Colombier 		break;
55980ee5cbfSDavid du Colombier 	}
56080ee5cbfSDavid du Colombier 	switch((align/3)%3){
56180ee5cbfSDavid du Colombier 	case 0:	/* top */
56280ee5cbfSDavid du Colombier 		p.y = r.min.y;
56380ee5cbfSDavid du Colombier 		break;
56480ee5cbfSDavid du Colombier 	case 1:	/* center */
56580ee5cbfSDavid du Colombier 		p.y = r.min.y+(Dy(r)-dy)/2;
56680ee5cbfSDavid du Colombier 		break;
56780ee5cbfSDavid du Colombier 	case 2:	/* bottom */
56880ee5cbfSDavid du Colombier 		p.y = r.max.y - dy;
56980ee5cbfSDavid du Colombier 		break;
57080ee5cbfSDavid du Colombier 	}
57180ee5cbfSDavid du Colombier 	return p;
57280ee5cbfSDavid du Colombier }
57380ee5cbfSDavid du Colombier 
57480ee5cbfSDavid du Colombier void
5759a747e4fSDavid du Colombier controlwire(Control *cfrom, char *name, Channel *chan)
57680ee5cbfSDavid du Colombier {
57780ee5cbfSDavid du Colombier 	Channel **p;
57880ee5cbfSDavid du Colombier 
57980ee5cbfSDavid du Colombier 	p = nil;
5809a747e4fSDavid du Colombier 	if(strcmp(name, "event") == 0){
5819a747e4fSDavid du Colombier 		p = &cfrom->event;
5829a747e4fSDavid du Colombier 		cfrom->wevent = 1;
5839a747e4fSDavid du Colombier 	}else if(strcmp(name, "data") == 0){
5849a747e4fSDavid du Colombier 		p = &cfrom->data;
5859a747e4fSDavid du Colombier 		cfrom->wdata = 1;
58680ee5cbfSDavid du Colombier 	}else
5879a747e4fSDavid du Colombier 		ctlerror("%q: unknown controlwire channel %s", cfrom->name, name);
58880ee5cbfSDavid du Colombier 	chanfree(*p);
5899a747e4fSDavid du Colombier 	*p = chan;
59080ee5cbfSDavid du Colombier }
59180ee5cbfSDavid du Colombier 
59280ee5cbfSDavid du Colombier void
5939a747e4fSDavid du Colombier _ctlfocus(Control *me, int set)
59480ee5cbfSDavid du Colombier {
59580ee5cbfSDavid du Colombier 	Controlset *cs;
59680ee5cbfSDavid du Colombier 
5979a747e4fSDavid du Colombier 	cs = me->controlset;
5989a747e4fSDavid du Colombier 	if(set){
5999a747e4fSDavid du Colombier 		if(cs->focus == me)
60080ee5cbfSDavid du Colombier 			return;
6019a747e4fSDavid du Colombier 		if(cs->focus != nil)
6029a747e4fSDavid du Colombier 			_ctlprint(cs->focus, "focus 0");
6039a747e4fSDavid du Colombier 		cs->focus = me;
6049a747e4fSDavid du Colombier 	}else{
6059a747e4fSDavid du Colombier 		if(cs->focus != me)
6069a747e4fSDavid du Colombier 			return;
6079a747e4fSDavid du Colombier 		cs->focus = nil;
60880ee5cbfSDavid du Colombier 	}
60980ee5cbfSDavid du Colombier }
61080ee5cbfSDavid du Colombier 
61180ee5cbfSDavid du Colombier static void
61280ee5cbfSDavid du Colombier resizethread(void *v)
61380ee5cbfSDavid du Colombier {
61480ee5cbfSDavid du Colombier 	Controlset *cs;
61580ee5cbfSDavid du Colombier 	char buf[64];
61680ee5cbfSDavid du Colombier 	Alt alts[3];
61780ee5cbfSDavid du Colombier 
61880ee5cbfSDavid du Colombier 	cs = v;
61980ee5cbfSDavid du Colombier 	snprint(buf, sizeof buf, "resizethread0x%p", cs);
62080ee5cbfSDavid du Colombier 	threadsetname(buf);
62180ee5cbfSDavid du Colombier 
62280ee5cbfSDavid du Colombier 	alts[0].c = cs->resizec;
62380ee5cbfSDavid du Colombier 	alts[0].v = nil;
62480ee5cbfSDavid du Colombier 	alts[0].op = CHANRCV;
62580ee5cbfSDavid du Colombier 	alts[1].c = cs->resizeexitc;
62680ee5cbfSDavid du Colombier 	alts[1].v = nil;
62780ee5cbfSDavid du Colombier 	alts[1].op = CHANRCV;
62880ee5cbfSDavid du Colombier 	alts[2].op = CHANEND;
62980ee5cbfSDavid du Colombier 
63080ee5cbfSDavid du Colombier 	for(;;){
63180ee5cbfSDavid du Colombier 		switch(alt(alts)){
63280ee5cbfSDavid du Colombier 		case 0:
63380ee5cbfSDavid du Colombier 			resizecontrolset(cs);
63480ee5cbfSDavid du Colombier 			break;
63580ee5cbfSDavid du Colombier 		case 1:
63680ee5cbfSDavid du Colombier 			return;
63780ee5cbfSDavid du Colombier 		}
63880ee5cbfSDavid du Colombier 	}
63980ee5cbfSDavid du Colombier }
64080ee5cbfSDavid du Colombier 
64180ee5cbfSDavid du Colombier void
64280ee5cbfSDavid du Colombier activate(Control *a)
64380ee5cbfSDavid du Colombier {
64480ee5cbfSDavid du Colombier 	Control *c;
64580ee5cbfSDavid du Colombier 
64680ee5cbfSDavid du Colombier 	for(c=a->controlset->actives; c; c=c->nextactive)
64780ee5cbfSDavid du Colombier 		if(c == a)
64880ee5cbfSDavid du Colombier 			ctlerror("%q already active\n", a->name);
6499a747e4fSDavid du Colombier 
6509a747e4fSDavid du Colombier 	if (a->activate){
6519a747e4fSDavid du Colombier 		a->activate(a, 1);
6529a747e4fSDavid du Colombier 		return;
6539a747e4fSDavid du Colombier 	}
6549a747e4fSDavid du Colombier 	/* prepend */
65580ee5cbfSDavid du Colombier 	a->nextactive = a->controlset->actives;
65680ee5cbfSDavid du Colombier 	a->controlset->actives = a;
65780ee5cbfSDavid du Colombier }
65880ee5cbfSDavid du Colombier 
65980ee5cbfSDavid du Colombier void
66080ee5cbfSDavid du Colombier deactivate(Control *a)
66180ee5cbfSDavid du Colombier {
66280ee5cbfSDavid du Colombier 	Control *c, *prev;
66380ee5cbfSDavid du Colombier 
6649a747e4fSDavid du Colombier 	/* if group, first deactivate kids, then self */
6659a747e4fSDavid du Colombier 	if (a->activate){
6669a747e4fSDavid du Colombier 		a->activate(a, 0);
6679a747e4fSDavid du Colombier 		return;
6689a747e4fSDavid du Colombier 	}
66980ee5cbfSDavid du Colombier 	prev = nil;
67080ee5cbfSDavid du Colombier 	for(c=a->controlset->actives; c; c=c->nextactive){
67180ee5cbfSDavid du Colombier 		if(c == a){
67280ee5cbfSDavid du Colombier 			if(a->controlset->focus == a)
67380ee5cbfSDavid du Colombier 				a->controlset->focus = nil;
67480ee5cbfSDavid du Colombier 			if(prev != nil)
67580ee5cbfSDavid du Colombier 				prev->nextactive = a->nextactive;
67680ee5cbfSDavid du Colombier 			else
67780ee5cbfSDavid du Colombier 				a->controlset->actives = a->nextactive;
67880ee5cbfSDavid du Colombier 			return;
67980ee5cbfSDavid du Colombier 		}
68080ee5cbfSDavid du Colombier 		prev = c;
68180ee5cbfSDavid du Colombier 	}
68280ee5cbfSDavid du Colombier 	ctlerror("%q not active\n", a->name);
68380ee5cbfSDavid du Colombier }
68480ee5cbfSDavid du Colombier 
68580ee5cbfSDavid du Colombier static struct
68680ee5cbfSDavid du Colombier {
68780ee5cbfSDavid du Colombier 	char	*name;
68880ee5cbfSDavid du Colombier 	ulong	color;
68980ee5cbfSDavid du Colombier }coltab[] = {
69080ee5cbfSDavid du Colombier 	"red",			DRed,
69180ee5cbfSDavid du Colombier 	"green",			DGreen,
69280ee5cbfSDavid du Colombier 	"blue",			DBlue,
69380ee5cbfSDavid du Colombier 	"cyan",			DCyan,
69480ee5cbfSDavid du Colombier 	"magenta",		DMagenta,
69580ee5cbfSDavid du Colombier 	"yellow",			DYellow,
69680ee5cbfSDavid du Colombier 	"paleyellow",		DPaleyellow,
69780ee5cbfSDavid du Colombier 	"darkyellow",		DDarkyellow,
69880ee5cbfSDavid du Colombier 	"darkgreen",		DDarkgreen,
69980ee5cbfSDavid du Colombier 	"palegreen",		DPalegreen,
70080ee5cbfSDavid du Colombier 	"medgreen",		DMedgreen,
70180ee5cbfSDavid du Colombier 	"darkblue",		DDarkblue,
70280ee5cbfSDavid du Colombier 	"palebluegreen",	DPalebluegreen,
70380ee5cbfSDavid du Colombier 	"paleblue",		DPaleblue,
70480ee5cbfSDavid du Colombier 	"bluegreen",		DBluegreen,
70580ee5cbfSDavid du Colombier 	"greygreen",		DGreygreen,
70680ee5cbfSDavid du Colombier 	"palegreygreen",	DPalegreygreen,
70780ee5cbfSDavid du Colombier 	"yellowgreen",		DYellowgreen,
70880ee5cbfSDavid du Colombier 	"medblue",		DMedblue,
70980ee5cbfSDavid du Colombier 	"greyblue",		DGreyblue,
71080ee5cbfSDavid du Colombier 	"palegreyblue",		DPalegreyblue,
71180ee5cbfSDavid du Colombier 	"purpleblue",		DPurpleblue,
71280ee5cbfSDavid du Colombier 	nil,	0
71380ee5cbfSDavid du Colombier };
71480ee5cbfSDavid du Colombier 
71580ee5cbfSDavid du Colombier void
71680ee5cbfSDavid du Colombier initcontrols(void)
71780ee5cbfSDavid du Colombier {
71880ee5cbfSDavid du Colombier 	int i;
71980ee5cbfSDavid du Colombier 	Image *im;
72080ee5cbfSDavid du Colombier 
72180ee5cbfSDavid du Colombier 	quotefmtinstall();
72280ee5cbfSDavid du Colombier 	namectlimage(display->opaque, "opaque");
72380ee5cbfSDavid du Colombier 	namectlimage(display->transparent, "transparent");
72480ee5cbfSDavid du Colombier 	namectlimage(display->white, "white");
72580ee5cbfSDavid du Colombier 	namectlimage(display->black, "black");
72680ee5cbfSDavid du Colombier 	for(i=0; coltab[i].name!=nil; i++){
72780ee5cbfSDavid du Colombier 		im = allocimage(display, Rect(0,0,1,1), RGB24, 1, coltab[i].color);
72880ee5cbfSDavid du Colombier 		namectlimage(im, coltab[i].name);
72980ee5cbfSDavid du Colombier 	}
73080ee5cbfSDavid du Colombier 	namectlfont(font, "font");
73180ee5cbfSDavid du Colombier 	_ctlsnarffd = open("/dev/snarf", OREAD);
73280ee5cbfSDavid du Colombier }
73380ee5cbfSDavid du Colombier 
73480ee5cbfSDavid du Colombier Controlset*
73580ee5cbfSDavid du Colombier newcontrolset(Image *im, Channel *kbdc, Channel *mousec, Channel *resizec)
73680ee5cbfSDavid du Colombier {
73780ee5cbfSDavid du Colombier 	Controlset *cs;
73880ee5cbfSDavid du Colombier 
73980ee5cbfSDavid du Colombier 	if(im == nil)
74080ee5cbfSDavid du Colombier 		im = screen;
74180ee5cbfSDavid du Colombier 	if((mousec==nil && resizec!=nil) || (mousec!=nil && resizec==nil))
74280ee5cbfSDavid du Colombier 		ctlerror("must specify either or both of mouse and resize channels");
74380ee5cbfSDavid du Colombier 
74480ee5cbfSDavid du Colombier 	cs = ctlmalloc(sizeof(Controlset));
74580ee5cbfSDavid du Colombier 	cs->screen = im;
74680ee5cbfSDavid du Colombier 
74780ee5cbfSDavid du Colombier 	if(kbdc == nil){
74880ee5cbfSDavid du Colombier 		cs->keyboardctl = initkeyboard(nil);
74980ee5cbfSDavid du Colombier 		if(cs->keyboardctl == nil)
75080ee5cbfSDavid du Colombier 			ctlerror("can't initialize keyboard: %r");
75180ee5cbfSDavid du Colombier 		kbdc = cs->keyboardctl->c;
75280ee5cbfSDavid du Colombier 	}
75380ee5cbfSDavid du Colombier 	cs ->kbdc = kbdc;
75480ee5cbfSDavid du Colombier 
75580ee5cbfSDavid du Colombier 	if(mousec == nil){
75680ee5cbfSDavid du Colombier 		cs->mousectl = initmouse(nil, im);
75780ee5cbfSDavid du Colombier 		if(cs->mousectl == nil)
75880ee5cbfSDavid du Colombier 			ctlerror("can't initialize mouse: %r");
75980ee5cbfSDavid du Colombier 		mousec = cs->mousectl->c;
76080ee5cbfSDavid du Colombier 		resizec = cs->mousectl->resizec;
76180ee5cbfSDavid du Colombier 	}
76280ee5cbfSDavid du Colombier 	cs->mousec = mousec;
76380ee5cbfSDavid du Colombier 	cs->resizec = resizec;
764b7b24591SDavid du Colombier 	cs->ctl = chancreate(sizeof(char*), 64);	/* buffer to prevent deadlock */
7659a747e4fSDavid du Colombier 	cs->data = chancreate(sizeof(char*), 0);
76680ee5cbfSDavid du Colombier 	cs->resizeexitc = chancreate(sizeof(int), 0);
7679a747e4fSDavid du Colombier 	cs->csexitc = chancreate(sizeof(int), 0);
76880ee5cbfSDavid du Colombier 
7699a747e4fSDavid du Colombier 	threadcreate(resizethread, cs, 32*1024);
7709a747e4fSDavid du Colombier 	threadcreate(controlsetthread, cs, 32*1024);
77180ee5cbfSDavid du Colombier 
77280ee5cbfSDavid du Colombier 	controlset = ctlrealloc(controlset, (ncontrolset+1)*sizeof(Controlset*));
77380ee5cbfSDavid du Colombier 	controlset[ncontrolset++] = cs;
77480ee5cbfSDavid du Colombier 	return cs;
77580ee5cbfSDavid du Colombier }
77680ee5cbfSDavid du Colombier 
77780ee5cbfSDavid du Colombier void
77880ee5cbfSDavid du Colombier closecontrolset(Controlset *cs)
77980ee5cbfSDavid du Colombier {
78080ee5cbfSDavid du Colombier 	int i;
78180ee5cbfSDavid du Colombier 
7829a747e4fSDavid du Colombier 	sendul(cs->resizeexitc, 0);
7839a747e4fSDavid du Colombier 	chanfree(cs->resizeexitc);
7849a747e4fSDavid du Colombier 	sendul(cs->csexitc, 0);
7859a747e4fSDavid du Colombier 	chanfree(cs->csexitc);
7869a747e4fSDavid du Colombier 	chanfree(cs->ctl);
7879a747e4fSDavid du Colombier 	chanfree(cs->data);
7889a747e4fSDavid du Colombier 
78980ee5cbfSDavid du Colombier 	for(i=0; i<ncontrolset; i++)
79080ee5cbfSDavid du Colombier 		if(cs == controlset[i]){
79180ee5cbfSDavid du Colombier 			memmove(controlset+i, controlset+i+1, (ncontrolset-(i+1))*sizeof(Controlset*));
79280ee5cbfSDavid du Colombier 			ncontrolset--;
79380ee5cbfSDavid du Colombier 			goto Found;
79480ee5cbfSDavid du Colombier 		}
79580ee5cbfSDavid du Colombier 
79680ee5cbfSDavid du Colombier 	if(i == ncontrolset)
79780ee5cbfSDavid du Colombier 		ctlerror("closecontrolset: control set not found");
79880ee5cbfSDavid du Colombier 
79980ee5cbfSDavid du Colombier     Found:
80080ee5cbfSDavid du Colombier 	while(cs->controls != nil)
80180ee5cbfSDavid du Colombier 		closecontrol(cs->controls);
80280ee5cbfSDavid du Colombier }
803