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