20 static Controlset **controlset;
24 char *alignnames[Nalignments] = {
25 [Aupperleft] = "upperleft",
26 [Auppercenter] = "uppercenter",
27 [Aupperright] = "upperright",
28 [Acenterleft] = "centerleft",
30 [Acenterright] = "centerright",
31 [Alowerleft] = "lowerleft",
32 [Alowercenter] = "lowercenter",
33 [Alowerright] = "lowerright",
36 char *ctltypenames[Ntypes] = {
37 [Ctlunknown] = "unknown",
39 [Ctlbutton] = "button",
41 [Ctlkeyboard] = "keyboard",
45 [Ctlscribble] = "scribble",
46 [Ctlslider] = "slider",
49 [Ctltextbutton] = "textbutton",
50 [Ctltextbutton3] = "textbutton3",
51 [Ctlgroup] = "group", // divider between controls and metacontrols
52 [Ctlboxbox] = "boxbox",
53 [Ctlcolumn] = "column",
59 static void _ctlcmd(Controlset*, char*);
60 static void _ctlcontrol(Controlset*, char*);
63 _mkctlcmd(Control *c, char *fmt, va_list arg)
65 char *name, *p, *both;
67 name = quotestrdup(c->name);
69 ctlerror("quotestrdup in ctlprint failed");
70 p = vsmprint(fmt, arg);
73 ctlerror("vsmprint1 in ctlprint failed");
75 both = ctlmalloc(strlen(name)+strlen(p)+2);
85 ctlprint(Control *c, char *fmt, ...)
92 p = _mkctlcmd(c, fmt, arg);
94 n = sendp(c->controlset->ctl, p);
100 _ctlprint(Control *c, char *fmt, ...)
106 p = _mkctlcmd(c, fmt, arg);
108 _ctlcmd(c->controlset, p);
113 _ctllookup(char *s, char *tab[], int ntab)
117 for(i=0; i<ntab; i++)
118 if(tab[i] != nil && strcmp(s, tab[i]) == 0)
124 _newcontrol(Controlset *cs, uint n, char *name, char *type)
128 for(c=cs->controls; c; c=c->next)
129 if(strcmp(c->name, name) == 0){
130 werrstr("control %q already defined", name);
134 c->screen = cs->screen;
135 c->name = ctlstrdup(name);
136 c->type = _ctllookup(type, ctltypenames, Ntypes);
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);
150 c->next = cs->controls;
156 controlsetthread(void *v)
164 Rune buf[2][20], *rp;
167 threadsetname("controlsetthread 0x%p", cs);
169 alts[AKey].c = cs->kbdc;
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;
177 alts[ACtl].op = CHANRCV;
178 alts[AExit].c = cs->csexitc;
180 alts[AExit].op = CHANRCV;
181 alts[NALT].op = CHANEND;
187 /* toggle so we can receive in one buffer while client processes the other */
188 alts[AKey].v = buf[n];
193 if(ctldeletequits && rp[0]=='\177')
195 for(i=1; i<nelem(buf[0])-1; i++)
196 if(nbrecv(cs->kbdc, rp+i) <= 0)
199 if(cs->focus && cs->focus->key)
200 cs->focus->key(cs->focus, rp);
203 /* is this a focus change? */
204 if(prevbut) /* don't change focus if button was down */
206 if(cs->focus!=nil && cs->focus->hidden == 0 && ptinrect(mouse.xy, cs->focus->rect))
208 if(cs->clicktotype == 0)
210 /* click to type: only change if button is down */
211 if(mouse.buttons == 0)
214 /* change of focus */
216 _ctlprint(cs->focus, "focus 0");
218 for(f=cs->actives; f!=nil; f=f->nextactive)
219 if(f->hidden == 0 && f->mouse && ptinrect(mouse.xy, f->rect)){
221 _ctlprint(f, "focus 1");
223 if (debug) fprint(2, "f->mouse %s\n", f->name);
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);
233 prevbut=mouse.buttons;
236 _ctlcontrol(cs, str);
246 _createctl(Controlset *cs, char *type, uint size, char *name)
250 c = _newcontrol(cs, size, name, type);
252 ctlerror("can't create %s control %q: %r", type, name);
257 closecontrol(Control *c)
263 if (c == c->controlset->focus)
264 c->controlset->focus = nil;
269 for(p=c->controlset->controls; p; p=p->next){
275 ctlerror("closecontrol: no such control %q %p\n", c->name, c);
277 c->controlset->controls = c->next;
279 prev->next = c->next;
281 /* is it active? if so, delete from active list */
283 for(p=c->controlset->actives; p; p=p->nextactive){
290 c->controlset->actives = c->nextactive;
292 prev->nextactive = c->nextactive;
305 controlcalled(char *name)
310 for(i=0; i<ncontrolset; i++)
311 for(c=controlset[i]->controls; c; c=c->next)
312 if(strcmp(c->name, name) == 0)
318 ctlerror(char *fmt, ...)
324 vfprint(2, fmt, arg);
335 ret = r = ctlmalloc((utflen(s)+1)*sizeof(Rune));
337 s += chartorune(r++, s);
346 s = ctlmalloc(runestrlen(r)*UTFmax+1);
358 ctlerror("control allocation failed: %r");
363 ctlrealloc(void *p, uint n)
367 ctlerror("control reallocation failed: %r");
378 ctlerror("control strdup(%q) failed: %r", s);
383 ctokenize(char *s, CParse *cp)
385 snprint(cp->str, sizeof cp->str, "%s", s);
386 cp->args = cp->pargs;
387 cp->nargs = tokenize(s, cp->args, nelem(cp->pargs));
391 ctlparse(CParse *cp, char *s, int hasreceiver)
396 /* keep original string for good error messages */
397 strncpy(cp->str, s, sizeof cp->str);
398 cp->str[sizeof cp->str - 1] = '\0';
402 /* strip leading sender name if present */
404 i = strlen(cp->args[0])-1;
405 if(cp->args[0][i] == ':'){
406 cp->sender = cp->args[0];
407 cp->sender[i] = '\0';
414 cp->receiver = *cp->args++;
417 for(i=0; i<cp->nargs; i++){
419 while(*t == '[') /* %R gives [0 0] [1 1]; atoi will stop at closing ] */
421 cp->iargs[i] = atoi(t);
427 _ctlargcount(Control *c, CParse *cp, int n)
430 ctlerror("%q: wrong argument count in '%s'", c->name, cp->str);
434 _ctlcmd(Controlset *cs, char*s)
438 int ircvrs[32], n, i, hit;
441 // fprint(2, "_ctlcmd: %s\n", s);
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");
450 ctlerror("no command in command string: %q", cp.str);
452 n = tokenize(cp.receiver, rcvrs, nelem(rcvrs));
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);
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
464 for (i = 0; i < n; i++)
465 if(strcmp(c->name, rcvrs[i]) == 0 || c->type == ircvrs[i])
473 _ctlcontrol(Controlset *cs, char *s)
479 // fprint(2, "_ctlcontrol: %s\n", s);
480 n = gettokens(s, lines, nelem(lines), "\n");
483 while(*l==' ' || *l=='\t')
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);
510 snarf = _ctlrunestr(sn);
517 _ctlputsnarf(Rune *snarf)
519 int fd, i, n, nsnarf;
521 if(_ctlsnarffd<0 || snarf[0]==0)
523 fd = open("/dev/snarf", OWRITE);
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){
532 if(fprint(fd, "%.*S", n, snarf+i) < 0)
539 _ctlalignment(char *s)
543 i = _ctllookup(s, alignnames, Nalignments);
545 ctlerror("unknown alignment: %s", s);
550 _ctlalignpoint(Rectangle r, int dx, int dy, int align)
554 p = r.min; /* in case of trouble */
560 p.x = r.min.x+(Dx(r)-dx)/2;
571 p.y = r.min.y+(Dy(r)-dy)/2;
581 controlwire(Control *cfrom, char *name, Channel *chan)
586 if(strcmp(name, "event") == 0){
589 }else if(strcmp(name, "data") == 0){
593 ctlerror("%q: unknown controlwire channel %s", cfrom->name, name);
599 _ctlfocus(Control *me, int set)
608 _ctlprint(cs->focus, "focus 0");
618 resizethread(void *v)
624 threadsetname("resizethread 0x%p", cs);
626 alts[0].c = cs->resizec;
628 alts[0].op = CHANRCV;
629 alts[1].c = cs->resizeexitc;
631 alts[1].op = CHANRCV;
632 alts[2].op = CHANEND;
637 resizecontrolset(cs);
650 for(c=a->controlset->actives; c; c=c->nextactive)
652 ctlerror("%q already active\n", a->name);
659 a->nextactive = a->controlset->actives;
660 a->controlset->actives = a;
664 deactivate(Control *a)
668 /* if group, first deactivate kids, then self */
674 for(c=a->controlset->actives; c; c=c->nextactive){
676 if(a->controlset->focus == a)
677 a->controlset->focus = nil;
679 prev->nextactive = a->nextactive;
681 a->controlset->actives = a->nextactive;
686 ctlerror("%q not active\n", a->name);
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,
713 "greyblue", DGreyblue,
714 "palegreyblue", DPalegreyblue,
715 "purpleblue", DPurpleblue,
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);
734 namectlfont(font, "font");
735 _ctlsnarffd = open("/dev/snarf", OREAD);
739 newcontrolset(Image *im, Channel *kbdc, Channel *mousec, Channel *resizec)
745 if((mousec==nil && resizec!=nil) || (mousec!=nil && resizec==nil))
746 ctlerror("must specify either or both of mouse and resize channels");
748 cs = ctlmalloc(sizeof(Controlset));
752 cs->keyboardctl = initkeyboard(nil);
753 if(cs->keyboardctl == nil)
754 ctlerror("can't initialize keyboard: %r");
755 kbdc = cs->keyboardctl->c;
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;
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);
773 threadcreate(resizethread, cs, 32*1024);
774 threadcreate(controlsetthread, cs, 32*1024);
776 controlset = ctlrealloc(controlset, (ncontrolset+1)*sizeof(Controlset*));
777 controlset[ncontrolset++] = cs;
782 closecontrolset(Controlset *cs)
786 sendul(cs->resizeexitc, 0);
787 chanfree(cs->resizeexitc);
788 sendul(cs->csexitc, 0);
789 chanfree(cs->csexitc);
793 for(i=0; i<ncontrolset; i++)
794 if(cs == controlset[i]){
795 memmove(controlset+i, controlset+i+1, (ncontrolset-(i+1))*sizeof(Controlset*));
801 ctlerror("closecontrolset: control set not found");
804 while(cs->controls != nil)
805 closecontrol(cs->controls);