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