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