]> git.lizzy.rs Git - plan9front.git/blob - sys/src/libcontrol/control.c
Import sources from 2011-03-30 iso image - lib
[plan9front.git] / sys / src / libcontrol / control.c
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*
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
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
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
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*
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
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*
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
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*
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
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*
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*
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*
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*
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*
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
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
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
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
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
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*
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
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
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
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
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
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
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
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
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
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*
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
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 }