xref: /plan9-contrib/sys/src/libcontrol/control.c (revision 75e4bb061c5a96f72ddf5c2f9a4bde6c5cf90ed0)
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 	int nb;
347 	char *s;
348 
349 	nb = runestrlen(r)*UTFmax+1;
350 	s = ctlmalloc(nb);
351 	snprint(s, nb, "%S", r);
352 	return s;
353 }
354 
355 void*
ctlmalloc(uint n)356 ctlmalloc(uint n)
357 {
358 	void *p;
359 
360 	p = mallocz(n, 1);
361 	if(p == nil)
362 		ctlerror("control allocation failed: %r");
363 	return p;
364 }
365 
366 void*
ctlrealloc(void * p,uint n)367 ctlrealloc(void *p, uint n)
368 {
369 	p = realloc(p, n);
370 	if(p == nil)
371 		ctlerror("control reallocation failed: %r");
372 	return p;
373 }
374 
375 char*
ctlstrdup(char * s)376 ctlstrdup(char *s)
377 {
378 	char *t;
379 
380 	t = strdup(s);
381 	if(t == nil)
382 		ctlerror("control strdup(%q) failed: %r", s);
383 	return t;
384 }
385 
386 static void
ctokenize(char * s,CParse * cp)387 ctokenize(char *s, CParse *cp)
388 {
389 	snprint(cp->str, sizeof cp->str, "%s", s);
390 	cp->args = cp->pargs;
391 	cp->nargs = tokenize(s, cp->args, nelem(cp->pargs));
392 }
393 
394 static int
ctlparse(CParse * cp,char * s,int hasreceiver)395 ctlparse(CParse *cp, char *s, int hasreceiver)
396 {
397 	int i;
398 	char *t;
399 
400 	/* keep original string for good error messages */
401 	strncpy(cp->str, s, sizeof cp->str);
402 	cp->str[sizeof cp->str - 1] = '\0';
403 	ctokenize(s, cp);
404 	if(cp->nargs == 0)
405 		return -1;
406 	/* strip leading sender name if present */
407 	cp->sender = nil;
408 	i = strlen(cp->args[0])-1;
409 	if(cp->args[0][i] == ':'){
410 		cp->sender = cp->args[0];
411 		cp->sender[i] = '\0';
412 		cp->args++;
413 		cp->nargs--;
414 	}
415 	if(hasreceiver){
416 		if(cp->nargs-- == 0)
417 			return -1;
418 		cp->receiver = *cp->args++;
419 	}else
420 		cp->receiver = nil;
421 	for(i=0; i<cp->nargs; i++){
422 		t = cp->args[i];
423 		while(*t == '[')	/* %R gives [0 0] [1 1]; atoi will stop at closing ] */
424 			t++;
425 		cp->iargs[i] = atoi(t);
426 	}
427 	return cp->nargs;
428 }
429 
430 void
_ctlargcount(Control * c,CParse * cp,int n)431 _ctlargcount(Control *c, CParse *cp, int n)
432 {
433 	if(cp->nargs != n)
434 		ctlerror("%q: wrong argument count in '%s'", c->name, cp->str);
435 }
436 
437 static void
_ctlcmd(Controlset * cs,char * s)438 _ctlcmd(Controlset *cs, char*s)
439 {
440 	CParse cp;
441 	char	*rcvrs[32];
442 	int	ircvrs[32], n, i, hit;
443 	Control *c;
444 
445 //	fprint(2, "_ctlcmd: %s\n", s);
446 	cp.args = cp.pargs;
447 	if (ctlparse(&cp, s, 1) < 0)
448 		ctlerror("bad command string: %q", cp.str);
449 	if (cp.nargs == 0 && strcmp(cp.receiver, "sync") == 0){
450 		chanprint(cs->data, "sync");
451 		return;
452 	}
453 	if (cp.nargs == 0)
454 		ctlerror("no command in command string: %q", cp.str);
455 
456 	n = tokenize(cp.receiver, rcvrs, nelem(rcvrs));
457 
458 	// lookup type names: a receiver can be a named type or a named control
459 	for (i = 0; i < n; i++)
460 		ircvrs[i] = _ctllookup(rcvrs[i], ctltypenames, Ntypes);
461 
462 	for(c = cs->controls; c != nil; c = c->next){
463 		/* if a control matches on more than one receiver element,
464 		 * make sure it gets processed once; hence loop through controls
465 		 * in the outer loop
466 		 */
467 		hit = 0;
468 		for (i = 0; i < n; i++)
469 			if(strcmp(c->name, rcvrs[i]) == 0 || c->type == ircvrs[i])
470 				hit++;
471 		if (hit && c->ctl)
472 			c->ctl(c, &cp);
473 	}
474 }
475 
476 static void
_ctlcontrol(Controlset * cs,char * s)477 _ctlcontrol(Controlset *cs, char *s)
478 {
479 	char *lines[16];
480 	int i, n;
481 	char *l;
482 
483 //	fprint(2, "_ctlcontrol: %s\n", s);
484 	n = gettokens(s, lines, nelem(lines), "\n");
485 	for(i=0; i<n; i++){
486 		l = lines[i];
487 		while(*l==' ' || *l=='\t')
488 			l++;
489 		if(*l != '\0')
490 			_ctlcmd(cs, l);
491 	}
492 }
493 
494 Rune*
_ctlgetsnarf(void)495 _ctlgetsnarf(void)
496 {
497 	int i, n;
498 	char *sn, buf[512];
499 	Rune *snarf;
500 
501 	if(_ctlsnarffd < 0)
502 		return nil;
503 	sn = nil;
504 	i = 0;
505 	seek(_ctlsnarffd, 0, 0);
506 	while((n = read(_ctlsnarffd, buf, sizeof buf)) > 0){
507 		sn = ctlrealloc(sn, i+n+1);
508 		memmove(sn+i, buf, n);
509 		i += n;
510 		sn[i] = 0;
511 	}
512 	snarf = nil;
513 	if(i > 0){
514 		snarf = _ctlrunestr(sn);
515 		free(sn);
516 	}
517 	return snarf;
518 }
519 
520 void
_ctlputsnarf(Rune * snarf)521 _ctlputsnarf(Rune *snarf)
522 {
523 	int fd, i, n, nsnarf;
524 
525 	if(_ctlsnarffd<0 || snarf[0]==0)
526 		return;
527 	fd = open("/dev/snarf", OWRITE);
528 	if(fd < 0)
529 		return;
530 	nsnarf = runestrlen(snarf);
531 	/* snarf buffer could be huge, so fprint will truncate; do it in blocks */
532 	for(i=0; i<nsnarf; i+=n){
533 		n = nsnarf-i;
534 		if(n >= 256)
535 			n = 256;
536 		if(fprint(fd, "%.*S", n, snarf+i) < 0)
537 			break;
538 	}
539 	close(fd);
540 }
541 
542 int
_ctlalignment(char * s)543 _ctlalignment(char *s)
544 {
545 	int i;
546 
547 	i = _ctllookup(s, alignnames, Nalignments);
548 	if (i < 0)
549 		ctlerror("unknown alignment: %s", s);
550 	return i;
551 }
552 
553 Point
_ctlalignpoint(Rectangle r,int dx,int dy,int align)554 _ctlalignpoint(Rectangle r, int dx, int dy, int align)
555 {
556 	Point p;
557 
558 	p = r.min;	/* in case of trouble */
559 	switch(align%3){
560 	case 0:	/* left */
561 		p.x = r.min.x;
562 		break;
563 	case 1:	/* center */
564 		p.x = r.min.x+(Dx(r)-dx)/2;
565 		break;
566 	case 2:	/* right */
567 		p.x = r.max.x-dx;
568 		break;
569 	}
570 	switch((align/3)%3){
571 	case 0:	/* top */
572 		p.y = r.min.y;
573 		break;
574 	case 1:	/* center */
575 		p.y = r.min.y+(Dy(r)-dy)/2;
576 		break;
577 	case 2:	/* bottom */
578 		p.y = r.max.y - dy;
579 		break;
580 	}
581 	return p;
582 }
583 
584 void
controlwire(Control * cfrom,char * name,Channel * chan)585 controlwire(Control *cfrom, char *name, Channel *chan)
586 {
587 	Channel **p;
588 
589 	p = nil;
590 	if(strcmp(name, "event") == 0){
591 		p = &cfrom->event;
592 		cfrom->wevent = 1;
593 	}else if(strcmp(name, "data") == 0){
594 		p = &cfrom->data;
595 		cfrom->wdata = 1;
596 	}else
597 		ctlerror("%q: unknown controlwire channel %s", cfrom->name, name);
598 	chanfree(*p);
599 	*p = chan;
600 }
601 
602 void
_ctlfocus(Control * me,int set)603 _ctlfocus(Control *me, int set)
604 {
605 	Controlset *cs;
606 
607 	cs = me->controlset;
608 	if(set){
609 		if(cs->focus == me)
610 			return;
611 		if(cs->focus != nil)
612 			_ctlprint(cs->focus, "focus 0");
613 		cs->focus = me;
614 	}else{
615 		if(cs->focus != me)
616 			return;
617 		cs->focus = nil;
618 	}
619 }
620 
621 static void
resizethread(void * v)622 resizethread(void *v)
623 {
624 	Controlset *cs;
625 	char buf[64];
626 	Alt alts[3];
627 
628 	cs = v;
629 	snprint(buf, sizeof buf, "resizethread0x%p", cs);
630 	threadsetname(buf);
631 
632 	alts[0].c = cs->resizec;
633 	alts[0].v = nil;
634 	alts[0].op = CHANRCV;
635 	alts[1].c = cs->resizeexitc;
636 	alts[1].v = nil;
637 	alts[1].op = CHANRCV;
638 	alts[2].op = CHANEND;
639 
640 	for(;;){
641 		switch(alt(alts)){
642 		case 0:
643 			resizecontrolset(cs);
644 			break;
645 		case 1:
646 			return;
647 		}
648 	}
649 }
650 
651 void
activate(Control * a)652 activate(Control *a)
653 {
654 	Control *c;
655 
656 	for(c=a->controlset->actives; c; c=c->nextactive)
657 		if(c == a)
658 			ctlerror("%q already active\n", a->name);
659 
660 	if (a->activate){
661 		a->activate(a, 1);
662 		return;
663 	}
664 	/* prepend */
665 	a->nextactive = a->controlset->actives;
666 	a->controlset->actives = a;
667 }
668 
669 void
deactivate(Control * a)670 deactivate(Control *a)
671 {
672 	Control *c, *prev;
673 
674 	/* if group, first deactivate kids, then self */
675 	if (a->activate){
676 		a->activate(a, 0);
677 		return;
678 	}
679 	prev = nil;
680 	for(c=a->controlset->actives; c; c=c->nextactive){
681 		if(c == a){
682 			if(a->controlset->focus == a)
683 				a->controlset->focus = nil;
684 			if(prev != nil)
685 				prev->nextactive = a->nextactive;
686 			else
687 				a->controlset->actives = a->nextactive;
688 			return;
689 		}
690 		prev = c;
691 	}
692 	ctlerror("%q not active\n", a->name);
693 }
694 
695 static struct
696 {
697 	char	*name;
698 	ulong	color;
699 }coltab[] = {
700 	"red",			DRed,
701 	"green",			DGreen,
702 	"blue",			DBlue,
703 	"cyan",			DCyan,
704 	"magenta",		DMagenta,
705 	"yellow",			DYellow,
706 	"paleyellow",		DPaleyellow,
707 	"darkyellow",		DDarkyellow,
708 	"darkgreen",		DDarkgreen,
709 	"palegreen",		DPalegreen,
710 	"medgreen",		DMedgreen,
711 	"darkblue",		DDarkblue,
712 	"palebluegreen",	DPalebluegreen,
713 	"paleblue",		DPaleblue,
714 	"bluegreen",		DBluegreen,
715 	"greygreen",		DGreygreen,
716 	"palegreygreen",	DPalegreygreen,
717 	"yellowgreen",		DYellowgreen,
718 	"medblue",		DMedblue,
719 	"greyblue",		DGreyblue,
720 	"palegreyblue",		DPalegreyblue,
721 	"purpleblue",		DPurpleblue,
722 	nil,	0
723 };
724 
725 void
initcontrols(void)726 initcontrols(void)
727 {
728 	int i;
729 	Image *im;
730 
731 	quotefmtinstall();
732 	namectlimage(display->opaque, "opaque");
733 	namectlimage(display->transparent, "transparent");
734 	namectlimage(display->white, "white");
735 	namectlimage(display->black, "black");
736 	for(i=0; coltab[i].name!=nil; i++){
737 		im = allocimage(display, Rect(0,0,1,1), RGB24, 1, coltab[i].color);
738 		namectlimage(im, coltab[i].name);
739 	}
740 	namectlfont(font, "font");
741 	_ctlsnarffd = open("/dev/snarf", OREAD);
742 }
743 
744 Controlset*
newcontrolset(Image * im,Channel * kbdc,Channel * mousec,Channel * resizec)745 newcontrolset(Image *im, Channel *kbdc, Channel *mousec, Channel *resizec)
746 {
747 	Controlset *cs;
748 
749 	if(im == nil)
750 		im = screen;
751 	if((mousec==nil && resizec!=nil) || (mousec!=nil && resizec==nil))
752 		ctlerror("must specify either or both of mouse and resize channels");
753 
754 	cs = ctlmalloc(sizeof(Controlset));
755 	cs->screen = im;
756 
757 	if(kbdc == nil){
758 		cs->keyboardctl = initkeyboard(nil);
759 		if(cs->keyboardctl == nil)
760 			ctlerror("can't initialize keyboard: %r");
761 		kbdc = cs->keyboardctl->c;
762 	}
763 	cs ->kbdc = kbdc;
764 
765 	if(mousec == nil){
766 		cs->mousectl = initmouse(nil, im);
767 		if(cs->mousectl == nil)
768 			ctlerror("can't initialize mouse: %r");
769 		mousec = cs->mousectl->c;
770 		resizec = cs->mousectl->resizec;
771 	}
772 	cs->mousec = mousec;
773 	cs->resizec = resizec;
774 	cs->ctl = chancreate(sizeof(char*), 64);	/* buffer to prevent deadlock */
775 	cs->data = chancreate(sizeof(char*), 0);
776 	cs->resizeexitc = chancreate(sizeof(int), 0);
777 	cs->csexitc = chancreate(sizeof(int), 0);
778 
779 	threadcreate(resizethread, cs, 32*1024);
780 	threadcreate(controlsetthread, cs, 32*1024);
781 
782 	controlset = ctlrealloc(controlset, (ncontrolset+1)*sizeof(Controlset*));
783 	controlset[ncontrolset++] = cs;
784 	return cs;
785 }
786 
787 void
closecontrolset(Controlset * cs)788 closecontrolset(Controlset *cs)
789 {
790 	int i;
791 
792 	sendul(cs->resizeexitc, 0);
793 	chanfree(cs->resizeexitc);
794 	sendul(cs->csexitc, 0);
795 	chanfree(cs->csexitc);
796 	chanfree(cs->ctl);
797 	chanfree(cs->data);
798 
799 	for(i=0; i<ncontrolset; i++)
800 		if(cs == controlset[i]){
801 			memmove(controlset+i, controlset+i+1, (ncontrolset-(i+1))*sizeof(Controlset*));
802 			ncontrolset--;
803 			goto Found;
804 		}
805 
806 	if(i == ncontrolset)
807 		ctlerror("closecontrolset: control set not found");
808 
809     Found:
810 	while(cs->controls != nil)
811 		closecontrol(cs->controls);
812 }
813