diff options
author | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
---|---|---|
committer | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
commit | e5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch) | |
tree | d8d51eac403f07814b9e936eed0c9a79195e2450 /sys/src/libcontrol/control.c |
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/libcontrol/control.c')
-rwxr-xr-x | sys/src/libcontrol/control.c | 809 |
1 files changed, 809 insertions, 0 deletions
diff --git a/sys/src/libcontrol/control.c b/sys/src/libcontrol/control.c new file mode 100755 index 000000000..f30d6fe57 --- /dev/null +++ b/sys/src/libcontrol/control.c @@ -0,0 +1,809 @@ +#include <u.h> +#include <libc.h> +#include <draw.h> +#include <thread.h> +#include <mouse.h> +#include <keyboard.h> +#include <control.h> + +static int debug = 0; + +enum /* alts */ +{ + AKey, + AMouse, + ACtl, + AExit, + NALT +}; + +static Controlset **controlset; +int ncontrolset; +int ctldeletequits; + +char *alignnames[Nalignments] = { + [Aupperleft] = "upperleft", + [Auppercenter] = "uppercenter", + [Aupperright] = "upperright", + [Acenterleft] = "centerleft", + [Acenter] = "center", + [Acenterright] = "centerright", + [Alowerleft] = "lowerleft", + [Alowercenter] = "lowercenter", + [Alowerright] = "lowerright", +}; + +char *ctltypenames[Ntypes] = { + [Ctlunknown] = "unknown", + [Ctlbox] = "box", + [Ctlbutton] = "button", + [Ctlentry] = "entry", + [Ctlkeyboard] = "keyboard", + [Ctllabel] = "label", + [Ctlmenu] = "menu", + [Ctlradio] = "radio", + [Ctlscribble] = "scribble", + [Ctlslider] = "slider", + [Ctltabs] = "tabs", + [Ctltext] = "text", + [Ctltextbutton] = "textbutton", + [Ctltextbutton3] = "textbutton3", + [Ctlgroup] = "group", // divider between controls and metacontrols + [Ctlboxbox] = "boxbox", + [Ctlcolumn] = "column", + [Ctlrow] = "row", + [Ctlstack] = "stack", + [Ctltab] = "tab", +}; + +static void _ctlcmd(Controlset*, char*); +static void _ctlcontrol(Controlset*, char*); + +static char* +_mkctlcmd(Control *c, char *fmt, va_list arg) +{ + char *name, *p, *both; + + name = quotestrdup(c->name); + if(name == nil) + ctlerror("quotestrdup in ctlprint failed"); + p = vsmprint(fmt, arg); + if(p == nil){ + free(name); + ctlerror("vsmprint1 in ctlprint failed"); + } + both = ctlmalloc(strlen(name)+strlen(p)+2); + strcpy(both, name); + strcat(both, " "); + strcat(both, p); + free(name); + free(p); + return both; +} + +int +ctlprint(Control *c, char *fmt, ...) +{ + int n; + char *p; + va_list arg; + + va_start(arg, fmt); + p = _mkctlcmd(c, fmt, arg); + va_end(arg); + n = sendp(c->controlset->ctl, p); + yield(); + return n; +} + +void +_ctlprint(Control *c, char *fmt, ...) +{ + char *p; + va_list arg; + + va_start(arg, fmt); + p = _mkctlcmd(c, fmt, arg); + va_end(arg); + _ctlcmd(c->controlset, p); + free(p); +} + +int +_ctllookup(char *s, char *tab[], int ntab) +{ + int i; + + for(i=0; i<ntab; i++) + if(tab[i] != nil && strcmp(s, tab[i]) == 0) + return i; + return -1; +} + +static Control* +_newcontrol(Controlset *cs, uint n, char *name, char *type) +{ + Control *c; + + for(c=cs->controls; c; c=c->next) + if(strcmp(c->name, name) == 0){ + werrstr("control %q already defined", name); + return nil; + } + c = ctlmalloc(n); + c->screen = cs->screen; + c->name = ctlstrdup(name); + c->type = _ctllookup(type, ctltypenames, Ntypes); + if (c->type < 0) + ctlerror("unknown type: %s", type); + c->event = chancreate(sizeof(char*), 64); + c->data = chancreate(sizeof(char*), 0); + c->size = Rect(1, 1, _Ctlmaxsize, _Ctlmaxsize); + c->hidden = 0; + c->ctl = nil; + c->mouse = nil; + c->key = nil; + c->exit = nil; + c->setsize = nil; + + c->controlset = cs; + c->next = cs->controls; + cs->controls = c; + return c; +} + +static void +controlsetthread(void *v) +{ + Controlset *cs; + Mouse mouse; + Control *f; + int prevbut, n, i; + Alt alts[NALT+1]; + char tmp[64], *str; + Rune buf[2][20], *rp; + + cs = v; + snprint(tmp, sizeof tmp, "controlsetthread 0x%p", cs); + threadsetname(tmp); + + alts[AKey].c = cs->kbdc; + alts[AKey].v = &rp; + alts[AKey].op = CHANRCV; + alts[AMouse].c = cs->mousec; + alts[AMouse].v = &mouse; + alts[AMouse].op = CHANRCV; + alts[ACtl].c = cs->ctl; + alts[ACtl].v = &str; + alts[ACtl].op = CHANRCV; + alts[AExit].c = cs->csexitc; + alts[AExit].v = nil; + alts[AExit].op = CHANRCV; + alts[NALT].op = CHANEND; + + cs->focus = nil; + prevbut=0; + n = 0; + for(;;){ + /* toggle so we can receive in one buffer while client processes the other */ + alts[AKey].v = buf[n]; + rp = buf[n]; + n = 1-n; + switch(alt(alts)){ + case AKey: + if(ctldeletequits && rp[0]=='\177') + ctlerror("delete"); + for(i=1; i<nelem(buf[0])-1; i++) + if(nbrecv(cs->kbdc, rp+i) <= 0) + break; + rp[i] = L'\0'; + if(cs->focus && cs->focus->key) + cs->focus->key(cs->focus, rp); + break; + case AMouse: + /* is this a focus change? */ + if(prevbut) /* don't change focus if button was down */ + goto Send; + if(cs->focus!=nil && cs->focus->hidden == 0 && ptinrect(mouse.xy, cs->focus->rect)) + goto Send; + if(cs->clicktotype == 0) + goto Change; + /* click to type: only change if button is down */ + if(mouse.buttons == 0) + goto Send; + Change: + /* change of focus */ + if(cs->focus != nil) + _ctlprint(cs->focus, "focus 0"); + cs->focus = nil; + for(f=cs->actives; f!=nil; f=f->nextactive) + if(f->hidden == 0 && f->mouse && ptinrect(mouse.xy, f->rect)){ + cs->focus = f; + _ctlprint(f, "focus 1"); + if (f->mouse) { + if (debug) fprint(2, "f->mouse %s\n", f->name); + f->mouse(f, &mouse); + } + break; + } + Send: + if(cs->focus && cs->focus->mouse) { + if (debug) fprint(2, "cs->focus->mouse %s\n", cs->focus->name); + cs->focus->mouse(cs->focus, &mouse); + } + prevbut=mouse.buttons; + break; + case ACtl: + _ctlcontrol(cs, str); + free(str); + break; + case AExit: + threadexits(nil); + } + } +} + +Control* +_createctl(Controlset *cs, char *type, uint size, char *name) +{ + Control *c; + + c = _newcontrol(cs, size, name, type); + if(c == nil) + ctlerror("can't create %s control %q: %r", type, name); + return c; +} + +void +closecontrol(Control *c) +{ + Control *prev, *p; + + if(c == nil) + return; + if (c == c->controlset->focus) + c->controlset->focus = nil; + if(c->exit) + c->exit(c); + + prev = nil; + for(p=c->controlset->controls; p; p=p->next){ + if(p == c) + break; + prev = p; + } + if(p == nil) + ctlerror("closecontrol: no such control %q %p\n", c->name, c); + if(prev == nil) + c->controlset->controls = c->next; + else + prev->next = c->next; + + /* is it active? if so, delete from active list */ + prev = nil; + for(p=c->controlset->actives; p; p=p->nextactive){ + if(p == c) + break; + prev = p; + } + if(p != nil){ + if(prev == nil) + c->controlset->actives = c->nextactive; + else + prev->nextactive = c->nextactive; + } + + if(!c->wevent) + chanfree(c->event); + if(!c->wdata) + chanfree(c->data); + free(c->name); + free(c->format); + free(c); +} + +Control* +controlcalled(char *name) +{ + Control *c; + int i; + + for(i=0; i<ncontrolset; i++) + for(c=controlset[i]->controls; c; c=c->next) + if(strcmp(c->name, name) == 0) + return c; + return nil; +} + +void +ctlerror(char *fmt, ...) +{ + va_list arg; + char buf[256]; + + va_start(arg, fmt); + vfprint(2, fmt, arg); + va_end(arg); + write(2, "\n", 1); + threadexitsall(buf); +} + +Rune* +_ctlrunestr(char *s) +{ + Rune *r, *ret; + + ret = r = ctlmalloc((utflen(s)+1)*sizeof(Rune)); + while(*s != '\0') + s += chartorune(r++, s); + *r = L'\0'; + return ret; +} + +char* +_ctlstrrune(Rune *r) +{ + char *s; + s = ctlmalloc(runestrlen(r)*UTFmax+1); + sprint(s, "%S", r); + return s; +} + +void* +ctlmalloc(uint n) +{ + void *p; + + p = mallocz(n, 1); + if(p == nil) + ctlerror("control allocation failed: %r"); + return p; +} + +void* +ctlrealloc(void *p, uint n) +{ + p = realloc(p, n); + if(p == nil) + ctlerror("control reallocation failed: %r"); + return p; +} + +char* +ctlstrdup(char *s) +{ + char *t; + + t = strdup(s); + if(t == nil) + ctlerror("control strdup(%q) failed: %r", s); + return t; +} + +static void +ctokenize(char *s, CParse *cp) +{ + snprint(cp->str, sizeof cp->str, "%s", s); + cp->args = cp->pargs; + cp->nargs = tokenize(s, cp->args, nelem(cp->pargs)); +} + +static int +ctlparse(CParse *cp, char *s, int hasreceiver) +{ + int i; + char *t; + + /* keep original string for good error messages */ + strncpy(cp->str, s, sizeof cp->str); + cp->str[sizeof cp->str - 1] = '\0'; + ctokenize(s, cp); + if(cp->nargs == 0) + return -1; + /* strip leading sender name if present */ + cp->sender = nil; + i = strlen(cp->args[0])-1; + if(cp->args[0][i] == ':'){ + cp->sender = cp->args[0]; + cp->sender[i] = '\0'; + cp->args++; + cp->nargs--; + } + if(hasreceiver){ + if(cp->nargs-- == 0) + return -1; + cp->receiver = *cp->args++; + }else + cp->receiver = nil; + for(i=0; i<cp->nargs; i++){ + t = cp->args[i]; + while(*t == '[') /* %R gives [0 0] [1 1]; atoi will stop at closing ] */ + t++; + cp->iargs[i] = atoi(t); + } + return cp->nargs; +} + +void +_ctlargcount(Control *c, CParse *cp, int n) +{ + if(cp->nargs != n) + ctlerror("%q: wrong argument count in '%s'", c->name, cp->str); +} + +static void +_ctlcmd(Controlset *cs, char*s) +{ + CParse cp; + char *rcvrs[32]; + int ircvrs[32], n, i, hit; + Control *c; + +// fprint(2, "_ctlcmd: %s\n", s); + cp.args = cp.pargs; + if (ctlparse(&cp, s, 1) < 0) + ctlerror("bad command string: %q", cp.str); + if (cp.nargs == 0 && strcmp(cp.receiver, "sync") == 0){ + chanprint(cs->data, "sync"); + return; + } + if (cp.nargs == 0) + ctlerror("no command in command string: %q", cp.str); + + n = tokenize(cp.receiver, rcvrs, nelem(rcvrs)); + + // lookup type names: a receiver can be a named type or a named control + for (i = 0; i < n; i++) + ircvrs[i] = _ctllookup(rcvrs[i], ctltypenames, Ntypes); + + for(c = cs->controls; c != nil; c = c->next){ + /* if a control matches on more than one receiver element, + * make sure it gets processed once; hence loop through controls + * in the outer loop + */ + hit = 0; + for (i = 0; i < n; i++) + if(strcmp(c->name, rcvrs[i]) == 0 || c->type == ircvrs[i]) + hit++; + if (hit && c->ctl) + c->ctl(c, &cp); + } +} + +static void +_ctlcontrol(Controlset *cs, char *s) +{ + char *lines[16]; + int i, n; + char *l; + +// fprint(2, "_ctlcontrol: %s\n", s); + n = gettokens(s, lines, nelem(lines), "\n"); + for(i=0; i<n; i++){ + l = lines[i]; + while(*l==' ' || *l=='\t') + l++; + if(*l != '\0') + _ctlcmd(cs, l); + } +} + +Rune* +_ctlgetsnarf(void) +{ + int i, n; + char *sn, buf[512]; + Rune *snarf; + + if(_ctlsnarffd < 0) + return nil; + sn = nil; + i = 0; + seek(_ctlsnarffd, 0, 0); + while((n = read(_ctlsnarffd, buf, sizeof buf)) > 0){ + sn = ctlrealloc(sn, i+n+1); + memmove(sn+i, buf, n); + i += n; + sn[i] = 0; + } + snarf = nil; + if(i > 0){ + snarf = _ctlrunestr(sn); + free(sn); + } + return snarf; +} + +void +_ctlputsnarf(Rune *snarf) +{ + int fd, i, n, nsnarf; + + if(_ctlsnarffd<0 || snarf[0]==0) + return; + fd = open("/dev/snarf", OWRITE); + if(fd < 0) + return; + nsnarf = runestrlen(snarf); + /* snarf buffer could be huge, so fprint will truncate; do it in blocks */ + for(i=0; i<nsnarf; i+=n){ + n = nsnarf-i; + if(n >= 256) + n = 256; + if(fprint(fd, "%.*S", n, snarf+i) < 0) + break; + } + close(fd); +} + +int +_ctlalignment(char *s) +{ + int i; + + i = _ctllookup(s, alignnames, Nalignments); + if (i < 0) + ctlerror("unknown alignment: %s", s); + return i; +} + +Point +_ctlalignpoint(Rectangle r, int dx, int dy, int align) +{ + Point p; + + p = r.min; /* in case of trouble */ + switch(align%3){ + case 0: /* left */ + p.x = r.min.x; + break; + case 1: /* center */ + p.x = r.min.x+(Dx(r)-dx)/2; + break; + case 2: /* right */ + p.x = r.max.x-dx; + break; + } + switch((align/3)%3){ + case 0: /* top */ + p.y = r.min.y; + break; + case 1: /* center */ + p.y = r.min.y+(Dy(r)-dy)/2; + break; + case 2: /* bottom */ + p.y = r.max.y - dy; + break; + } + return p; +} + +void +controlwire(Control *cfrom, char *name, Channel *chan) +{ + Channel **p; + + p = nil; + if(strcmp(name, "event") == 0){ + p = &cfrom->event; + cfrom->wevent = 1; + }else if(strcmp(name, "data") == 0){ + p = &cfrom->data; + cfrom->wdata = 1; + }else + ctlerror("%q: unknown controlwire channel %s", cfrom->name, name); + chanfree(*p); + *p = chan; +} + +void +_ctlfocus(Control *me, int set) +{ + Controlset *cs; + + cs = me->controlset; + if(set){ + if(cs->focus == me) + return; + if(cs->focus != nil) + _ctlprint(cs->focus, "focus 0"); + cs->focus = me; + }else{ + if(cs->focus != me) + return; + cs->focus = nil; + } +} + +static void +resizethread(void *v) +{ + Controlset *cs; + char buf[64]; + Alt alts[3]; + + cs = v; + snprint(buf, sizeof buf, "resizethread0x%p", cs); + threadsetname(buf); + + alts[0].c = cs->resizec; + alts[0].v = nil; + alts[0].op = CHANRCV; + alts[1].c = cs->resizeexitc; + alts[1].v = nil; + alts[1].op = CHANRCV; + alts[2].op = CHANEND; + + for(;;){ + switch(alt(alts)){ + case 0: + resizecontrolset(cs); + break; + case 1: + return; + } + } +} + +void +activate(Control *a) +{ + Control *c; + + for(c=a->controlset->actives; c; c=c->nextactive) + if(c == a) + ctlerror("%q already active\n", a->name); + + if (a->activate){ + a->activate(a, 1); + return; + } + /* prepend */ + a->nextactive = a->controlset->actives; + a->controlset->actives = a; +} + +void +deactivate(Control *a) +{ + Control *c, *prev; + + /* if group, first deactivate kids, then self */ + if (a->activate){ + a->activate(a, 0); + return; + } + prev = nil; + for(c=a->controlset->actives; c; c=c->nextactive){ + if(c == a){ + if(a->controlset->focus == a) + a->controlset->focus = nil; + if(prev != nil) + prev->nextactive = a->nextactive; + else + a->controlset->actives = a->nextactive; + return; + } + prev = c; + } + ctlerror("%q not active\n", a->name); +} + +static struct +{ + char *name; + ulong color; +}coltab[] = { + "red", DRed, + "green", DGreen, + "blue", DBlue, + "cyan", DCyan, + "magenta", DMagenta, + "yellow", DYellow, + "paleyellow", DPaleyellow, + "darkyellow", DDarkyellow, + "darkgreen", DDarkgreen, + "palegreen", DPalegreen, + "medgreen", DMedgreen, + "darkblue", DDarkblue, + "palebluegreen", DPalebluegreen, + "paleblue", DPaleblue, + "bluegreen", DBluegreen, + "greygreen", DGreygreen, + "palegreygreen", DPalegreygreen, + "yellowgreen", DYellowgreen, + "medblue", DMedblue, + "greyblue", DGreyblue, + "palegreyblue", DPalegreyblue, + "purpleblue", DPurpleblue, + nil, 0 +}; + +void +initcontrols(void) +{ + int i; + Image *im; + + quotefmtinstall(); + namectlimage(display->opaque, "opaque"); + namectlimage(display->transparent, "transparent"); + namectlimage(display->white, "white"); + namectlimage(display->black, "black"); + for(i=0; coltab[i].name!=nil; i++){ + im = allocimage(display, Rect(0,0,1,1), RGB24, 1, coltab[i].color); + namectlimage(im, coltab[i].name); + } + namectlfont(font, "font"); + _ctlsnarffd = open("/dev/snarf", OREAD); +} + +Controlset* +newcontrolset(Image *im, Channel *kbdc, Channel *mousec, Channel *resizec) +{ + Controlset *cs; + + if(im == nil) + im = screen; + if((mousec==nil && resizec!=nil) || (mousec!=nil && resizec==nil)) + ctlerror("must specify either or both of mouse and resize channels"); + + cs = ctlmalloc(sizeof(Controlset)); + cs->screen = im; + + if(kbdc == nil){ + cs->keyboardctl = initkeyboard(nil); + if(cs->keyboardctl == nil) + ctlerror("can't initialize keyboard: %r"); + kbdc = cs->keyboardctl->c; + } + cs ->kbdc = kbdc; + + if(mousec == nil){ + cs->mousectl = initmouse(nil, im); + if(cs->mousectl == nil) + ctlerror("can't initialize mouse: %r"); + mousec = cs->mousectl->c; + resizec = cs->mousectl->resizec; + } + cs->mousec = mousec; + cs->resizec = resizec; + cs->ctl = chancreate(sizeof(char*), 64); /* buffer to prevent deadlock */ + cs->data = chancreate(sizeof(char*), 0); + cs->resizeexitc = chancreate(sizeof(int), 0); + cs->csexitc = chancreate(sizeof(int), 0); + + threadcreate(resizethread, cs, 32*1024); + threadcreate(controlsetthread, cs, 32*1024); + + controlset = ctlrealloc(controlset, (ncontrolset+1)*sizeof(Controlset*)); + controlset[ncontrolset++] = cs; + return cs; +} + +void +closecontrolset(Controlset *cs) +{ + int i; + + sendul(cs->resizeexitc, 0); + chanfree(cs->resizeexitc); + sendul(cs->csexitc, 0); + chanfree(cs->csexitc); + chanfree(cs->ctl); + chanfree(cs->data); + + for(i=0; i<ncontrolset; i++) + if(cs == controlset[i]){ + memmove(controlset+i, controlset+i+1, (ncontrolset-(i+1))*sizeof(Controlset*)); + ncontrolset--; + goto Found; + } + + if(i == ncontrolset) + ctlerror("closecontrolset: control set not found"); + + Found: + while(cs->controls != nil) + closecontrol(cs->controls); +} |