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