1 #include "lib9.h" 2 #include "draw.h" 3 #include "tk.h" 4 5 extern void rptwakeup(void*, void*); 6 extern void* rptproc(char*, int, void*, int (*)(void*), int (*)(void*,int), void (*)(void*)); 7 8 typedef struct Cmd Cmd; 9 struct Cmd 10 { 11 char* name; 12 char* (*fn)(TkTop*, char*, char**); 13 }; 14 static struct Cmd cmdmain[] = 15 { 16 "bind", tkbind, 17 "button", tkbutton, 18 "canvas", tkcanvas, 19 "checkbutton", tkcheckbutton, 20 "choicebutton", tkchoicebutton, 21 "cursor", tkcursorcmd, 22 "destroy", tkdestroy, 23 "entry", tkentry, 24 "focus", tkfocus, 25 "frame", tkframe, 26 "grab", tkgrab, 27 "grid", tkgrid, 28 "image", tkimage, 29 "label", tklabel, 30 "listbox", tklistbox, 31 "lower", tklower, 32 "menu", tkmenu, 33 "menubutton", tkmenubutton, 34 "pack", tkpack, 35 "panel", tkpanel, 36 "puts", tkputs, 37 "radiobutton", tkradiobutton, 38 "raise", tkraise, 39 "scale", tkscale, 40 "scrollbar", tkscrollbar, 41 "see", tkseecmd, 42 "send", tksend, 43 "text", tktext, 44 "update", tkupdatecmd, 45 "variable", tkvariable, 46 "winfo", tkwinfo, 47 }; 48 49 char* tkfont; 50 51 /* auto-repeating support 52 * should perhaps be one rptproc per TkCtxt 53 * This is not done for the moment as there isn't 54 * a mechanism for terminating the rptproc 55 */ 56 static void *autorpt; 57 static int rptid; 58 static Tk *rptw; 59 static void *rptnote; 60 static void (*rptcb)(Tk*, void*, int); 61 static long rptto; 62 static int rptint; 63 64 /* blinking carets - should be per TkCtxt */ 65 static void *blinkrpt; 66 static Tk *blinkw; 67 static void (*blinkcb)(Tk*, int); 68 static int blinkignore; 69 static int blinkon; 70 71 72 ulong 73 tkrgba(int r, int g, int b, int a) 74 { 75 ulong p; 76 77 if(r < 0) 78 r = 0; 79 else if(r > 255) 80 r = 255; 81 if(g < 0) 82 g = 0; 83 else if(g > 255) 84 g = 255; 85 if(b < 0) 86 b = 0; 87 else if(b > 255) 88 b = 255; 89 p = (r<<24)|(g<<16)|(b<<8)|0xFF; 90 if(a == 255) 91 return p; 92 return setalpha(p, a); 93 } 94 95 /* to be replaced */ 96 static int 97 revalpha(int c, int a) 98 { 99 if (a == 0) 100 return 0; 101 return (c & 0xff) * 255 / a; 102 } 103 104 void 105 tkrgbavals(ulong rgba, int *R, int *G, int *B, int *A) 106 { 107 int a; 108 109 a = rgba & 0xff; 110 *A = a; 111 if (a != 0xff) { 112 *R = revalpha(rgba>>24, a); 113 *G = revalpha((rgba>>16) & 0xFF, a); 114 *B = revalpha((rgba >> 8) & 0xFF, a); 115 } else { 116 *R = (rgba>>24); 117 *G = ((rgba>>16) & 0xFF); 118 *B = ((rgba >> 8) & 0xFF); 119 } 120 } 121 122 void 123 tkfreecolcache(TkCtxt *c) 124 { 125 TkCol *cc; 126 127 if(c == nil) 128 return; 129 while((cc = c->chead) != nil){ 130 c->chead = cc->forw; 131 freeimage(cc->i); 132 free(cc); 133 } 134 c->ctail = nil; 135 c->ncol = 0; 136 } 137 138 Image* 139 tkcolor(TkCtxt *c, ulong pix) 140 { 141 Image *i; 142 TkCol *cc, **l; 143 Display *d; 144 Rectangle r; 145 146 for(l = &c->chead; (cc = *l) != nil; l = &cc->forw) 147 if(cc->rgba == pix){ 148 /* move it up in the list */ 149 *l = cc->forw; 150 cc->forw = c->chead; 151 c->chead = cc; 152 /* we assume it will be used right away and not stored */ 153 return cc->i; 154 } 155 d = c->display; 156 if(pix == DWhite) 157 return d->white; 158 if(pix == DBlack) 159 return d->black; 160 r.min = ZP; 161 r.max.x = 1; 162 r.max.y = 1; 163 if ((pix & 0xff) == 0xff) 164 i = allocimage(d, r, RGB24, 1, pix); 165 else 166 i = allocimage(d, r, RGBA32, 1, pix); 167 if(i == nil) 168 return d->black; 169 cc = malloc(sizeof(*cc)); 170 if(cc == nil){ 171 freeimage(i); 172 return d->black; 173 } 174 cc->rgba = pix; 175 cc->i = i; 176 cc->forw = c->chead; 177 c->chead = cc; 178 c->ncol++; 179 /* we'll do LRU management at some point */ 180 if(c->ncol > TkColcachesize){ 181 static int warn; 182 if(warn == 0){ 183 warn = 1; 184 print("tk: %d colours cached\n", TkColcachesize); 185 } 186 } 187 return i; 188 } 189 190 /* 191 * XXX should be in libdraw? 192 */ 193 int 194 tkchanhastype(ulong c, int t) 195 { 196 for(; c; c>>=8) 197 if(TYPE(c) == t) 198 return 1; 199 return 0; 200 } 201 202 void 203 tksettransparent(Tk *tk, int transparent) 204 { 205 if (transparent) 206 tk->flag |= Tktransparent; 207 else 208 tk->flag &= ~Tktransparent; 209 } 210 211 int 212 tkhasalpha(TkEnv *e, int col) 213 { 214 return (e->colors[col] & 0xff) != 0xff; 215 } 216 217 Image* 218 tkgc(TkEnv *e, int col) 219 { 220 return tkcolor(e->top->ctxt, e->colors[col]); 221 } 222 223 224 /* 225 * Todo: improve the fixed-point code 226 * the 255 scale factor is used because RGB ranges 0-255 227 */ 228 static void 229 rgb2hsv(int r, int g, int b, int *h, int *s, int *v) 230 { 231 int min, max, delta; 232 233 max = r; 234 if(g > max) 235 max = g; 236 if(b > max) 237 max = b; 238 min = r; 239 if(g < min) 240 min = g; 241 if(b < min) 242 min = b; 243 *v = max; 244 if (max != 0) 245 *s = ((max - min)*255) / max; 246 else 247 *s = 0; 248 249 if (*s == 0) { 250 *h = 0; /* undefined */ 251 } else { 252 delta = max - min; 253 if (r == max) 254 *h = (g - b)*255 / delta; 255 else if (g == max) 256 *h = (2*255) + ((b - r)*255) / delta; 257 else if (b == max) 258 *h = (4*255) + ((r - g)*255)/ delta; 259 *h *= 60; 260 if (*h < 0) 261 *h += 360*255; 262 *h /= 255; 263 } 264 } 265 266 static void 267 hsv2rgb(int h, int s, int v, int *r, int *g, int *b) 268 { 269 int i; 270 int f,p,q,t; 271 272 if (s == 0 && h == 0) { 273 *r = *g = *b = v; /* achromatic case */ 274 } else { 275 if (h >= 360) 276 h = 0; 277 i = h / 60; 278 h *= 255; 279 h /= 60; 280 281 f = h % 255; 282 p = v * (255 - s); 283 q = v * (255 - ((s * f)/255)); 284 t = v * (255- ((s * (255 - f))/255)); 285 p /= 255; 286 q /= 255; 287 t /= 255; 288 switch (i) { 289 case 0: *r = v; *g = t; *b = p; break; 290 case 1: *r = q; *g = v; *b = p; break; 291 case 2: *r = p; *g = v; *b = t; break; 292 case 3: *r = p; *g = q; *b = v; break; 293 case 4: *r = t; *g = p; *b = v; break; 294 case 5: *r = v; *g = p; *b = q; break; 295 } 296 } 297 } 298 299 enum { 300 MINDELTA = 0x10, 301 DELTA = 0x30, 302 }; 303 304 ulong 305 tkrgbashade(ulong rgba, int shade) 306 { 307 int R, G, B, A, h, s, v, vl, vd; 308 309 if (shade == TkSameshade) 310 return rgba; 311 312 tkrgbavals(rgba, &R, &G, &B, &A); 313 rgb2hsv(R, G, B, &h, &s, &v); 314 315 if (v < MINDELTA) { 316 vd = v+DELTA; 317 vl = vd+DELTA; 318 } else if (v > 255-MINDELTA) { 319 vl = v-DELTA; 320 vd = vl-DELTA; 321 } else { 322 vl = v+DELTA; 323 vd = v-DELTA; 324 } 325 326 v = (shade == TkLightshade)?vl:vd; 327 if (v < 0) 328 v = 0; 329 if (v > 255) 330 v = 255; 331 hsv2rgb(h, s, v, &R, &G, &B); 332 333 return tkrgba(R, G, B, A); 334 } 335 336 Image* 337 tkgshade(TkEnv *e, int col, int shade) 338 { 339 ulong rgba; 340 341 if (col == TkCbackgnd || col == TkCselectbgnd || col == TkCactivebgnd) 342 return tkgc(e, col+shade); 343 rgba = tkrgbashade(e->colors[col], shade); 344 return tkcolor(e->top->ctxt, rgba); 345 } 346 347 TkEnv* 348 tknewenv(TkTop *t) 349 { 350 TkEnv *e; 351 352 e = malloc(sizeof(TkEnv)); 353 if(e == nil) 354 return nil; 355 356 e->ref = 1; 357 e->top = t; 358 return e; 359 } 360 361 TkEnv* 362 tkdefaultenv(TkTop *t) 363 { 364 int locked; 365 TkEnv *env; 366 Display *d; 367 368 if(t->env != nil) { 369 t->env->ref++; 370 return t->env; 371 } 372 t->env = malloc(sizeof(TkEnv)); 373 if(t->env == nil) 374 return nil; 375 376 env = t->env; 377 env->ref = 1; 378 env->top = t; 379 380 if(tkfont == nil) 381 tkfont = "/fonts/pelm/unicode.8.font"; 382 383 d = t->display; 384 env->font = font_open(d, tkfont); 385 if(env->font == nil) { 386 static int warn; 387 if(warn == 0) { 388 warn = 1; 389 print("tk: font not found: %s\n", tkfont); 390 } 391 env->font = font_open(d, "*default*"); 392 if(env->font == nil) { 393 free(t->env); 394 t->env = nil; 395 return nil; 396 } 397 } 398 399 locked = lockdisplay(d); 400 env->wzero = stringwidth(env->font, "0"); 401 if ( env->wzero <= 0 ) 402 env->wzero = env->font->height / 2; 403 if(locked) 404 unlockdisplay(d); 405 406 tksetenvcolours(env); 407 return env; 408 } 409 410 void 411 tkputenv(TkEnv *env) 412 { 413 Display *d; 414 int locked; 415 416 if(env == nil) 417 return; 418 419 env->ref--; 420 if(env->ref != 0) 421 return; 422 423 d = env->top->display; 424 locked = lockdisplay(d); 425 426 if(env->font != nil) 427 font_close(env->font); 428 429 if(locked) 430 unlockdisplay(d); 431 432 free(env); 433 } 434 435 TkEnv* 436 tkdupenv(TkEnv **env) 437 { 438 Display *d; 439 TkEnv *e, *ne; 440 441 e = *env; 442 if(e->ref == 1) 443 return e; 444 445 ne = malloc(sizeof(TkEnv)); 446 if(ne == nil) 447 return nil; 448 449 ne->ref = 1; 450 ne->top = e->top; 451 452 d = e->top->display; 453 memmove(ne->colors, e->colors, sizeof(e->colors)); 454 ne->set = e->set; 455 ne->font = font_open(d, e->font->name); 456 ne->wzero = e->wzero; 457 458 e->ref--; 459 *env = ne; 460 return ne; 461 } 462 463 Tk* 464 tknewobj(TkTop *t, int type, int n) 465 { 466 Tk *tk; 467 468 tk = malloc(n); 469 if(tk == 0) 470 return 0; 471 472 tk->type = type; /* Defaults */ 473 tk->flag = Tktop; 474 tk->relief = TKflat; 475 tk->env = tkdefaultenv(t); 476 if(tk->env == nil) { 477 free(tk); 478 return nil; 479 } 480 481 return tk; 482 } 483 484 void 485 tkfreebind(TkAction *a) 486 { 487 TkAction *next; 488 489 while(a != nil) { 490 next = a->link; 491 if((a->type & 0xff) == TkDynamic) 492 free(a->arg); 493 free(a); 494 a = next; 495 } 496 } 497 498 void 499 tkfreename(TkName *f) 500 { 501 TkName *n; 502 503 while(f != nil) { 504 n = f->link; 505 free(f); 506 f = n; 507 } 508 } 509 510 void 511 tkfreeobj(Tk *tk) 512 { 513 TkCtxt *c; 514 515 c = tk->env->top->ctxt; 516 if(c != nil) { 517 if(c->tkkeygrab == tk) 518 c->tkkeygrab = nil; 519 if(c->mgrab == tk) 520 tksetmgrab(tk->env->top, nil); 521 if(c->mfocus == tk) 522 c->mfocus = nil; 523 if(c->entered == tk) 524 c->entered = nil; 525 } 526 527 if (tk == rptw) { 528 /* cancel the autorepeat without notifying the widget */ 529 rptid++; 530 rptw = nil; 531 } 532 if (tk == blinkw) 533 blinkw = nil; 534 tkextnfreeobj(tk); 535 tkmethod[tk->type]->free(tk); 536 tkputenv(tk->env); 537 tkfreebind(tk->binds); 538 if(tk->name != nil) 539 free(tk->name); 540 free(tk); 541 } 542 543 char* 544 tkaddchild(TkTop *t, Tk *tk, TkName **names) 545 { 546 TkName *n; 547 Tk *f, **l; 548 int found, len; 549 char *s, *ep; 550 551 n = *names; 552 if(n == nil || n->name[0] != '.'){ 553 if(n != nil) 554 tkerr(t, n->name); 555 return TkBadwp; 556 } 557 558 if (n->name[1] == '\0') 559 return TkDupli; 560 561 /* 562 * check that the name is well-formed. 563 * ep will point to end of parent component of the name. 564 */ 565 ep = nil; 566 for (s = n->name + 1; *s; s++) { 567 if (*s == '.'){ 568 tkerr(t, n->name); 569 return TkBadwp; 570 } 571 for (; *s && *s != '.'; s++) 572 ; 573 if (*s == '\0') 574 break; 575 ep = s; 576 } 577 if (ep == s - 1){ 578 tkerr(t, n->name); 579 return TkBadwp; 580 } 581 if (ep == nil) 582 ep = n->name + 1; 583 len = ep - n->name; 584 585 found = 0; 586 l = &t->root; 587 for(f = *l; f; f = f->siblings) { 588 if (f->name != nil) { 589 if (strcmp(n->name, f->name->name) == 0) 590 return TkDupli; 591 if (!found && 592 strncmp(n->name, f->name->name, len) == 0 && 593 f->name->name[len] == '\0') 594 found = 1; 595 } 596 l = &f->siblings; 597 } 598 if (0) { /* don't enable this until a reasonably major release... if ever */ 599 /* 600 * parent widget must already exist 601 */ 602 if (!found){ 603 tkerr(t, n->name); 604 return TkBadwp; 605 } 606 } 607 *l = tk; 608 tk->name = n; 609 *names = n->link; 610 611 return nil; 612 } 613 614 Tk* 615 tklook(TkTop *t, char *wp, int parent) 616 { 617 Tk *f; 618 char *p, *q; 619 620 if(wp == nil) 621 return nil; 622 623 if(parent) { 624 p = strdup(wp); 625 if(p == nil) 626 return nil; 627 q = strrchr(p, '.'); 628 if(q == nil) 629 abort(); 630 if(q == p) { 631 free(p); 632 return t->root; 633 } 634 *q = '\0'; 635 } else 636 p = wp; 637 638 for(f = t->root; f; f = f->siblings) 639 if ((f->name != nil) && (strcmp(f->name->name, p) == 0)) 640 break; 641 642 if(f != nil && (f->flag & Tkdestroy)) 643 f = nil; 644 645 if (parent) 646 free(p); 647 return f; 648 } 649 650 void 651 tktextsdraw(Image *img, Rectangle r, TkEnv *e, int sbw) 652 { 653 Image *l, *d; 654 Rectangle s; 655 656 draw(img, r, tkgc(e, TkCselectbgnd), nil, ZP); 657 s.min = r.min; 658 s.min.x -= sbw; 659 s.min.y -= sbw; 660 s.max.x = r.max.x; 661 s.max.y = r.min.y; 662 l = tkgc(e, TkCselectbgndlght); 663 draw(img, s, l, nil, ZP); 664 s.max.x = s.min.x + sbw; 665 s.max.y = r.max.y + sbw; 666 draw(img, s, l, nil, ZP); 667 s.max = r.max; 668 s.max.x += sbw; 669 s.max.y += sbw; 670 s.min.x = r.min.x; 671 s.min.y = r.max.y; 672 d = tkgc(e, TkCselectbgnddark); 673 draw(img, s, d, nil, ZP); 674 s.min.x = r.max.x; 675 s.min.y = r.min.y - sbw; 676 draw(img, s, d, nil, ZP); 677 } 678 679 void 680 tkbox(Image *i, Rectangle r, int bd, Image *fill) 681 { 682 if (bd > 0) { 683 draw(i, Rect(r.min.x, r.min.y, r.max.x, r.min.y+bd), fill, nil, ZP); 684 draw(i, Rect(r.min.x, r.min.y+bd, r.min.x+bd, r.max.y-bd), fill, nil, ZP); 685 draw(i, Rect(r.min.x, r.max.y-bd, r.max.x, r.max.y), fill, nil, ZP); 686 draw(i, Rect(r.max.x-bd, r.min.y+bd, r.max.x, r.max.y), fill, nil, ZP); 687 } 688 } 689 690 void 691 tkbevel(Image *i, Point o, int w, int h, int bw, Image *top, Image *bottom) 692 { 693 Rectangle r; 694 int x, border; 695 696 border = 2 * bw; 697 698 r.min = o; 699 r.max.x = r.min.x + w + border; 700 r.max.y = r.min.y + bw; 701 draw(i, r, top, nil, ZP); 702 703 r.max.x = r.min.x + bw; 704 r.max.y = r.min.y + h + border; 705 draw(i, r, top, nil, ZP); 706 707 r.max.x = o.x + w + border; 708 r.max.y = o.y + h + border; 709 r.min.x = o.x + bw; 710 r.min.y = r.max.y - bw; 711 for(x = 0; x < bw; x++) { 712 draw(i, r, bottom, nil, ZP); 713 r.min.x--; 714 r.min.y++; 715 } 716 r.min.x = o.x + bw + w; 717 r.min.y = o.y + bw; 718 for(x = bw; x >= 0; x--) { 719 draw(i, r, bottom, nil, ZP); 720 r.min.x++; 721 r.min.y--; 722 } 723 } 724 725 /* 726 * draw a relief border. 727 * color is an index into tk->env->colors and assumes 728 * light and dark versions following immediately after 729 * that index 730 */ 731 void 732 tkdrawrelief(Image *i, Tk *tk, Point o, int color, int rlf) 733 { 734 TkEnv *e; 735 Image *l, *d, *t; 736 int h, w, bd, bd1, bd2; 737 738 if(tk->borderwidth == 0) 739 return; 740 741 h = tk->act.height; 742 w = tk->act.width; 743 744 e = tk->env; 745 if (color == TkCbackgnd || color == TkCselectbgnd || color == TkCactivebgnd) { 746 l = tkgc(e, color+TkLightshade); 747 d = tkgc(e, color+TkDarkshade); 748 } else { 749 l = tkgshade(e, color, TkLightshade); 750 d = tkgshade(e, color, TkDarkshade); 751 } 752 bd = tk->borderwidth; 753 if(rlf < 0) 754 rlf = TKraised; 755 switch(rlf) { 756 case TKflat: 757 break; 758 case TKsunken: 759 tkbevel(i, o, w, h, bd, d, l); 760 break; 761 case TKraised: 762 tkbevel(i, o, w, h, bd, l, d); 763 break; 764 case TKgroove: 765 t = d; 766 d = l; 767 l = t; 768 /* fall through */ 769 case TKridge: 770 bd1 = bd/2; 771 bd2 = bd - bd1; 772 if(bd1 > 0) 773 tkbevel(i, o, w + 2*bd2, h + 2*bd2, bd1, l, d); 774 o.x += bd1; 775 o.y += bd1; 776 tkbevel(i, o, w, h, bd2, d, l); 777 break; 778 } 779 } 780 781 Point 782 tkstringsize(Tk *tk, char *text) 783 { 784 char *q; 785 int locked; 786 Display *d; 787 Point p, t; 788 789 if(text == nil) { 790 p.x = 0; 791 p.y = tk->env->font->height; 792 return p; 793 } 794 795 d = tk->env->top->display; 796 locked = lockdisplay(d); 797 798 p = ZP; 799 while(*text) { 800 q = strchr(text, '\n'); 801 if(q != nil) 802 *q = '\0'; 803 t = stringsize(tk->env->font, text); 804 p.y += t.y; 805 if(p.x < t.x) 806 p.x = t.x; 807 if(q == nil) 808 break; 809 text = q+1; 810 *q = '\n'; 811 } 812 if(locked) 813 unlockdisplay(d); 814 815 return p; 816 } 817 818 static char* 819 tkul(Image *i, Point o, Image *col, int ul, Font *f, char *text) 820 { 821 char c, *v; 822 Rectangle r; 823 824 v = text+ul+1; 825 c = *v; 826 *v = '\0'; 827 r.max = stringsize(f, text); 828 r.max = addpt(r.max, o); 829 r.min = stringsize(f, v-1); 830 *v = c; 831 r.min.x = r.max.x - r.min.x; 832 r.min.y = r.max.y - 1; 833 r.max.y += 2; 834 draw(i, r, col, nil, ZP); 835 836 return nil; 837 } 838 839 char* 840 tkdrawstring(Tk *tk, Image *i, Point o, char *text, int ul, Image *col, int j) 841 { 842 int n, l, maxl, sox; 843 char *q, *txt; 844 Point p; 845 TkEnv *e; 846 847 e = tk->env; 848 sox = maxl = 0; 849 if(j != Tkleft){ 850 maxl = 0; 851 txt = text; 852 while(*txt){ 853 q = strchr(txt, '\n'); 854 if(q != nil) 855 *q = '\0'; 856 l = stringwidth(e->font, txt); 857 if(l > maxl) 858 maxl = l; 859 if(q == nil) 860 break; 861 txt = q+1; 862 *q = '\n'; 863 } 864 sox = o.x; 865 } 866 while(*text) { 867 q = strchr(text, '\n'); 868 if(q != nil) 869 *q = '\0'; 870 if(j != Tkleft){ 871 o.x = sox; 872 l = stringwidth(e->font, text); 873 if(j == Tkcenter) 874 o.x += (maxl-l)/2; 875 else 876 o.x += maxl-l; 877 } 878 p = string(i, o, col, o, e->font, text); 879 if(ul >= 0) { 880 n = strlen(text); 881 if(ul < n) { 882 char *r; 883 884 r = tkul(i, o, col, ul, e->font, text); 885 if(r != nil) 886 return r; 887 ul = -1; 888 } 889 ul -= n; 890 } 891 o.y += e->font->height; 892 if(q == nil) 893 break; 894 text = q+1; 895 *q = '\n'; 896 } 897 return nil; 898 } 899 900 /* for debugging */ 901 char* 902 tkname(Tk *tk) 903 { 904 return tk ? (tk->name ? tk->name->name : "(noname)") : "(nil)"; 905 } 906 907 Tk* 908 tkdeliver(Tk *tk, int event, void *data) 909 { 910 Tk *dest; 911 //print("tkdeliver %v to %s\n", event, tkname(tk)); 912 if(tk == nil || ((tk->flag&Tkdestroy) && event != TkDestroy)) 913 return tk; 914 915 if(event&(TkFocusin|TkFocusout) && (tk->flag&Tktakefocus)) 916 tk->dirty = tkrect(tk, 1); 917 918 if (tkmethod[tk->type]->deliver != nil) { 919 dest = tkmethod[tk->type]->deliver(tk, event, data); 920 if (dest == nil) 921 return tk; 922 tkdirty(tk); 923 return dest; 924 } 925 926 if((tk->flag & Tkdisabled) == 0) 927 tksubdeliver(tk, tk->binds, event, data, 0); 928 tkdirty(tk); 929 return tk; 930 } 931 932 static int 933 nullop(char *fmt, ...) 934 { 935 USED(fmt); 936 return 0; 937 } 938 939 int 940 tksubdeliver(Tk *tk, TkAction *binds, int event, void *data, int extn) 941 { 942 943 TkAction *a; 944 int delivered, genkey, delivered2, iskey; 945 //int (*debug)(char *fmt, ...); 946 if (!extn) 947 return tkextndeliver(tk, binds, event, data); 948 949 //debug = (tk->name && !strcmp(tk->name->name, ".cd")) ? print : nullop; 950 //debug("subdeliver %v\n", event); 951 952 if (event & TkTakefocus) { 953 if (tk->flag & Tktakefocus) 954 tksetkeyfocus(tk->env->top, tk, 0); 955 return TkDdelivered; 956 } 957 958 delivered = TkDnone; 959 genkey = 0; 960 for(a = binds; a != nil; a = a->link) { 961 if(event == a->event) { 962 //debug(" exact match on %v\n", a->event); 963 tkcmdbind(tk, event, a->arg, data); 964 delivered = TkDdelivered; 965 } else if (a->event == TkKey && (a->type>>8)==TkAadd) 966 genkey = 1; 967 } 968 if(delivered != TkDnone && !((event & TkKey) && genkey)) 969 return delivered; 970 971 delivered2 = delivered; 972 for(a = binds; a != nil; a = a->link) { 973 /* 974 * only bind to non-specific key events; if a specific 975 * key event has already been delivered, only deliver event if 976 * the non-specific binding was added. (TkAadd) 977 */ 978 if (a->event & TkExtns) 979 continue; 980 iskey = (a->event & TkKey); 981 if (iskey ^ (event & TkKey)) 982 continue; 983 if(iskey && (TKKEY(a->event) != 0 984 || ((a->type>>8) != TkAadd && delivered != TkDnone))) 985 continue; 986 if(!iskey && (a->event & TkMotion) && (a->event&TkEpress) != 0) 987 continue; 988 if(!(event & TkDouble) && (a->event & TkDouble)) 989 continue; 990 if((event & ~TkDouble) & a->event) { 991 //debug(" partial match on %v\n", a->event); 992 tkcmdbind(tk, event, a->arg, data); 993 delivered2 = TkDdelivered; 994 } 995 } 996 return delivered2; 997 } 998 999 void 1000 tkcancel(TkAction **l, int event) 1001 { 1002 TkAction *a; 1003 1004 for(a = *l; a; a = *l) { 1005 if(a->event == event) { 1006 *l = a->link; 1007 a->link = nil; 1008 tkfreebind(a); 1009 continue; 1010 } 1011 l = &a->link; 1012 } 1013 } 1014 1015 static void 1016 tkcancela(TkAction **l, int event, int type, char *arg) 1017 { 1018 TkAction *a; 1019 1020 for(a = *l; a; a = *l) { 1021 if(a->event == event && strcmp(a->arg, arg) == 0 && (a->type&0xff) == type){ 1022 *l = a->link; 1023 a->link = nil; 1024 tkfreebind(a); 1025 continue; 1026 } 1027 l = &a->link; 1028 } 1029 } 1030 1031 char* 1032 tkaction(TkAction **l, int event, int type, char *arg, int how) 1033 { 1034 TkAction *a; 1035 1036 if(arg == nil) 1037 return nil; 1038 if(how == TkArepl) 1039 tkcancel(l, event); 1040 else if(how == TkAadd){ 1041 for(a = *l; a; a = a->link) 1042 if(a->event == event && strcmp(a->arg, arg) == 0 && (a->type&0xff) == type){ 1043 a->type = type + (how << 8); 1044 return nil; 1045 } 1046 } 1047 else if(how == TkAsub){ 1048 tkcancela(l, event, type, arg); 1049 if(type == TkDynamic) /* should always be the case */ 1050 free(arg); 1051 return nil; 1052 } 1053 1054 a = malloc(sizeof(TkAction)); 1055 if(a == nil) { 1056 if(type == TkDynamic) 1057 free(arg); 1058 return TkNomem; 1059 } 1060 1061 a->event = event; 1062 a->arg = arg; 1063 a->type = type + (how << 8); 1064 1065 a->link = *l; 1066 *l = a; 1067 1068 return nil; 1069 } 1070 1071 char* 1072 tkitem(char *buf, char *a) 1073 { 1074 char *e; 1075 1076 while(*a && (*a == ' ' || *a == '\t')) 1077 a++; 1078 1079 e = buf + Tkmaxitem - 1; 1080 while(*a && *a != ' ' && *a != '\t' && buf < e) 1081 *buf++ = *a++; 1082 1083 *buf = '\0'; 1084 while(*a && (*a == ' ' || *a == '\t')) 1085 a++; 1086 return a; 1087 } 1088 1089 int 1090 tkismapped(Tk *tk) 1091 { 1092 while(tk->master) 1093 tk = tk->master; 1094 1095 /* We need subwindows of text & canvas to appear mapped always 1096 * so that the geom function update are seen by the parent 1097 * widget 1098 */ 1099 if((tk->flag & Tkwindow) == 0) 1100 return 1; 1101 1102 return tk->flag & Tkmapped; 1103 } 1104 1105 /* 1106 * Return absolute screen position of tk (just outside its top-left border). 1107 * When a widget is embedded in a text or canvas widget, we need to 1108 * use the text or canvas's relpos() function instead of act{x,y}, and we 1109 * need to folow up the parent pointer rather than the master one. 1110 */ 1111 Point 1112 tkposn(Tk *tk) 1113 { 1114 Tk *f, *last; 1115 Point g; 1116 1117 last = tk; 1118 if(tk->parent != nil) { 1119 g = tkmethod[tk->parent->type]->relpos(tk); 1120 f = tk->parent; 1121 } 1122 else { 1123 g.x = tk->act.x; 1124 g.y = tk->act.y; 1125 f = tk->master; 1126 } 1127 while(f) { 1128 g.x += f->borderwidth; 1129 g.y += f->borderwidth; 1130 last = f; 1131 if(f->parent != nil) { 1132 g = addpt(g, tkmethod[f->parent->type]->relpos(f)); 1133 f = f->parent; 1134 } 1135 else { 1136 g.x += f->act.x; 1137 g.y += f->act.y; 1138 f = f->master; 1139 } 1140 } 1141 if (last->flag & Tkwindow) 1142 g = addpt(g, TKobj(TkWin, last)->req); 1143 return g; 1144 } 1145 1146 /* 1147 * convert screen coords to local widget coords 1148 */ 1149 Point 1150 tkscrn2local(Tk *tk, Point p) 1151 { 1152 p = subpt(p, tkposn(tk)); 1153 p.x -= tk->borderwidth; 1154 p.y -= tk->borderwidth; 1155 return p; 1156 } 1157 1158 int 1159 tkvisiblerect(Tk *tk, Rectangle *rr) 1160 { 1161 Rectangle r; 1162 Point g; 1163 Tk *f, *last; 1164 g = Pt(tk->borderwidth, tk->borderwidth); 1165 last = tk; 1166 if(tk->parent != nil) { 1167 g = addpt(g, tkmethod[tk->parent->type]->relpos(tk)); 1168 f = tk->parent; 1169 } else { 1170 g.x += tk->act.x; 1171 g.y += tk->act.y; 1172 f = tk->master; 1173 } 1174 if (f == nil) { 1175 *rr = tkrect(tk, 1); 1176 return 1; 1177 } 1178 r = rectaddpt(tkrect(tk, 1), g); 1179 while (f) { 1180 if (!rectclip(&r, tkrect(f, 0))) 1181 return 0; 1182 g.x = f->borderwidth; 1183 g.y = f->borderwidth; 1184 last = f; 1185 if (f->parent != nil) { 1186 g = addpt(g, tkmethod[f->parent->type]->relpos(f)); 1187 f = f->parent; 1188 } else { 1189 g.x += f->act.x; 1190 g.y += f->act.y; 1191 f = f->master; 1192 } 1193 r = rectaddpt(r, g); 1194 } 1195 if (last->flag & Tkwindow) 1196 r = rectaddpt(r, TKobj(TkWin, last)->act); 1197 /* 1198 * now we have the visible rectangle in screen coords; 1199 * subtract actx+borderwidth and we've got it back in 1200 * widget-local coords again 1201 */ 1202 r = rectsubpt(r, tkposn(tk)); 1203 *rr = rectsubpt(r, Pt(tk->borderwidth, tk->borderwidth)); 1204 return 1; 1205 } 1206 1207 Point 1208 tkanchorpoint(Rectangle r, Point size, int anchor) 1209 { 1210 int dx, dy; 1211 Point p; 1212 1213 p = r.min; 1214 dx = Dx(r) - size.x; 1215 dy = Dy(r) - size.y; 1216 if((anchor & (Tknorth|Tksouth)) == 0) 1217 p.y += dy/2; 1218 else 1219 if(anchor & Tksouth) 1220 p.y += dy; 1221 1222 if((anchor & (Tkeast|Tkwest)) == 0) 1223 p.x += dx/2; 1224 else 1225 if(anchor & Tkeast) 1226 p.x += dx; 1227 return p; 1228 } 1229 1230 static char* 1231 tkunits(char c, int *d, TkEnv *e) 1232 { 1233 switch(c) { 1234 default: 1235 if(c >= '0' || c <= '9' || c == '.') 1236 break; 1237 return TkBadvl; 1238 case '\0': 1239 break; 1240 case 'c': /* Centimeters */ 1241 *d *= (Tkdpi*100)/254; 1242 break; 1243 case 'm': /* Millimeters */ 1244 *d *= (Tkdpi*10)/254; 1245 break; 1246 case 'i': /* Inches */ 1247 *d *= Tkdpi; 1248 break; 1249 case 'p': /* Points */ 1250 *d = (*d*Tkdpi)/72; 1251 break; 1252 case 'w': /* Character width */ 1253 if(e == nil) 1254 return TkBadvl; 1255 *d = *d * e->wzero; 1256 break; 1257 case 'h': /* Character height */ 1258 if(e == nil) 1259 return TkBadvl; 1260 *d = *d * e->font->height; 1261 break; 1262 } 1263 return nil; 1264 } 1265 1266 int 1267 TKF2I(int f) 1268 { 1269 if (f >= 0) 1270 return (f + Tkfpscalar/2) / Tkfpscalar; 1271 return (f - Tkfpscalar/2) / Tkfpscalar; 1272 } 1273 1274 /* 1275 * Parse a floating point number into a decimal fixed point representation 1276 */ 1277 char* 1278 tkfrac(char **arg, int *f, TkEnv *env) 1279 { 1280 int c, minus, i, fscale, seendigit; 1281 char *p, *e; 1282 1283 seendigit = 0; 1284 1285 p = *arg; 1286 p = tkskip(p, " \t"); 1287 1288 minus = 0; 1289 if(*p == '-') { 1290 minus = 1; 1291 p++; 1292 } 1293 i = 0; 1294 while(*p) { 1295 c = *p; 1296 if(c == '.') 1297 break; 1298 if(c < '0' || c > '9') 1299 break; 1300 i = i*10 + (c - '0'); 1301 seendigit = 1; 1302 p++; 1303 } 1304 i *= Tkfpscalar; 1305 if(*p == '.') 1306 p++; 1307 fscale = Tkfpscalar; 1308 while(*p && *p >= '0' && *p <= '9') { 1309 fscale /= 10; 1310 i += fscale * (*p++ - '0'); 1311 seendigit = 1; 1312 } 1313 1314 if(minus) 1315 i = -i; 1316 1317 if(!seendigit) 1318 return TkBadvl; 1319 e = tkunits(*p, &i, env); 1320 if (e != nil) 1321 return e; 1322 while (*p && *p != ' ' && *p != '\t') 1323 p++; 1324 *arg = p; 1325 *f = i; 1326 return nil; 1327 } 1328 1329 char* 1330 tkfracword(TkTop *t, char **arg, int *f, TkEnv *env) 1331 { 1332 char *p; 1333 char buf[Tkminitem]; 1334 1335 *arg = tkword(t, *arg, buf, buf+sizeof(buf), nil); 1336 p = buf; 1337 return tkfrac(&p, f, env); 1338 } 1339 1340 char* 1341 tkfprint(char *v, int frac) 1342 { 1343 int fscale; 1344 1345 if(frac < 0) { 1346 *v++ = '-'; 1347 frac = -frac; 1348 } 1349 v += sprint(v, "%d", frac/Tkfpscalar); 1350 frac = frac%Tkfpscalar; 1351 if(frac != 0) 1352 *v++ = '.'; 1353 fscale = Tkfpscalar/10; 1354 while(frac) { 1355 *v++ = '0' + frac/fscale; 1356 frac %= fscale; 1357 fscale /= 10; 1358 } 1359 *v = '\0'; 1360 return v; 1361 } 1362 1363 char* 1364 tkvalue(char **val, char *fmt, ...) 1365 { 1366 va_list arg; 1367 Fmt fmtx; 1368 1369 if(val == nil) 1370 return nil; 1371 1372 fmtstrinit(&fmtx); 1373 if(*val != nil) 1374 if(fmtprint(&fmtx, "%s", *val) < 0) 1375 return TkNomem; 1376 va_start(arg, fmt); 1377 fmtvprint(&fmtx, fmt, arg); 1378 va_end(arg); 1379 free(*val); 1380 *val = fmtstrflush(&fmtx); 1381 if(*val == nil) 1382 return TkNomem; 1383 return nil; 1384 } 1385 1386 static char* 1387 tkwidgetcmd(TkTop *t, Tk *tk, char *arg, char **val) 1388 { 1389 TkMethod *cm; 1390 TkCmdtab *ct; 1391 int bot, top, new, r; 1392 char *e, *buf; 1393 1394 buf = mallocz(Tkmaxitem, 0); 1395 if(buf == nil) 1396 return TkNomem; 1397 1398 arg = tkword(t, arg, buf, buf+Tkmaxitem, nil); 1399 if(val != nil) 1400 *val = nil; 1401 1402 cm = tkmethod[tk->type]; 1403 1404 e = TkBadcm; 1405 bot = 0; 1406 top = cm->ncmd - 1; 1407 1408 while(bot <= top) { 1409 new = (bot + top)/2; 1410 ct = &cm->cmd[new]; 1411 r = strcmp(ct->name, buf); 1412 if(r == 0) { 1413 e = ct->fn(tk, arg, val); 1414 break; 1415 } 1416 if(r < 0) 1417 bot = new + 1; 1418 else 1419 top = new - 1; 1420 } 1421 free(buf); 1422 tkdirty(tk); 1423 return e; 1424 } 1425 1426 Rectangle 1427 tkrect(Tk *tk, int withborder) 1428 { 1429 Rectangle r; 1430 int bd; 1431 bd = withborder ? tk->borderwidth : 0; 1432 r.min.x = -bd; 1433 r.min.y = -bd; 1434 r.max.x = tk->act.width + bd; 1435 r.max.y = tk->act.height + bd; 1436 return r; 1437 } 1438 1439 void 1440 tkdirty(Tk *tk) 1441 { 1442 Tk *sub; 1443 Point rel; 1444 Rectangle dirty; 1445 int isdirty, transparent; 1446 1447 /* 1448 * mark as dirty all views underneath a dirty transparent widget 1449 * down to the first opaque widget. 1450 * inform parents about any dirtiness. 1451 1452 * XXX as Tksubsub never gets reset, testing against Tksubsub doesn't *exactly* test 1453 * whether we're in a canvas/text widget, but merely 1454 * whether it has ever been. Tksubsub should probably be reset on unpack. 1455 */ 1456 isdirty = Dx(tk->dirty) > 0; 1457 transparent = tk->flag & Tktransparent; 1458 sub = tk; 1459 while (isdirty && ((tk->flag&Tksubsub) || transparent)) { 1460 if (tk->master != nil) { 1461 if (transparent) { 1462 rel.x = tk->act.x + tk->borderwidth; 1463 rel.y = tk->act.y + tk->borderwidth; 1464 dirty = rectaddpt(sub->dirty, rel); 1465 sub = tk->master; 1466 combinerect(&sub->dirty, dirty); 1467 transparent = sub->flag & Tktransparent; 1468 } 1469 tk = tk->master; 1470 } else if (tk->parent != nil) { 1471 tkmethod[tk->parent->type]->dirtychild(sub); 1472 tk = sub = tk->parent; 1473 isdirty = Dx(sub->dirty) > 0; 1474 transparent = sub->flag & Tktransparent; 1475 } else 1476 break; 1477 } 1478 } 1479 1480 static int 1481 qcmdcmp(const void *a, const void *b) 1482 { 1483 return strcmp(((TkCmdtab*)a)->name, ((TkCmdtab*)b)->name); 1484 } 1485 1486 void 1487 tksorttable(void) 1488 { 1489 int i; 1490 TkMethod *c; 1491 TkCmdtab *cmd; 1492 1493 for(i = 0; i < TKwidgets; i++) { 1494 c = tkmethod[i]; 1495 if(c->cmd == nil) 1496 continue; 1497 1498 for(cmd = c->cmd; cmd->name != nil; cmd++) 1499 ; 1500 c->ncmd = cmd - c->cmd; 1501 1502 qsort(c->cmd, c->ncmd, sizeof(TkCmdtab), qcmdcmp); 1503 } 1504 } 1505 1506 static char* 1507 tksinglecmd(TkTop *t, char *arg, char **val) 1508 { 1509 Tk *tk; 1510 int bot, top, new; 1511 char *e, *buf; 1512 1513 if(t->debug) 1514 print("tk: '%s'\n", arg); 1515 1516 buf = mallocz(Tkmaxitem, 0); 1517 if(buf == nil) 1518 return TkNomem; 1519 1520 arg = tkword(t, arg, buf, buf+Tkmaxitem, nil); 1521 switch(buf[0]) { 1522 case '\0': 1523 free(buf); 1524 return nil; 1525 case '.': 1526 tk = tklook(t, buf, 0); 1527 if(tk == nil){ 1528 tkerr(t, buf); 1529 free(buf); 1530 return TkBadwp; 1531 } 1532 e = tkwidgetcmd(t, tk, arg, val); 1533 free(buf); 1534 return e; 1535 } 1536 1537 bot = 0; 1538 top = nelem(cmdmain) - 1; 1539 e = TkBadcm; 1540 while(bot <= top) { 1541 int rc; 1542 new = (bot + top)/2; 1543 rc = strcmp(cmdmain[new].name, buf); 1544 if(!rc) { 1545 e = cmdmain[new].fn(t, arg, val); 1546 break; 1547 } 1548 1549 if(rc < 0) 1550 bot = new + 1; 1551 else 1552 top = new - 1; 1553 } 1554 free(buf); 1555 return e; 1556 } 1557 1558 static char* 1559 tkmatch(int inc, int dec, char *p) 1560 { 1561 int depth, esc, c; 1562 1563 esc = 0; 1564 depth = 1; 1565 while(*p) { 1566 c = *p; 1567 if(esc == 0) { 1568 if(c == inc) 1569 depth++; 1570 if(c == dec) 1571 depth--; 1572 if(depth == 0) 1573 return p; 1574 } 1575 if(c == '\\' && esc == 0) 1576 esc = 1; 1577 else 1578 esc = 0; 1579 p++; 1580 } 1581 return nil; 1582 } 1583 1584 char* 1585 tkexec(TkTop *t, char *arg, char **val) 1586 { 1587 int cmdsz, n; 1588 char *p, *cmd, *e, *c; 1589 1590 if(t->execdepth >= 0 && ++t->execdepth > 128) 1591 return TkDepth; 1592 1593 cmd = nil; 1594 cmdsz = 0; 1595 1596 p = arg; 1597 for(;;) { 1598 switch(*p++) { 1599 case '[': 1600 p = tkmatch('[', ']', p); 1601 if(p == nil){ 1602 free(cmd); 1603 return TkSyntx; 1604 } 1605 break; 1606 case '{': 1607 p = tkmatch('{', '}', p); 1608 if(p == nil){ 1609 free(cmd); 1610 return TkSyntx; 1611 } 1612 break; 1613 case ';': 1614 n = p - arg - 1; 1615 if(cmdsz < n) 1616 cmdsz = n; 1617 c = realloc(cmd, cmdsz+1); 1618 if(c == nil){ 1619 free(cmd); 1620 return TkNomem; 1621 } 1622 cmd = c; 1623 memmove(cmd, arg, n); 1624 cmd[n] = '\0'; 1625 e = tksinglecmd(t, cmd, nil); 1626 if(e != nil) { 1627 t->err = e; 1628 strncpy(t->errcmd, cmd, sizeof(t->errcmd)); 1629 t->errcmd[sizeof(t->errcmd)-1] = '\0'; 1630 free(cmd); 1631 return e; 1632 } 1633 arg = p; 1634 break; 1635 case '\0': 1636 case '\'': 1637 free(cmd); 1638 e = tksinglecmd(t, arg, val); 1639 if(e != nil) { 1640 t->err = e; 1641 strncpy(t->errcmd, arg, sizeof(t->errcmd)); 1642 t->errcmd[sizeof(t->errcmd)-1] = '\0'; 1643 } 1644 return e; 1645 } 1646 } 1647 } 1648 1649 static struct { 1650 char *name; 1651 int mask; 1652 } events[] = { 1653 "Button1P", TkButton1P, 1654 "Button1R", TkButton1R, 1655 "Button2P", TkButton2P, 1656 "Button2R", TkButton2R, 1657 "Button3P", TkButton3P, 1658 "Button3R", TkButton3R, 1659 "Button4P", TkButton4P, 1660 "Button4R", TkButton4R, 1661 "Button5P", TkButton5P, 1662 "Button5R", TkButton5R, 1663 "Button6P", TkButton6P, 1664 "Button6R", TkButton6R, 1665 "Extn1", TkExtn1, 1666 "Extn2", TkExtn2, 1667 "Takefocus", TkTakefocus, 1668 "Destroy", TkDestroy, 1669 "Enter", TkEnter, 1670 "Leave", TkLeave, 1671 "Motion", TkMotion, 1672 "Map", TkMap, 1673 "Unmap", TkUnmap, 1674 "Key", TkKey, 1675 "Focusin", TkFocusin, 1676 "Focusout", TkFocusout, 1677 "Configure", TkConfigure, 1678 "Double", TkDouble, 1679 0 1680 }; 1681 1682 int 1683 tkeventfmt(Fmt *f) 1684 { 1685 int k, i, d; 1686 int e; 1687 1688 e = va_arg(f->args, int); 1689 1690 if ((f->flags & FmtSharp) && e == TkMotion) 1691 return 0; 1692 fmtprint(f, "<"); 1693 k = -1; 1694 if (e & TkKey) { 1695 k = e & 0xffff; 1696 e &= ~0xffff; 1697 } 1698 d = 0; 1699 for (i = 0; events[i].name; i++) { 1700 if (e & events[i].mask) { 1701 if (d++) 1702 fmtprint(f, "|"); 1703 fmtprint(f, "%s", events[i].name); 1704 } 1705 } 1706 if (k != -1) { 1707 fmtprint(f, "[%c]", k); 1708 } else if (e == 0) 1709 fmtprint(f, "Noevent"); 1710 fmtprint(f, ">"); 1711 return 0; 1712 } 1713 1714 void 1715 tkerr(TkTop *t, char *e) 1716 { 1717 if(t != nil && e != nil){ 1718 strncpy(t->errx, e, sizeof(t->errx)); 1719 t->errx[sizeof(t->errx)-1] = '\0'; 1720 } 1721 } 1722 1723 char* 1724 tkerrstr(TkTop *t, char *e) 1725 { 1726 char *s = malloc(strlen(e)+1+strlen(t->errx)+1); 1727 1728 if(s == nil) 1729 return nil; 1730 strcpy(s, e); 1731 if(*e == '!'){ 1732 strcat(s, " "); 1733 strcat(s, t->errx); 1734 } 1735 t->errx[0] = '\0'; 1736 return s; 1737 } 1738 1739 char* 1740 tksetmgrab(TkTop *t, Tk *tk) 1741 { 1742 Tk *omgrab; 1743 TkCtxt *c; 1744 c = t->ctxt; 1745 if (tk == nil) { 1746 omgrab = c->mgrab; 1747 c->mgrab = nil; 1748 /* 1749 * don't enterleave if grab reset would cause no leave event 1750 */ 1751 if (!(omgrab != nil && (omgrab->flag & Tknograb) && 1752 c->entered != nil && (c->entered->flag & Tknograb))) 1753 tkenterleave(t); 1754 } else { 1755 if (c->focused && c->mfocus != nil && c->mfocus->env->top != tk->env->top) 1756 return "!grab already taken on another toplevel"; 1757 c->mgrab = tk; 1758 if (tk->flag & Tknograb) { 1759 if (c->focused) { 1760 c->focused = 0; 1761 c->mfocus = nil; 1762 } 1763 } else if (c->focused || c->mstate.b != 0) { 1764 c->focused = 1; 1765 c->mfocus = tk; 1766 } 1767 //print("setmgrab(%s) focus now %s\n", tkname(tk), tkname(c->mfocus)); 1768 tkenterleave(t); 1769 } 1770 return nil; 1771 } 1772 1773 int 1774 tkinsidepoly(Point *poly, int np, int winding, Point p) 1775 { 1776 Point pi, pj; 1777 int i, j, hit; 1778 1779 hit = 0; 1780 j = np - 1; 1781 for (i = 0; i < np; j = i++) { 1782 pi = poly[i]; 1783 pj = poly[j]; 1784 if ((pi.y <= p.y && p.y < pj.y || pj.y <= p.y && p.y < pi.y) && 1785 p.x < (pj.x - pi.x) * (p.y - pi.y) / (pj.y - pi.y) + pi.x) { 1786 if (winding == 1 || pi.y > p.y) 1787 hit++; 1788 else 1789 hit--; 1790 } 1791 } 1792 return (hit & winding) != 0; 1793 } 1794 1795 int 1796 tklinehit(Point *a, int np, int w, Point p) 1797 { 1798 Point *b; 1799 int z, nx, ny, nrm; 1800 while(np-- > 1) { 1801 b = a+1; 1802 nx = a->y - b->y; 1803 ny = b->x - a->x; 1804 nrm = (nx < 0? -nx : nx) + (ny < 0? -ny : ny); 1805 if(nrm) 1806 z = (p.x-b->x)*nx/nrm + (p.y-b->y)*ny/nrm; 1807 else 1808 z = (p.x-b->x) + (p.y-b->y); 1809 if(z < 0) 1810 z = -z; 1811 if(z < w) 1812 return 1; 1813 a++; 1814 } 1815 return 0; 1816 } 1817 1818 int 1819 tkiswordchar(int c) 1820 { 1821 return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_' || c >= 0xA0; 1822 } 1823 1824 int 1825 tkhaskeyfocus(Tk *tk) 1826 { 1827 if (tk == nil || tk->env->top->focused == 0) 1828 return 0; 1829 return tk == tk->env->top->ctxt->tkkeygrab; 1830 } 1831 1832 static int 1833 rptactive(void *v) 1834 { 1835 int id = (int)v; 1836 if (id == rptid) 1837 return 1; 1838 return 0; 1839 } 1840 1841 static int 1842 ckrpt(void *v, int interval) 1843 { 1844 int id = (int)v; 1845 if (id != rptid) 1846 return -1; 1847 if (interval < rptto) 1848 return 0; 1849 return 1; 1850 } 1851 1852 static void 1853 dorpt(void *v) 1854 { 1855 int id = (int)v; 1856 1857 if (id == rptid) { 1858 rptto = rptint; 1859 (*rptcb)(rptw, rptnote, 0); 1860 if (rptint <= 0) { 1861 rptid++; 1862 rptw = nil; 1863 } 1864 } 1865 } 1866 1867 void 1868 tkcancelrepeat(Tk *tk) 1869 { 1870 if (tk == rptw) { 1871 rptid++; 1872 rptw = nil; 1873 } 1874 } 1875 1876 void 1877 tkrepeat(Tk *tk, void (*callback)(Tk*, void*, int), void *note, int pause, int interval) 1878 { 1879 rptid++; 1880 if (tk != rptw && rptw != nil) 1881 /* existing callback being replaced- report to owner */ 1882 (*rptcb)(rptw, rptnote, 1); 1883 rptw = tk; 1884 if (tk == nil || callback == nil) 1885 return; 1886 rptnote = note; 1887 rptcb = callback; 1888 rptto = pause; 1889 rptint = interval; 1890 if (!autorpt) 1891 autorpt = rptproc("autorepeat", TkRptclick, (void*)rptid, rptactive, ckrpt, dorpt); 1892 else 1893 rptwakeup((void*)rptid, autorpt); 1894 } 1895 1896 static int 1897 blinkactive(void *v) 1898 { 1899 USED(v); 1900 return blinkw != nil; 1901 } 1902 1903 static int 1904 ckblink(void *v, int interval) 1905 { 1906 USED(v); 1907 USED(interval); 1908 1909 if (blinkw == nil) 1910 return -1; 1911 if (blinkignore) { 1912 blinkignore = 0; 1913 return 0; 1914 } 1915 return 1; 1916 } 1917 1918 static void 1919 doblink(void *v) 1920 { 1921 USED(v); 1922 1923 if (blinkw == nil) 1924 return; 1925 blinkcb(blinkw, blinkon++ & 1); 1926 tkupdate(blinkw->env->top); 1927 } 1928 1929 void 1930 tkblinkreset(Tk *tk) 1931 { 1932 if (blinkw == tk) { 1933 blinkignore = 1; 1934 blinkon = 0; 1935 } 1936 } 1937 1938 void 1939 tkblink(Tk *tk, void (*callback)(Tk*, int)) 1940 { 1941 if (tk == nil || callback == nil) { 1942 blinkw = nil; 1943 return; 1944 } 1945 blinkw = tk; 1946 blinkcb = callback; 1947 if (!blinkrpt) 1948 blinkrpt = rptproc("blinker", TkBlinkinterval, nil, blinkactive, ckblink, doblink); 1949 else 1950 rptwakeup(nil, blinkrpt); 1951 } 1952