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 } 810