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