xref: /plan9/sys/src/libcontrol/control.c (revision 6f314b92956e16637888967d7478acfbd2ac8dab)
1 #include <u.h>
2 #include <libc.h>
3 #include <draw.h>
4 #include <thread.h>
5 #include <mouse.h>
6 #include <keyboard.h>
7 #include <control.h>
8 
9 static int debug = 0;
10 
11 enum	/* alts */
12 {
13 	AKey,
14 	AMouse,
15 	ACtl,
16 	AExit,
17 	NALT
18 };
19 
20 static Controlset **controlset;
21 int	ncontrolset;
22 int	ctldeletequits;
23 
24 char *alignnames[Nalignments] = {
25 	[Aupperleft] =		"upperleft",
26 	[Auppercenter] =	"uppercenter",
27 	[Aupperright] =		"upperright",
28 	[Acenterleft] =		"centerleft",
29 	[Acenter] =		"center",
30 	[Acenterright] =	"centerright",
31 	[Alowerleft] =		"lowerleft",
32 	[Alowercenter] =	"lowercenter",
33 	[Alowerright] =		"lowerright",
34 };
35 
36 char *ctltypenames[Ntypes] = {
37 	[Ctlunknown] =		"unknown",
38 	[Ctlbox] =			"box",
39 	[Ctlbutton] =		"button",
40 	[Ctlentry] =		"entry",
41 	[Ctlkeyboard] =		"keyboard",
42 	[Ctllabel] =		"label",
43 	[Ctlmenu] =		"menu",
44 	[Ctlradio] =		"radio",
45 	[Ctlscribble] =		"scribble",
46 	[Ctlslider] =		"slider",
47 	[Ctltabs] =			"tabs",
48 	[Ctltext] =			"text",
49 	[Ctltextbutton] =	"textbutton",
50 	[Ctltextbutton3] =	"textbutton3",
51 	[Ctlgroup] =		"group",		// divider between controls and metacontrols
52 	[Ctlboxbox] =		"boxbox",
53 	[Ctlcolumn] =		"column",
54 	[Ctlrow] =			"row",
55 	[Ctlstack] =		"stack",
56 	[Ctltab] =			"tab",
57 };
58 
59 static void	_ctlcmd(Controlset*, char*);
60 static void	_ctlcontrol(Controlset*, char*);
61 
62 static char*
_mkctlcmd(Control * c,char * fmt,va_list arg)63 _mkctlcmd(Control *c, char *fmt, va_list arg)
64 {
65 	char *name, *p, *both;
66 
67 	name = quotestrdup(c->name);
68 	if(name == nil)
69 		ctlerror("quotestrdup in ctlprint failed");
70 	p = vsmprint(fmt, arg);
71 	if(p == nil){
72 		free(name);
73 		ctlerror("vsmprint1 in ctlprint failed");
74 	}
75 	both = ctlmalloc(strlen(name)+strlen(p)+2);
76 	strcpy(both, name);
77 	strcat(both, " ");
78 	strcat(both, p);
79 	free(name);
80 	free(p);
81 	return both;
82 }
83 
84 int
ctlprint(Control * c,char * fmt,...)85 ctlprint(Control *c, char *fmt, ...)
86 {
87 	int n;
88 	char *p;
89 	va_list arg;
90 
91 	va_start(arg, fmt);
92 	p = _mkctlcmd(c, fmt, arg);
93 	va_end(arg);
94 	n = sendp(c->controlset->ctl, p);
95 	yield();
96 	return n;
97 }
98 
99 void
_ctlprint(Control * c,char * fmt,...)100 _ctlprint(Control *c, char *fmt, ...)
101 {
102 	char *p;
103 	va_list arg;
104 
105 	va_start(arg, fmt);
106 	p = _mkctlcmd(c, fmt, arg);
107 	va_end(arg);
108 	_ctlcmd(c->controlset, p);
109 	free(p);
110 }
111 
112 int
_ctllookup(char * s,char * tab[],int ntab)113 _ctllookup(char *s, char *tab[], int ntab)
114 {
115 	int i;
116 
117 	for(i=0; i<ntab; i++)
118 		if(tab[i] != nil && strcmp(s, tab[i]) == 0)
119 			return i;
120 	return -1;
121 }
122 
123 static Control*
_newcontrol(Controlset * cs,uint n,char * name,char * type)124 _newcontrol(Controlset *cs, uint n, char *name, char *type)
125 {
126 	Control *c;
127 
128 	for(c=cs->controls; c; c=c->next)
129 		if(strcmp(c->name, name) == 0){
130 			werrstr("control %q already defined", name);
131 			return nil;
132 		}
133 	c = ctlmalloc(n);
134 	c->screen = cs->screen;
135 	c->name = ctlstrdup(name);
136 	c->type = _ctllookup(type, ctltypenames, Ntypes);
137 	if (c->type < 0)
138 		ctlerror("unknown type: %s", type);
139 	c->event = chancreate(sizeof(char*), 64);
140 	c->data = chancreate(sizeof(char*), 0);
141 	c->size = Rect(1, 1, _Ctlmaxsize, _Ctlmaxsize);
142 	c->hidden = 0;
143 	c->ctl = nil;
144 	c->mouse = nil;
145 	c->key = nil;
146 	c->exit = nil;
147 	c->setsize = nil;
148 
149 	c->controlset = cs;
150 	c->next = cs->controls;
151 	cs->controls = c;
152 	return c;
153 }
154 
155 static void
controlsetthread(void * v)156 controlsetthread(void *v)
157 {
158 	Controlset *cs;
159 	Mouse mouse;
160 	Control *f;
161 	int prevbut, n, i;
162 	Alt alts[NALT+1];
163 	char tmp[64], *str;
164 	Rune buf[2][20], *rp;
165 
166 	cs = v;
167 	snprint(tmp, sizeof tmp, "controlsetthread 0x%p", cs);
168 	threadsetname(tmp);
169 
170 	alts[AKey].c = cs->kbdc;
171 	alts[AKey].v = &rp;
172 	alts[AKey].op = CHANRCV;
173 	alts[AMouse].c = cs->mousec;
174 	alts[AMouse].v = &mouse;
175 	alts[AMouse].op = CHANRCV;
176 	alts[ACtl].c = cs->ctl;
177 	alts[ACtl].v = &str;
178 	alts[ACtl].op = CHANRCV;
179 	alts[AExit].c = cs->csexitc;
180 	alts[AExit].v = nil;
181 	alts[AExit].op = CHANRCV;
182 	alts[NALT].op = CHANEND;
183 
184 	cs->focus = nil;
185 	prevbut=0;
186 	n = 0;
187 	for(;;){
188 		/* toggle so we can receive in one buffer while client processes the other */
189 		alts[AKey].v = buf[n];
190 		rp = buf[n];
191 		n = 1-n;
192 		switch(alt(alts)){
193 		case AKey:
194 			if(ctldeletequits && rp[0]=='\177')
195 				ctlerror("delete");
196 			for(i=1; i<nelem(buf[0])-1; i++)
197 				if(nbrecv(cs->kbdc, rp+i) <= 0)
198 					break;
199 			rp[i] = L'\0';
200 			if(cs->focus && cs->focus->key)
201 				cs->focus->key(cs->focus, rp);
202 			break;
203 		case AMouse:
204 			/* is this a focus change? */
205 			if(prevbut)	/* don't change focus if button was down */
206 				goto Send;
207 			if(cs->focus!=nil && cs->focus->hidden == 0 && ptinrect(mouse.xy, cs->focus->rect))
208 				goto Send;
209 			if(cs->clicktotype == 0)
210 				goto Change;
211 			/* click to type: only change if button is down */
212 			if(mouse.buttons == 0)
213 				goto Send;
214 		Change:
215 			/* change of focus */
216 			if(cs->focus != nil)
217 				_ctlprint(cs->focus, "focus 0");
218 			cs->focus = nil;
219 			for(f=cs->actives; f!=nil; f=f->nextactive)
220 				if(f->hidden == 0 && f->mouse && ptinrect(mouse.xy, f->rect)){
221 					cs->focus = f;
222 					_ctlprint(f, "focus 1");
223 					if (f->mouse) {
224 						if (debug) fprint(2, "f->mouse %s\n", f->name);
225 						f->mouse(f, &mouse);
226 					}
227 					break;
228 				}
229 		Send:
230 			if(cs->focus && cs->focus->mouse) {
231 				if (debug) fprint(2, "cs->focus->mouse %s\n", cs->focus->name);
232 				cs->focus->mouse(cs->focus, &mouse);
233 			}
234 			prevbut=mouse.buttons;
235 			break;
236 		case ACtl:
237 			_ctlcontrol(cs, str);
238 			free(str);
239 			break;
240 		case AExit:
241 			threadexits(nil);
242 		}
243 	}
244 }
245 
246 Control*
_createctl(Controlset * cs,char * type,uint size,char * name)247 _createctl(Controlset *cs, char *type, uint size, char *name)
248 {
249 	Control *c;
250 
251 	c = _newcontrol(cs, size, name, type);
252 	if(c == nil)
253 		ctlerror("can't create %s control %q: %r", type, name);
254 	return c;
255 }
256 
257 void
closecontrol(Control * c)258 closecontrol(Control *c)
259 {
260 	Control *prev, *p;
261 
262 	if(c == nil)
263 		return;
264 	if (c == c->controlset->focus)
265 		c->controlset->focus = nil;
266 	if(c->exit)
267 		c->exit(c);
268 
269 	prev = nil;
270 	for(p=c->controlset->controls; p; p=p->next){
271 		if(p == c)
272 			break;
273 		prev = p;
274 	}
275 	if(p == nil)
276 		ctlerror("closecontrol: no such control %q %p\n", c->name, c);
277 	if(prev == nil)
278 		c->controlset->controls = c->next;
279 	else
280 		prev->next = c->next;
281 
282 	/* is it active? if so, delete from active list */
283 	prev = nil;
284 	for(p=c->controlset->actives; p; p=p->nextactive){
285 		if(p == c)
286 			break;
287 		prev = p;
288 	}
289 	if(p != nil){
290 		if(prev == nil)
291 			c->controlset->actives = c->nextactive;
292 		else
293 			prev->nextactive = c->nextactive;
294 	}
295 
296 	if(!c->wevent)
297 		chanfree(c->event);
298 	if(!c->wdata)
299 		chanfree(c->data);
300 	free(c->name);
301 	free(c->format);
302 	free(c);
303 }
304 
305 Control*
controlcalled(char * name)306 controlcalled(char *name)
307 {
308 	Control *c;
309 	int i;
310 
311 	for(i=0; i<ncontrolset; i++)
312 		for(c=controlset[i]->controls; c; c=c->next)
313 			if(strcmp(c->name, name) == 0)
314 				return c;
315 	return nil;
316 }
317 
318 void
ctlerror(char * fmt,...)319 ctlerror(char *fmt, ...)
320 {
321 	va_list arg;
322 	char buf[256];
323 
324 	va_start(arg, fmt);
325 	vfprint(2, fmt, arg);
326 	va_end(arg);
327 	write(2, "\n", 1);
328 	threadexitsall(buf);
329 }
330 
331 Rune*
_ctlrunestr(char * s)332 _ctlrunestr(char *s)
333 {
334 	Rune *r, *ret;
335 
336 	ret = r = ctlmalloc((utflen(s)+1)*sizeof(Rune));
337 	while(*s != '\0')
338 		s += chartorune(r++, s);
339 	*r = L'\0';
340 	return ret;
341 }
342 
343 char*
_ctlstrrune(Rune * r)344 _ctlstrrune(Rune *r)
345 {
346 	char *s;
347 	s = ctlmalloc(runestrlen(r)*UTFmax+1);
348 	sprint(s, "%S", r);
349 	return s;
350 }
351 
352 void*
ctlmalloc(uint n)353 ctlmalloc(uint n)
354 {
355 	void *p;
356 
357 	p = mallocz(n, 1);
358 	if(p == nil)
359 		ctlerror("control allocation failed: %r");
360 	return p;
361 }
362 
363 void*
ctlrealloc(void * p,uint n)364 ctlrealloc(void *p, uint n)
365 {
366 	p = realloc(p, n);
367 	if(p == nil)
368 		ctlerror("control reallocation failed: %r");
369 	return p;
370 }
371 
372 char*
ctlstrdup(char * s)373 ctlstrdup(char *s)
374 {
375 	char *t;
376 
377 	t = strdup(s);
378 	if(t == nil)
379 		ctlerror("control strdup(%q) failed: %r", s);
380 	return t;
381 }
382 
383 static void
ctokenize(char * s,CParse * cp)384 ctokenize(char *s, CParse *cp)
385 {
386 	snprint(cp->str, sizeof cp->str, "%s", s);
387 	cp->args = cp->pargs;
388 	cp->nargs = tokenize(s, cp->args, nelem(cp->pargs));
389 }
390 
391 static int
ctlparse(CParse * cp,char * s,int hasreceiver)392 ctlparse(CParse *cp, char *s, int hasreceiver)
393 {
394 	int i;
395 	char *t;
396 
397 	/* keep original string for good error messages */
398 	strncpy(cp->str, s, sizeof cp->str);
399 	cp->str[sizeof cp->str - 1] = '\0';
400 	ctokenize(s, cp);
401 	if(cp->nargs == 0)
402 		return -1;
403 	/* strip leading sender name if present */
404 	cp->sender = nil;
405 	i = strlen(cp->args[0])-1;
406 	if(cp->args[0][i] == ':'){
407 		cp->sender = cp->args[0];
408 		cp->sender[i] = '\0';
409 		cp->args++;
410 		cp->nargs--;
411 	}
412 	if(hasreceiver){
413 		if(cp->nargs-- == 0)
414 			return -1;
415 		cp->receiver = *cp->args++;
416 	}else
417 		cp->receiver = nil;
418 	for(i=0; i<cp->nargs; i++){
419 		t = cp->args[i];
420 		while(*t == '[')	/* %R gives [0 0] [1 1]; atoi will stop at closing ] */
421 			t++;
422 		cp->iargs[i] = atoi(t);
423 	}
424 	return cp->nargs;
425 }
426 
427 void
_ctlargcount(Control * c,CParse * cp,int n)428 _ctlargcount(Control *c, CParse *cp, int n)
429 {
430 	if(cp->nargs != n)
431 		ctlerror("%q: wrong argument count in '%s'", c->name, cp->str);
432 }
433 
434 static void
_ctlcmd(Controlset * cs,char * s)435 _ctlcmd(Controlset *cs, char*s)
436 {
437 	CParse cp;
438 	char	*rcvrs[32];
439 	int	ircvrs[32], n, i, hit;
440 	Control *c;
441 
442 //	fprint(2, "_ctlcmd: %s\n", s);
443 	cp.args = cp.pargs;
444 	if (ctlparse(&cp, s, 1) < 0)
445 		ctlerror("bad command string: %q", cp.str);
446 	if (cp.nargs == 0 && strcmp(cp.receiver, "sync") == 0){
447 		chanprint(cs->data, "sync");
448 		return;
449 	}
450 	if (cp.nargs == 0)
451 		ctlerror("no command in command string: %q", cp.str);
452 
453 	n = tokenize(cp.receiver, rcvrs, nelem(rcvrs));
454 
455 	// lookup type names: a receiver can be a named type or a named control
456 	for (i = 0; i < n; i++)
457 		ircvrs[i] = _ctllookup(rcvrs[i], ctltypenames, Ntypes);
458 
459 	for(c = cs->controls; c != nil; c = c->next){
460 		/* if a control matches on more than one receiver element,
461 		 * make sure it gets processed once; hence loop through controls
462 		 * in the outer loop
463 		 */
464 		hit = 0;
465 		for (i = 0; i < n; i++)
466 			if(strcmp(c->name, rcvrs[i]) == 0 || c->type == ircvrs[i])
467 				hit++;
468 		if (hit && c->ctl)
469 			c->ctl(c, &cp);
470 	}
471 }
472 
473 static void
_ctlcontrol(Controlset * cs,char * s)474 _ctlcontrol(Controlset *cs, char *s)
475 {
476 	char *lines[16];
477 	int i, n;
478 	char *l;
479 
480 //	fprint(2, "_ctlcontrol: %s\n", s);
481 	n = gettokens(s, lines, nelem(lines), "\n");
482 	for(i=0; i<n; i++){
483 		l = lines[i];
484 		while(*l==' ' || *l=='\t')
485 			l++;
486 		if(*l != '\0')
487 			_ctlcmd(cs, l);
488 	}
489 }
490 
491 Rune*
_ctlgetsnarf(void)492 _ctlgetsnarf(void)
493 {
494 	int i, n;
495 	char *sn, buf[512];
496 	Rune *snarf;
497 
498 	if(_ctlsnarffd < 0)
499 		return nil;
500 	sn = nil;
501 	i = 0;
502 	seek(_ctlsnarffd, 0, 0);
503 	while((n = read(_ctlsnarffd, buf, sizeof buf)) > 0){
504 		sn = ctlrealloc(sn, i+n+1);
505 		memmove(sn+i, buf, n);
506 		i += n;
507 		sn[i] = 0;
508 	}
509 	snarf = nil;
510 	if(i > 0){
511 		snarf = _ctlrunestr(sn);
512 		free(sn);
513 	}
514 	return snarf;
515 }
516 
517 void
_ctlputsnarf(Rune * snarf)518 _ctlputsnarf(Rune *snarf)
519 {
520 	int fd, i, n, nsnarf;
521 
522 	if(_ctlsnarffd<0 || snarf[0]==0)
523 		return;
524 	fd = open("/dev/snarf", OWRITE);
525 	if(fd < 0)
526 		return;
527 	nsnarf = runestrlen(snarf);
528 	/* snarf buffer could be huge, so fprint will truncate; do it in blocks */
529 	for(i=0; i<nsnarf; i+=n){
530 		n = nsnarf-i;
531 		if(n >= 256)
532 			n = 256;
533 		if(fprint(fd, "%.*S", n, snarf+i) < 0)
534 			break;
535 	}
536 	close(fd);
537 }
538 
539 int
_ctlalignment(char * s)540 _ctlalignment(char *s)
541 {
542 	int i;
543 
544 	i = _ctllookup(s, alignnames, Nalignments);
545 	if (i < 0)
546 		ctlerror("unknown alignment: %s", s);
547 	return i;
548 }
549 
550 Point
_ctlalignpoint(Rectangle r,int dx,int dy,int align)551 _ctlalignpoint(Rectangle r, int dx, int dy, int align)
552 {
553 	Point p;
554 
555 	p = r.min;	/* in case of trouble */
556 	switch(align%3){
557 	case 0:	/* left */
558 		p.x = r.min.x;
559 		break;
560 	case 1:	/* center */
561 		p.x = r.min.x+(Dx(r)-dx)/2;
562 		break;
563 	case 2:	/* right */
564 		p.x = r.max.x-dx;
565 		break;
566 	}
567 	switch((align/3)%3){
568 	case 0:	/* top */
569 		p.y = r.min.y;
570 		break;
571 	case 1:	/* center */
572 		p.y = r.min.y+(Dy(r)-dy)/2;
573 		break;
574 	case 2:	/* bottom */
575 		p.y = r.max.y - dy;
576 		break;
577 	}
578 	return p;
579 }
580 
581 void
controlwire(Control * cfrom,char * name,Channel * chan)582 controlwire(Control *cfrom, char *name, Channel *chan)
583 {
584 	Channel **p;
585 
586 	p = nil;
587 	if(strcmp(name, "event") == 0){
588 		p = &cfrom->event;
589 		cfrom->wevent = 1;
590 	}else if(strcmp(name, "data") == 0){
591 		p = &cfrom->data;
592 		cfrom->wdata = 1;
593 	}else
594 		ctlerror("%q: unknown controlwire channel %s", cfrom->name, name);
595 	chanfree(*p);
596 	*p = chan;
597 }
598 
599 void
_ctlfocus(Control * me,int set)600 _ctlfocus(Control *me, int set)
601 {
602 	Controlset *cs;
603 
604 	cs = me->controlset;
605 	if(set){
606 		if(cs->focus == me)
607 			return;
608 		if(cs->focus != nil)
609 			_ctlprint(cs->focus, "focus 0");
610 		cs->focus = me;
611 	}else{
612 		if(cs->focus != me)
613 			return;
614 		cs->focus = nil;
615 	}
616 }
617 
618 static void
resizethread(void * v)619 resizethread(void *v)
620 {
621 	Controlset *cs;
622 	char buf[64];
623 	Alt alts[3];
624 
625 	cs = v;
626 	snprint(buf, sizeof buf, "resizethread0x%p", cs);
627 	threadsetname(buf);
628 
629 	alts[0].c = cs->resizec;
630 	alts[0].v = nil;
631 	alts[0].op = CHANRCV;
632 	alts[1].c = cs->resizeexitc;
633 	alts[1].v = nil;
634 	alts[1].op = CHANRCV;
635 	alts[2].op = CHANEND;
636 
637 	for(;;){
638 		switch(alt(alts)){
639 		case 0:
640 			resizecontrolset(cs);
641 			break;
642 		case 1:
643 			return;
644 		}
645 	}
646 }
647 
648 void
activate(Control * a)649 activate(Control *a)
650 {
651 	Control *c;
652 
653 	for(c=a->controlset->actives; c; c=c->nextactive)
654 		if(c == a)
655 			ctlerror("%q already active\n", a->name);
656 
657 	if (a->activate){
658 		a->activate(a, 1);
659 		return;
660 	}
661 	/* prepend */
662 	a->nextactive = a->controlset->actives;
663 	a->controlset->actives = a;
664 }
665 
666 void
deactivate(Control * a)667 deactivate(Control *a)
668 {
669 	Control *c, *prev;
670 
671 	/* if group, first deactivate kids, then self */
672 	if (a->activate){
673 		a->activate(a, 0);
674 		return;
675 	}
676 	prev = nil;
677 	for(c=a->controlset->actives; c; c=c->nextactive){
678 		if(c == a){
679 			if(a->controlset->focus == a)
680 				a->controlset->focus = nil;
681 			if(prev != nil)
682 				prev->nextactive = a->nextactive;
683 			else
684 				a->controlset->actives = a->nextactive;
685 			return;
686 		}
687 		prev = c;
688 	}
689 	ctlerror("%q not active\n", a->name);
690 }
691 
692 static struct
693 {
694 	char	*name;
695 	ulong	color;
696 }coltab[] = {
697 	"red",			DRed,
698 	"green",			DGreen,
699 	"blue",			DBlue,
700 	"cyan",			DCyan,
701 	"magenta",		DMagenta,
702 	"yellow",			DYellow,
703 	"paleyellow",		DPaleyellow,
704 	"darkyellow",		DDarkyellow,
705 	"darkgreen",		DDarkgreen,
706 	"palegreen",		DPalegreen,
707 	"medgreen",		DMedgreen,
708 	"darkblue",		DDarkblue,
709 	"palebluegreen",	DPalebluegreen,
710 	"paleblue",		DPaleblue,
711 	"bluegreen",		DBluegreen,
712 	"greygreen",		DGreygreen,
713 	"palegreygreen",	DPalegreygreen,
714 	"yellowgreen",		DYellowgreen,
715 	"medblue",		DMedblue,
716 	"greyblue",		DGreyblue,
717 	"palegreyblue",		DPalegreyblue,
718 	"purpleblue",		DPurpleblue,
719 	nil,	0
720 };
721 
722 void
initcontrols(void)723 initcontrols(void)
724 {
725 	int i;
726 	Image *im;
727 
728 	quotefmtinstall();
729 	namectlimage(display->opaque, "opaque");
730 	namectlimage(display->transparent, "transparent");
731 	namectlimage(display->white, "white");
732 	namectlimage(display->black, "black");
733 	for(i=0; coltab[i].name!=nil; i++){
734 		im = allocimage(display, Rect(0,0,1,1), RGB24, 1, coltab[i].color);
735 		namectlimage(im, coltab[i].name);
736 	}
737 	namectlfont(font, "font");
738 	_ctlsnarffd = open("/dev/snarf", OREAD);
739 }
740 
741 Controlset*
newcontrolset(Image * im,Channel * kbdc,Channel * mousec,Channel * resizec)742 newcontrolset(Image *im, Channel *kbdc, Channel *mousec, Channel *resizec)
743 {
744 	Controlset *cs;
745 
746 	if(im == nil)
747 		im = screen;
748 	if((mousec==nil && resizec!=nil) || (mousec!=nil && resizec==nil))
749 		ctlerror("must specify either or both of mouse and resize channels");
750 
751 	cs = ctlmalloc(sizeof(Controlset));
752 	cs->screen = im;
753 
754 	if(kbdc == nil){
755 		cs->keyboardctl = initkeyboard(nil);
756 		if(cs->keyboardctl == nil)
757 			ctlerror("can't initialize keyboard: %r");
758 		kbdc = cs->keyboardctl->c;
759 	}
760 	cs ->kbdc = kbdc;
761 
762 	if(mousec == nil){
763 		cs->mousectl = initmouse(nil, im);
764 		if(cs->mousectl == nil)
765 			ctlerror("can't initialize mouse: %r");
766 		mousec = cs->mousectl->c;
767 		resizec = cs->mousectl->resizec;
768 	}
769 	cs->mousec = mousec;
770 	cs->resizec = resizec;
771 	cs->ctl = chancreate(sizeof(char*), 64);	/* buffer to prevent deadlock */
772 	cs->data = chancreate(sizeof(char*), 0);
773 	cs->resizeexitc = chancreate(sizeof(int), 0);
774 	cs->csexitc = chancreate(sizeof(int), 0);
775 
776 	threadcreate(resizethread, cs, 32*1024);
777 	threadcreate(controlsetthread, cs, 32*1024);
778 
779 	controlset = ctlrealloc(controlset, (ncontrolset+1)*sizeof(Controlset*));
780 	controlset[ncontrolset++] = cs;
781 	return cs;
782 }
783 
784 void
closecontrolset(Controlset * cs)785 closecontrolset(Controlset *cs)
786 {
787 	int i;
788 
789 	sendul(cs->resizeexitc, 0);
790 	chanfree(cs->resizeexitc);
791 	sendul(cs->csexitc, 0);
792 	chanfree(cs->csexitc);
793 	chanfree(cs->ctl);
794 	chanfree(cs->data);
795 
796 	for(i=0; i<ncontrolset; i++)
797 		if(cs == controlset[i]){
798 			memmove(controlset+i, controlset+i+1, (ncontrolset-(i+1))*sizeof(Controlset*));
799 			ncontrolset--;
800 			goto Found;
801 		}
802 
803 	if(i == ncontrolset)
804 		ctlerror("closecontrolset: control set not found");
805 
806     Found:
807 	while(cs->controls != nil)
808 		closecontrol(cs->controls);
809 }
810