1 #include "lib9.h" 2 #include "interp.h" 3 #include "isa.h" 4 #include "runt.h" 5 #include "draw.h" 6 #include "tk.h" 7 #include "tkmod.h" 8 #include "pool.h" 9 #include "drawif.h" 10 #include "keyboard.h" 11 #include "raise.h" 12 #include "kernel.h" 13 14 extern void tkfreetop(Heap*, int); 15 Type* fakeTkTop; 16 static uchar TktypeMap[] = Tk_Toplevel_map; 17 int tkstylus; 18 void (*tkwiretap)(void*, char*, char*, void*, Rectangle*); 19 20 static void tktopimagedptr(TkTop*, Draw_Image*); 21 static char*tkputwinimage(Tk*, Draw_Image*, int); 22 23 static void 24 lockctxt(TkCtxt *ctxt) 25 { 26 libqlock(ctxt->lock); 27 } 28 29 static void 30 unlockctxt(TkCtxt *ctxt) 31 { 32 libqunlock(ctxt->lock); 33 } 34 35 static void 36 tkmarktop(Type *t, void *vw) 37 { 38 Heap *h; 39 TkVar *v; 40 TkPanelimage *di; 41 TkTop *top; 42 Tk *w, *next; 43 TkWin *tkw; 44 45 markheap(t, vw); 46 top = vw; 47 // XXX do we need to lock context here?? 48 for(v = top->vars; v; v = v->link) { 49 if(v->type == TkVchan) { 50 h = D2H(v->value); 51 Setmark(h); 52 } 53 } 54 for (di = top->panelimages; di != nil; di = di->link) { 55 h = D2H(di->image); 56 Setmark(h); 57 } 58 for(w = top->windows; w != nil; w = next){ 59 tkw = TKobj(TkWin, w); 60 if(tkw->image != nil){ 61 h = D2H(tkw->di); 62 Setmark(h); 63 } 64 next = tkw->next; 65 } 66 } 67 68 void 69 tkmodinit(void) 70 { 71 builtinmod("$Tk", Tkmodtab, Tkmodlen); 72 fmtinstall('v', tkeventfmt); /* XXX */ 73 74 fakeTkTop = dtype(tkfreetop, sizeof(TkTop), TktypeMap, sizeof(TktypeMap)); 75 fakeTkTop->mark = tkmarktop; 76 77 tksorttable(); 78 } 79 80 void 81 Tk_toplevel(void *a) 82 { 83 Tk *tk; 84 Heap *h; 85 TkTop *t; 86 TkWin *tkw; 87 TkCtxt *ctxt; 88 Display *disp; 89 F_Tk_toplevel *f = a; 90 void *r; 91 92 r = *f->ret; 93 *f->ret = H; 94 destroy(r); 95 disp = checkdisplay(f->d); 96 97 h = heapz(fakeTkTop); 98 t = H2D(TkTop*, h); 99 poolimmutable(h); 100 101 t->dd = f->d; 102 D2H(t->dd)->ref++; 103 104 t->execdepth = -1; 105 t->display = disp; 106 107 tk = tknewobj(t, TKframe, sizeof(Tk)+sizeof(TkWin)); 108 if(tk == nil) { 109 destroy(t); 110 return; 111 } 112 113 tk->act.x = 0; 114 tk->act.y = 0; 115 tk->act.width = 1; /* XXX why not zero? */ 116 tk->act.height = 1; 117 tk->flag |= Tkwindow; 118 119 tkw = TKobj(TkWin, tk); 120 tkw->di = H; 121 122 tktopopt(tk, string2c(f->arg)); 123 124 tk->geom = tkmoveresize; 125 tk->name = tkmkname("."); 126 if(tk->name == nil) { 127 tkfreeobj(tk); 128 destroy(t); 129 return; 130 } 131 132 ctxt = tknewctxt(disp); 133 if(ctxt == nil) { 134 tkfreeobj(tk); 135 destroy(t); 136 return; 137 } 138 t->ctxt = ctxt; 139 t->screenr = disp->image->r; 140 141 tkw->next = t->windows; 142 t->windows = tk; 143 t->root = tk; 144 Setmark(h); 145 poolmutable(h); 146 t->wreq = cnewc(&Tptr, movp, 8); 147 *f->ret = (Tk_Toplevel*)t; 148 } 149 150 void 151 Tk_cmd(void *a) 152 { 153 TkTop *t; 154 char *val, *e; 155 F_Tk_cmd *f = a; 156 157 t = (TkTop*)f->t; 158 if(t == H || D2H(t)->t != fakeTkTop) { 159 retstr(TkNotop, f->ret); 160 return; 161 } 162 lockctxt(t->ctxt); 163 val = nil; 164 e = tkexec(t, string2c(f->arg), &val); 165 unlockctxt(t->ctxt); 166 if(e == TkNomem){ 167 free(val); 168 error(exNomem); /* what about f->ret? */ 169 } 170 if(e != nil && t->errx[0] != '\0'){ 171 char *s = tkerrstr(t, e); 172 173 retstr(s, f->ret); 174 free(s); 175 } 176 else 177 retstr(e == nil ? val : e, f->ret); 178 if(tkwiretap != nil) 179 tkwiretap(t, string2c(f->arg), val, nil, nil); 180 free(val); 181 } 182 183 void 184 Tk_color(void *fp) 185 { 186 ulong rgba; 187 F_Tk_color *f = fp; 188 if(tkparsecolor(string2c(f->col), &rgba) != nil) 189 *f->ret = DNotacolor; 190 else 191 *f->ret = rgba; 192 } 193 194 void 195 Tk_rect(void *fp) 196 { 197 F_Tk_rect *f = fp; 198 Tk *tk; 199 TkTop *t; 200 Rectangle r; 201 Point o; 202 int bd, flags, w, h; 203 204 t = (TkTop*)f->t; 205 if(t == H || D2H(t)->t != fakeTkTop){ 206 *(Rectangle*)f->ret = ZR; 207 return; 208 } 209 lockctxt(t->ctxt); 210 tk = tklook(t, string2c(f->name), 0); 211 if(tk == nil){ 212 *(Rectangle*)f->ret = ZR; 213 unlockctxt(t->ctxt); 214 return; 215 } 216 o = tkposn(tk); 217 flags = f->flags; 218 if(flags & Tk_Local) 219 o = subpt(o, tkposn(tk->env->top->root)); 220 if(flags & Tk_Required){ 221 h = tk->req.height; 222 w = tk->req.width; 223 }else{ 224 h = tk->act.height; 225 w = tk->act.width; 226 } 227 unlockctxt(t->ctxt); 228 if(w < 0) 229 w = 0; 230 if(h < 0) 231 h = 0; 232 bd = tk->borderwidth; 233 if(bd < 0) 234 bd = 0; 235 if(flags & Tk_Border){ 236 r.min = o; 237 r.max.x = r.min.x + w + bd + bd; 238 r.max.y = r.min.y + h + bd + bd; 239 }else{ 240 r.min.x = o.x + bd; 241 r.min.y = o.y + bd; 242 r.max.x = r.min.x + w; 243 r.max.y = r.min.y + h; 244 } 245 *(Rectangle*)f->ret = r; 246 } 247 248 int 249 tkdescendant(Tk *p, Tk *c) 250 { 251 int n; 252 253 if(c == nil || p->env->top != c->env->top) 254 return 0; 255 256 if (p->name != nil && c->name != nil) { 257 n = strlen(p->name->name); 258 if(strncmp(p->name->name, c->name->name, n) == 0) 259 return 1; 260 } 261 262 return 0; 263 } 264 265 void 266 tkenterleave(TkTop *t) 267 { 268 Tk *fw, *ent; 269 TkMouse m; 270 TkTop *t1, *t2; 271 TkCtxt *c; 272 273 c = t->ctxt; 274 m = c->mstate; 275 276 if (c->mgrab != nil && (c->mgrab->flag & Tknograb)) { 277 fw = tkfindfocus(t, m.x, m.y, 1); 278 if (fw != c->mgrab && fw != nil && (fw->flag & Tknograb) == 0) 279 fw = nil; 280 } else if (c->focused) { 281 fw = tkfindfocus(t, m.x, m.y, 1); 282 if (fw != c->mfocus) 283 fw = nil; 284 } else if (c->mgrab != nil) { 285 fw = tkfindfocus(t, m.x, m.y, 1); 286 if (fw != nil) { 287 if (!tkdescendant(c->mgrab, fw) && !(fw->flag & c->mgrab->flag & Tknograb)) 288 fw = nil; 289 } 290 } else if (m.b == 0) 291 fw = tkfindfocus(t, m.x, m.y, 0); 292 else if (tkfindfocus(t, m.x, m.y, 1) == c->entered) 293 return; 294 else 295 fw = nil; 296 297 if (c->entered == fw) 298 return; 299 300 t1 = t2 = nil; 301 if (c->entered != nil) { 302 ent = c->entered; 303 t1 = ent->env->top; 304 c->entered = nil; 305 tkdeliver(ent, TkLeave, nil); 306 } 307 308 if (fw != nil) { 309 t2 = fw->env->top; 310 c->entered = fw; 311 tkdeliver(fw, TkEnter, &m); 312 } 313 if (t1 != nil) 314 tkupdate(t1); 315 if (t2 != nil && t1 != t2) 316 tkupdate(t2); 317 } 318 319 void 320 Tk_pointer(void *a) 321 { 322 static int buttonr[] = {TkButton1R, TkButton2R, TkButton3R, TkButton4R, TkButton5R, TkButton6R}; 323 static int buttonp[] = {TkButton1P, TkButton2P, TkButton3P, TkButton4P, TkButton5P, TkButton6P}; 324 Tk *fw, *target, *dest, *ent; 325 TkMouse m; 326 TkCtxt *c; 327 TkTop *t, *ot; 328 int d, dtype, etype; 329 F_Tk_pointer *f = a; 330 int b, lastb, inside; 331 332 t = (TkTop*)f->t; 333 if(t == H || D2H(t)->t != fakeTkTop) 334 return; 335 336 c = t->ctxt; 337 338 /* ignore no-button-motion for emulated stylus input */ 339 if(tkstylus && c->mstate.b == 0 && (f->p.buttons&0x1f)==0) 340 return; 341 342 lockctxt(c); 343 //if (f->p.buttons != 0 || c->mstate.b != 0) 344 //print("tkmouse %d [%d %d], focused %d[%s], grab %s, entered %s\n", 345 // f->p.buttons, f->p.xy.x, f->p.xy.y, c->focused, tkname(c->mfocus), tkname(c->mgrab), tkname(c->entered)); 346 /* 347 * target is the widget that we're deliver the mouse event to. 348 * inside is true if the mouse point is located inside target. 349 */ 350 inside = 1; 351 if (c->mgrab != nil && (c->mgrab->flag & Tknograb)) { 352 fw = tkfindfocus(t, f->p.xy.x, f->p.xy.y, 1); 353 if (fw != nil && (fw->flag & Tknograb)) 354 target = fw; 355 else { 356 target = c->mgrab; 357 inside = 0; 358 } 359 } else if (c->focused) { 360 if (c->mfocus != nil) { 361 fw = tkfindfocus(t, f->p.xy.x, f->p.xy.y, 1); 362 if (fw != c->mfocus) 363 inside = 0; 364 } 365 target = c->mfocus; 366 } else if (c->mgrab != nil && (c->mgrab->flag & Tkdisabled) == 0) { 367 /* 368 * XXX this isn't quite right, as perhaps we should do a tkinwindow() 369 * (below the grab). 370 * so that events to containers underneath the grab arrive 371 * via the containers (as is usual) 372 */ 373 fw = tkfindfocus(t, f->p.xy.x, f->p.xy.y, 1); 374 if (fw != nil && tkdescendant(c->mgrab, fw)) 375 target = fw; 376 else { 377 target = c->mgrab; 378 inside = 0; 379 } 380 } else 381 target = tkfindfocus(t, f->p.xy.x, f->p.xy.y, 0); 382 383 lastb = c->mstate.b; 384 c->mstate.x = f->p.xy.x; 385 c->mstate.y = f->p.xy.y; 386 c->mstate.b = f->p.buttons & 0x1f; /* Just the buttons */ 387 m = c->mstate; 388 389 /* XXX if the mouse is being moved with the buttons held down 390 * and we've no mfocus and no mgrab then ignore 391 * the event as our original target has gone away (or never existed) 392 */ 393 if (lastb && m.b && !c->focused && c->mgrab == nil) 394 target = nil; 395 396 if (target != c->entered || (c->entered != nil && !inside)) { 397 if (c->entered != nil) { 398 fw = c->entered; 399 c->entered = nil; 400 tkdeliver(fw, TkLeave, nil); 401 if (target == nil || fw->env->top != target->env->top) 402 tkupdate(fw->env->top); 403 } 404 if (inside) { 405 c->entered = target; 406 tkdeliver(target, TkEnter, &m); 407 } 408 } 409 410 dest = nil; 411 if (target != nil) { 412 etype = 0; 413 dtype = 0; 414 if(f->p.buttons & (1<<8)) /* Double */ 415 dtype = TkDouble; 416 417 d = lastb ^ m.b; 418 if (d) { 419 /* cancel any autorepeat, notifying existing client */ 420 tkrepeat(nil, nil, nil, 0, 0); 421 if (d & ~lastb & 1) /* button 1 potentially takes the focus */ 422 tkdeliver(target, TkTakefocus|TkButton1P, &m); 423 } 424 for(b=0; b<nelem(buttonp); b++){ 425 if(d & (1<<b)){ 426 etype = buttonr[b]; 427 if(m.b & (1<<b)) 428 etype = buttonp[b]|dtype; 429 dest = tkdeliver(target, etype, &m); 430 } 431 } 432 if(tkstylus && m.b==0) { 433 if ((ent = c->entered) != nil) { 434 c->entered = nil; 435 ot = ent->env->top; 436 tkdeliver(ent, TkLeave, nil); 437 if (ot != target->env->top) 438 tkupdate(ot); 439 } 440 } else if(etype == 0) { 441 etype = TkMotion; 442 for(b = 0; b<nelem(buttonp); b++) 443 if (m.b & (1<<b)) 444 etype |= buttonp[b]; 445 tkdeliver(target, etype, &m); 446 } 447 if (m.b != 0) { 448 if (lastb == 0 && !c->focused) { /* (some deliver might have grabbed it...) */ 449 if (dest == nil) 450 dest = target; 451 if ((dest->flag & Tknograb) == 0) { 452 c->focused = 1; 453 c->mfocus = dest; 454 } 455 } 456 } else { 457 c->focused = 0; 458 c->mfocus = nil; 459 if (lastb != 0) 460 tkenterleave(t); 461 } 462 tkupdate(target->env->top); 463 } else if (c->focused && m.b == 0) { 464 c->focused = 0; 465 tkenterleave(t); 466 } 467 unlockctxt(c); 468 } 469 470 void 471 Tk_keyboard(void *a) 472 { 473 Tk *grab; 474 TkTop *t; 475 TkCtxt *c; 476 F_Tk_keyboard *f = a; 477 478 t = (TkTop*)f->t; 479 if(t == H || D2H(t)->t != fakeTkTop) 480 return; 481 c = t->ctxt; 482 if (c == nil) 483 return; 484 lockctxt(c); 485 if (c->tkmenu != nil) 486 grab = c->tkmenu; 487 else 488 grab = c->tkkeygrab; 489 if(grab == nil){ 490 unlockctxt(c); 491 return; 492 } 493 494 t = grab->env->top; 495 tkdeliver(grab, TkKey|TKKEY(f->key), nil); 496 tkupdate(t); 497 unlockctxt(c); 498 } 499 500 TkVar* 501 tkmkvar(TkTop *t, char *name, int type) 502 { 503 TkVar *v; 504 505 for(v = t->vars; v; v = v->link) 506 if(strcmp(v->name, name) == 0) 507 return v; 508 509 if(type == 0) 510 return nil; 511 512 v = malloc(sizeof(TkVar)+strlen(name)+1); 513 if(v == nil) 514 return nil; 515 strcpy(v->name, name); 516 v->link = t->vars; 517 t->vars = v; 518 v->type = type; 519 v->value = nil; 520 if(type == TkVchan) 521 v->value = H; 522 return v; 523 } 524 525 void 526 tkfreevar(TkTop *t, char *name, int swept) 527 { 528 TkVar **l, *p; 529 530 if(name == nil) 531 return; 532 l = &t->vars; 533 for(p = *l; p != nil; p = p->link) { 534 if(strcmp(p->name, name) == 0) { 535 *l = p->link; 536 switch(p->type) { 537 default: 538 free(p->value); 539 break; 540 case TkVchan: 541 if(!swept) 542 destroy(p->value); 543 break; 544 } 545 free(p); 546 return; 547 } 548 l = &p->link; 549 } 550 } 551 552 void 553 Tk_namechan(void *a) 554 { 555 Heap *h; 556 TkVar *v; 557 TkTop *t; 558 char *name; 559 F_Tk_namechan *f = a; 560 561 t = (TkTop*)f->t; 562 if(t == H || D2H(t)->t != fakeTkTop) { 563 retstr(TkNotop, f->ret); 564 return; 565 } 566 if(f->c == H) { 567 retstr("nil channel", f->ret); 568 return; 569 } 570 name = string2c(f->n); 571 if(name[0] == '\0') { 572 retstr(TkBadvl, f->ret); 573 return; 574 } 575 576 lockctxt(t->ctxt); 577 v = tkmkvar(t, name, TkVchan); 578 if(v == nil) { 579 unlockctxt(t->ctxt); 580 retstr(TkNomem, f->ret); 581 return; 582 } 583 if(v->type != TkVchan) { 584 unlockctxt(t->ctxt); 585 retstr(TkNotvt, f->ret); 586 return; 587 } 588 destroy(v->value); 589 v->value = f->c; 590 unlockctxt(t->ctxt); 591 h = D2H(v->value); 592 h->ref++; 593 Setmark(h); 594 retstr("", f->ret); 595 } 596 597 void 598 Tk_quote(void *a) 599 { 600 String *s, *ns; 601 F_Tk_quote *f; 602 void *r; 603 int c, i, need, len, userune, last, n; 604 Rune *sr; 605 char *sc; 606 607 f = a; 608 609 r = *f->ret; 610 *f->ret = H; 611 destroy(r); 612 613 s = f->s; 614 if(s == H){ 615 retstr("{}", f->ret); 616 return; 617 } 618 len = s->len; 619 userune = 0; 620 if(len < 0) { 621 len = -len; 622 userune = 1; 623 } 624 need = len+2; 625 for(i = 0; i < len; i++) { 626 c = userune? s->Srune[i]: s->Sascii[i]; 627 if(c == '{' || c == '}' || c == '\\') 628 need++; 629 } 630 if(userune) { 631 ns = newrunes(need); 632 sr = ns->Srune; 633 *sr++ = '{'; 634 last = 0; 635 for(i = 0;; i++) { 636 if(i >= len || (c = s->Srune[i]) == '{' || c == '}' || c == '\\'){ 637 n = i-last; 638 if(n) { 639 memmove(sr, &s->Srune[last], n*sizeof(Rune)); 640 sr += n; 641 } 642 if(i >= len) 643 break; 644 *sr++ = '\\'; 645 last = i; 646 } 647 } 648 *sr = '}'; 649 } else { 650 ns = newstring(need); 651 sc = ns->Sascii; 652 *sc++ = '{'; 653 last = 0; 654 for(i = 0;; i++) { 655 if(i >= len || (c = s->Sascii[i]) == '{' || c == '}' || c == '\\'){ 656 n = i-last; 657 if(n) { 658 memmove(sc, &s->Sascii[last], n); 659 sc += n; 660 } 661 if(i >= len) 662 break; 663 *sc++ = '\\'; 664 last = i; 665 } 666 } 667 *sc= '}'; 668 } 669 *f->ret = ns; 670 } 671 672 static void 673 tkreplimg(TkTop *t, Draw_Image *f, Draw_Image *m, Image **ximg) 674 { 675 Display *d; 676 Image *cimg, *cmask, *new; 677 678 cimg = lookupimage(f); 679 d = t->display; 680 if(cimg == nil || cimg->screen != nil || cimg->display != d) 681 return; 682 cmask = lookupimage(m); 683 if(cmask != nil && (cmask->screen != nil || cmask->display != d)) 684 return; 685 686 if (cmask == nil) 687 new = allocimage(d, Rect(0, 0, Dx(cimg->r), Dy(cimg->r)), cimg->chan, 0, DNofill); 688 else { 689 if(cmask->screen != nil || cmask->display != d) 690 return; 691 new = allocimage(d, Rect(0, 0, Dx(cimg->r), Dy(cimg->r)), RGBA32, 0, DTransparent); 692 } 693 if(new == nil) 694 return; 695 draw(new, new->r, cimg, cmask, cimg->r.min); 696 if(tkwiretap != nil) 697 tkwiretap(t, "replimg", nil, cimg, &cimg->r); 698 if(*ximg != nil) 699 freeimage(*ximg); 700 *ximg = new; 701 } 702 703 static char* 704 tkaddpanelimage(TkTop *t, Draw_Image *di, Image **i) 705 { 706 TkPanelimage *pi; 707 708 if (di == H) { 709 *i = 0; 710 return nil; 711 } 712 713 *i = lookupimage(di); 714 if (*i == nil || (*i)->display != t->display) 715 return TkNotwm; 716 717 for (pi = t->panelimages; pi != nil; pi = pi->link) { 718 if (pi->image == di) { 719 pi->ref++; 720 return nil; 721 } 722 } 723 724 pi = malloc(sizeof(TkPanelimage)); 725 if (pi == nil) 726 return TkNomem; 727 pi->image = di; 728 D2H(di)->ref++; 729 pi->ref = 1; 730 pi->link = t->panelimages; 731 t->panelimages = pi; 732 return nil; 733 } 734 735 void 736 tkdelpanelimage(TkTop *t, Image *i) 737 { 738 TkPanelimage *pi, *prev; 739 int locked; 740 741 if (i == nil) 742 return; 743 744 prev = nil; 745 for (pi = t->panelimages; pi != nil; pi = pi->link) { 746 if (lookupimage(pi->image) == i) 747 break; 748 prev = pi; 749 } 750 if (pi == nil || --pi->ref > 0) 751 return; 752 if (prev) 753 prev->link = pi->link; 754 else 755 t->panelimages = pi->link; 756 if (D2H(pi->image)->ref == 1) { /* don't bother locking if it's not going away */ 757 locked = lockdisplay(t->display); 758 destroy(pi->image); 759 if (locked) 760 unlockdisplay(t->display); 761 } 762 763 free(pi); 764 } 765 766 void 767 Tk_putimage(void *a) 768 { 769 TkTop *t; 770 TkImg *tki; 771 Image *i, *m, *oldi, *oldm; 772 int locked, found, reqid, n; 773 char *words[2]; 774 Display *d; 775 F_Tk_putimage *f; 776 void *r; 777 char *name, *e; 778 Tk *tk; 779 780 f = a; 781 782 r = *f->ret; 783 *f->ret = H; 784 destroy(r); 785 786 t = (TkTop*)f->t; 787 if(t == H || D2H(t)->t != fakeTkTop) { 788 retstr(TkNotop, f->ret); 789 return; 790 } 791 792 if(f->i == H) { 793 retstr(TkBadvl, f->ret); 794 return; 795 } 796 797 name = string2c(f->name); 798 lockctxt(t->ctxt); 799 e = nil; 800 found = 0; 801 if(name[0] == '.'){ 802 n = getfields(name, words, nelem(words), 1, " "); 803 reqid = -1; 804 if(n > 1){ 805 reqid = atoi(words[1]); 806 name = words[0]; 807 } 808 if((tk = tklook(t, name, 0)) != nil){ 809 if(tk->type == TKchoicebutton){ 810 tk = tkfindchoicemenu(tk); 811 if(tk == nil) 812 goto Error; 813 } 814 if(tk->type == TKframe || tk->type == TKmenu){ 815 if((tk->flag & Tkwindow) == 0){ 816 e = TkNotwm; 817 goto Error; 818 } 819 e = tkputwinimage(tk, f->i, reqid); 820 found = 1; 821 } else 822 if(tk->type == TKpanel){ 823 if(n > 1){ 824 e = TkBadvl; 825 goto Error; 826 } 827 e = tkaddpanelimage(t, f->i, &i); 828 if(e != nil) 829 goto Error; 830 e = tkaddpanelimage(t, f->m, &m); 831 if(e != nil){ 832 tkdelpanelimage(t, i); 833 goto Error; 834 } 835 tkgetpanelimage(tk, &oldi, &oldm); 836 tkdelpanelimage(t, oldi); 837 tkdelpanelimage(t, oldm); 838 tksetpanelimage(tk, i, m); 839 tkdirty(tk); 840 found = 1; 841 } 842 } 843 } 844 if(!found){ 845 /* XXX perhaps we shouldn't ever do this if name begins with '.'? */ 846 tki = tkname2img(t, name); 847 if(tki == nil) { 848 e = TkBadvl; 849 goto Error; 850 } 851 852 d = t->display; 853 locked = lockdisplay(d); 854 tkreplimg(t, f->i, f->m, &tki->img); 855 if(locked) 856 unlockdisplay(d); 857 858 tksizeimage(t->root, tki); 859 } 860 Error: 861 unlockctxt(t->ctxt); 862 if(e != nil) 863 retstr(e, f->ret); 864 return; 865 } 866 867 Draw_Image* 868 tkimgcopy(TkTop *t, Image *cimg) 869 { 870 Image *new; 871 Display *dp; 872 Draw_Image *i; 873 874 if(cimg == nil) 875 return H; 876 877 dp = t->display; 878 new = allocimage(dp, cimg->r, cimg->chan, cimg->repl, DNofill); 879 if(new == nil) 880 return H; 881 new->clipr = cimg->clipr; 882 883 drawop(new, new->r, cimg, nil, cimg->r.min, S); 884 if(tkwiretap != nil) 885 tkwiretap(t, "imgcopy", nil, cimg, &cimg->r); 886 887 i = mkdrawimage(new, H, t->dd, nil); 888 if(i == H) 889 freeimage(new); 890 891 return i; 892 } 893 894 void 895 Tk_getimage(void *a) 896 { 897 Tk *tk; 898 char *n; 899 TkImg *i; 900 TkTop *t; 901 int locked; 902 Display *d; 903 F_Tk_getimage *f; 904 void *r; 905 void (*getimgs)(Tk*, Image**, Image**); 906 Image *image, *mask; 907 908 f = a; 909 910 r = f->ret->t0; 911 f->ret->t0 = H; 912 destroy(r); 913 r = f->ret->t1; 914 f->ret->t1 = H; 915 destroy(r); 916 r = f->ret->t2; 917 f->ret->t2 = H; 918 destroy(r); 919 920 t = (TkTop*)f->t; 921 if(t == H || D2H(t)->t != fakeTkTop) { 922 retstr(TkNotop, &f->ret->t2); 923 return; 924 } 925 d = t->ctxt->display; 926 n = string2c(f->name); 927 lockctxt(t->ctxt); 928 i = tkname2img(t, n); 929 if (i != nil) { 930 image = i->img; 931 mask = nil; 932 } else { 933 tk = tklook(t, n, 0); 934 if (tk == nil || (getimgs = tkmethod[tk->type]->getimgs) == nil) { 935 unlockctxt(t->ctxt); 936 retstr(TkBadvl, &f->ret->t2); 937 return; 938 } 939 getimgs(tk, &image, &mask); 940 } 941 locked = lockdisplay(d); 942 f->ret->t0 = tkimgcopy(t, image); 943 if (mask != nil) 944 f->ret->t1 = tkimgcopy(t, mask); 945 if (locked) 946 unlockdisplay(d); 947 unlockctxt(t->ctxt); 948 } 949 950 void 951 tkfreetop(Heap *h, int swept) 952 { 953 TkTop *t; 954 Tk *f; 955 TkImg *i, *nexti; 956 TkVar *v, *nextv; 957 int wgtype; 958 void *r; 959 TkPanelimage *pi, *nextpi; 960 961 t = H2D(TkTop*, h); 962 lockctxt(t->ctxt); 963 964 if(swept) { 965 t->di = H; 966 t->dd = H; 967 t->wreq = H; 968 t->wmctxt = H; 969 } 970 971 t->windows = nil; 972 973 for(f = t->root; f; f = f->siblings) { 974 f->flag |= Tkdestroy; 975 tkdeliver(f, TkDestroy, nil); 976 if(f->destroyed != nil) 977 f->destroyed(f); 978 } 979 980 for(f = t->root; f; f = t->root) { 981 t->root = f->siblings; 982 if(swept) 983 f->flag |= Tkswept; 984 tkfreeobj(f); 985 } 986 987 for(v = t->vars; v; v = nextv) { 988 nextv = v->link; 989 switch(v->type) { 990 default: 991 free(v->value); 992 break; 993 case TkVchan: 994 if(!swept) 995 destroy(v->value); 996 break; 997 } 998 free(v); 999 } 1000 1001 for (pi = t->panelimages; pi; pi = nextpi) { 1002 if (!swept) 1003 destroy(pi->image); 1004 nextpi = pi->link; 1005 free(pi); 1006 } 1007 1008 for(i = t->imgs; i; i = nexti) { 1009 if(i->ref != 1) 1010 abort(); 1011 nexti = i->link; 1012 tkimgput(i); 1013 } 1014 /* XXX free images inside widgets */ 1015 1016 for(wgtype = 0; wgtype < TKwidgets; wgtype++) 1017 if(t->binds[wgtype]) 1018 tkfreebind(t->binds[wgtype]); 1019 1020 unlockctxt(t->ctxt); 1021 /* XXX should we leave it locked for this bit? */ 1022 tkfreectxt(t->ctxt); 1023 if(!swept) { 1024 r = t->di; 1025 t->di = H; 1026 destroy(r); 1027 1028 r = t->dd; 1029 t->dd = H; 1030 destroy(r); 1031 1032 r = t->wreq; 1033 t->wreq = H; 1034 destroy(r); 1035 1036 r = t->wmctxt; 1037 t->wmctxt = H; 1038 destroy(r); 1039 } 1040 } 1041 1042 static void 1043 tktopimagedptr(TkTop *top, Draw_Image *di) 1044 { 1045 if(top->di != H){ 1046 destroy(top->di); 1047 top->di = H; 1048 } 1049 if(di == H) 1050 return; 1051 D2H(di)->ref++; 1052 top->di = di; 1053 } 1054 1055 static void 1056 tkfreewinimage(TkWin *w) 1057 { 1058 destroy(w->di); 1059 w->image = nil; 1060 w->di = H; 1061 } 1062 1063 static int 1064 tksetwindrawimage(Tk *tk, Draw_Image *di) 1065 { 1066 TkWin *tkw; 1067 char *name; 1068 Image *i; 1069 int locked; 1070 int same; 1071 1072 tkw = TKobj(TkWin, tk); 1073 1074 same = tkw->di == di; 1075 if(!same) 1076 if(tkw->image != nil) 1077 destroy(tkw->di); 1078 if(di == H){ 1079 tkw->di = H; 1080 tkw->image = nil; 1081 return same; 1082 } 1083 tkw->di = di; 1084 i = lookupimage(di); 1085 tkw->image = i; 1086 1087 locked = lockdisplay(i->display); 1088 if(originwindow(i, ZP, i->r.min) == -1) 1089 print("tk originwindow failed: %r\n"); 1090 di->r = DRECT(i->r); 1091 di->clipr = DRECT(i->clipr); 1092 if(locked) 1093 unlockdisplay(i->display); 1094 1095 if(!same){ 1096 D2H(di)->ref++; 1097 if(tk->name){ 1098 name = tk->name->name; 1099 if(name[0] == '.' && name[1] == '\0') 1100 tktopimagedptr(tk->env->top, tkw->di); 1101 } 1102 } 1103 return same; 1104 } 1105 1106 void 1107 tkdestroywinimage(Tk *tk) 1108 { 1109 TkWin *tkw; 1110 TkTop *top; 1111 char *name; 1112 1113 assert(tk->flag & Tkwindow); 1114 tkw = TKobj(TkWin, tk); 1115 top = tk->env->top; 1116 1117 if(tkw->image != nil && !(tk->flag & Tkswept)) 1118 destroy(tkw->di); 1119 tkw->di = H; 1120 tkw->image = nil; 1121 if(tk->name == nil) 1122 name = tkw->cbname; 1123 else 1124 name = tk->name->name; 1125 if(name[0] == '.' && name[1] == '\0' && !(tk->flag & Tkswept)) 1126 tktopimagedptr(top, H); 1127 tkw->reqid++; 1128 tkwreq(top, "delete %s", name); 1129 } 1130 1131 static char* 1132 tkputwinimage(Tk *tk, Draw_Image *di, int reqid) 1133 { 1134 TkWin *tkw; 1135 TkTop *top; 1136 Image *i; 1137 int bw2, prop, resize; 1138 Rectangle req; 1139 1140 top = tk->env->top; 1141 tkw = TKobj(TkWin, tk); 1142 i = lookupimage(di); 1143 if (i == nil || i->display != top->display) 1144 return TkNotwm; 1145 1146 if(reqid != -1 && reqid < tkw->reqid) 1147 return "!request out of date"; 1148 1149 bw2 = 2*tk->borderwidth; 1150 req.min.x = tkw->req.x; 1151 req.min.y = tkw->req.y; 1152 req.max.x = req.min.x + tk->act.width + bw2; 1153 req.max.y = req.min.y + tk->act.height + bw2; 1154 1155 resize = 0; 1156 if(eqrect(req, i->r) == 0){ 1157 /* 1158 * if we'd sent a request and our requested rect has now changed, 1159 * then resend the request (via tkupdatewinsize), 1160 * otherwise accept the new size and repack if necessary 1161 */ 1162 if(reqid != -1 && tkw->changed){ 1163 if(tkupdatewinsize(tk)) 1164 return "!requested size has changed"; 1165 1166 } else if(Dx(req) != Dx(i->r) || Dy(req) != Dy(i->r)){ 1167 tk->flag |= Tksuspended; 1168 tk->act.width = Dx(i->r) - bw2; 1169 tk->act.height = Dy(i->r) - bw2; 1170 tk->req = tk->act; 1171 prop = tk->flag & Tknoprop; 1172 tk->flag |= Tknoprop; 1173 tkpackqit(tk); 1174 tkrunpack(top); 1175 tk->flag = (tk->flag & ~Tknoprop) | prop; 1176 resize = 1; 1177 } 1178 } 1179 if(reqid == -1) 1180 tkw->reqid++; /* invalidate all buffered requests. */ 1181 tkw->act = i->r.min; 1182 tkw->req = tkw->act; 1183 tkw->changed = 0; 1184 tk->req.width = Dx(i->r) - bw2; 1185 tk->req.height = Dy(i->r) - bw2; 1186 tk->act = tk->req; 1187 if((tk->flag & Tkmapped) == 0){ 1188 tk->flag |= Tkmapped; 1189 tkdeliver(tk, TkMap, nil); 1190 } 1191 if(tksetwindrawimage(tk, di) == 0 || resize){ 1192 tk->dirty = tkrect(tk, 1); 1193 tk->flag |= Tkrefresh; 1194 } 1195 tk->flag &= ~Tksuspended; 1196 1197 lookupimage(di); /* make sure limbo image coords correspond correctly */ 1198 tkupdate(top); 1199 return nil; 1200 } 1201 1202 void 1203 tkwreq(TkTop *top, char *fmt, ...) 1204 { 1205 char *buf; 1206 va_list arg; 1207 1208 va_start(arg, fmt); 1209 buf = vsmprint(fmt, arg); 1210 va_end(arg); 1211 tktolimbo(top->wreq, buf); 1212 free(buf); 1213 } 1214 1215 int 1216 tktolimbo(void *var, char *msg) 1217 { 1218 void *ptrs[1]; 1219 int r; 1220 1221 if(var==H) 1222 return 0; 1223 ptrs[0] = H; 1224 retstr(msg, (String**) &ptrs[0]); 1225 r = csendalt((Channel *)var, ptrs, &Tptr, TkMaxmsgs); 1226 return r; 1227 } 1228 1229 static void 1230 hexify(char *buf, int n) 1231 { 1232 static char hex[] = "0123456789abcdef"; 1233 uchar b; 1234 char *dp, *fp; 1235 fp = buf+n; 1236 dp = buf+n*2; 1237 *dp-- = '\0'; 1238 while(fp-- > buf){ 1239 b = (uchar)*fp; 1240 *dp-- = hex[b & 0xf]; 1241 *dp-- = hex[b >> 4]; 1242 } 1243 } 1244 1245 char* 1246 tkcursorswitch(TkTop *top, Image *i, TkImg *img) 1247 { 1248 Image *ci, *scratch; 1249 char *buf; 1250 Rectangle r; 1251 int n, maxb, nb; 1252 1253 if(i == nil && img == nil){ 1254 tktolimbo(top->wreq, "cursor"); 1255 return nil; 1256 } 1257 1258 if(img != nil){ 1259 if(img->cursor){ 1260 tktolimbo(top->wreq, img->cursor); 1261 return nil; 1262 } 1263 i = img->img; 1264 } 1265 if(i->depth != 1 || Dx(i->r)*Dy(i->r) > 16000 || Dy(i->r)%8 != 0 || Dy(i->r)%2 != 0) 1266 return TkBadcursor; 1267 /* 1268 * readjust image, inferring hotspot from origin. 1269 */ 1270 if(i->r.min.x != 0 || i->r.min.y != 0){ 1271 r.min.x = 0; 1272 r.min.y = 0; 1273 r.max.x = Dx(i->r); 1274 r.max.y = Dy(i->r); 1275 scratch = allocimage(i->display, r, GREY1, 0, DNofill); 1276 if(scratch == nil) 1277 return TkNomem; 1278 draw(scratch, r, i, nil, i->r.min); 1279 ci = scratch; 1280 }else{ 1281 scratch = nil; 1282 ci = i; 1283 } 1284 nb = ci->r.max.x/8 * ci->r.max.y; 1285 maxb = 7 + 12*4 + 2*nb + 1; 1286 buf = mallocz(maxb, 0); 1287 if(buf == nil) 1288 return TkNomem; 1289 n = sprint(buf, "cursor %d %d %d %d ", i->r.min.x, i->r.min.y, ci->r.max.x, ci->r.max.y); 1290 unloadimage(ci, ci->r, (uchar*)buf+n, maxb-n); 1291 hexify(buf+n, nb); 1292 tktolimbo(top->wreq, buf); 1293 if(img != nil){ 1294 free(img->cursor); 1295 img->cursor = buf; 1296 } 1297 freeimage(scratch); 1298 return nil; 1299 } 1300 1301 void 1302 tkcursorset(TkTop *t, Point p) 1303 { 1304 tkwreq(t, "ptr %d %d", p.x, p.y); 1305 } 1306