1 #include "lib9.h" 2 #include "draw.h" 3 #include "tk.h" 4 #include "canvs.h" 5 #include "textw.h" 6 #include "kernel.h" 7 8 TkCtxt* 9 tknewctxt(Display *d) 10 { 11 TkCtxt *c; 12 c = malloc(sizeof(TkCtxt)); 13 if(c == nil) 14 return nil; 15 c->lock = libqlalloc(); 16 if(c->lock == nil){ 17 free(c); 18 return nil; 19 } 20 if (tkextnnewctxt(c) != 0) { 21 free(c->lock); 22 free(c); 23 return nil; 24 } 25 c->display = d; 26 return c; 27 } 28 29 void 30 tkfreectxt(TkCtxt *c) 31 { 32 int locked; 33 Display *d; 34 35 if(c == nil) 36 return; 37 38 tkextnfreectxt(c); 39 40 d = c->display; 41 locked = lockdisplay(d); 42 tkfreecolcache(c); 43 freeimage(c->i); 44 freeimage(c->ia); 45 if(locked) 46 unlockdisplay(d); 47 libqlfree(c->lock); 48 free(c); 49 } 50 51 Image* 52 tkitmp(TkEnv *e, Point p, int fillcol) 53 { 54 Image *i, **ip; 55 TkTop *t; 56 TkCtxt *ti; 57 Display *d; 58 Rectangle r; 59 ulong pix; 60 int alpha; 61 62 t = e->top; 63 ti = t->ctxt; 64 d = t->display; 65 66 pix = e->colors[fillcol]; 67 alpha = (pix & 0xff) != 0xff; 68 ip = alpha ? &ti->ia : &ti->i; 69 70 if(*ip != nil) { 71 i = *ip; 72 if(p.x <= i->r.max.x && p.y <= i->r.max.y) { 73 r.min = ZP; 74 r.max = p; 75 if (alpha) 76 drawop(i, r, nil, nil, ZP, Clear); 77 draw(i, r, tkgc(e, fillcol), nil, ZP); 78 return i; 79 } 80 r = i->r; 81 freeimage(i); 82 if(p.x < r.max.x) 83 p.x = r.max.x; 84 if(p.y < r.max.y) 85 p.y = r.max.y; 86 } 87 88 r.min = ZP; 89 r.max = p; 90 *ip = allocimage(d, r, alpha?RGBA32:d->image->chan, 0, pix); 91 92 return *ip; 93 } 94 95 void 96 tkgeomchg(Tk *tk, TkGeom *g, int bd) 97 { 98 int w, h; 99 void (*geomfn)(Tk*); 100 if(memcmp(&tk->req, g, sizeof(TkGeom)) == 0 && bd == tk->borderwidth) 101 return; 102 103 geomfn = tkmethod[tk->type]->geom; 104 if(geomfn != nil) 105 geomfn(tk); 106 107 if(tk->master != nil) { 108 tkpackqit(tk->master); 109 tkrunpack(tk->env->top); 110 } 111 else 112 if(tk->geom != nil) { 113 w = tk->req.width; 114 h = tk->req.height; 115 tk->req.width = 0; 116 tk->req.height = 0; 117 tk->geom(tk, tk->act.x, tk->act.y, w, h); 118 if (tk->slave) { 119 tkpackqit(tk); 120 tkrunpack(tk->env->top); 121 } 122 } 123 tkdeliver(tk, TkConfigure, g); 124 } 125 126 /* 127 * return the widget within tk with by point p (in widget coords) 128 */ 129 Tk* 130 tkinwindow(Tk *tk, Point p, int descend) 131 { 132 Tk *f; 133 Point q; 134 if (ptinrect(p, tkrect(tk, 1)) == 0) 135 return nil; 136 for (;;) { 137 if (descend && tkmethod[tk->type]->inwindow != nil) 138 f = tkmethod[tk->type]->inwindow(tk, &p); 139 else { 140 q = p; 141 for (f = tk->slave; f; f = f->next) { 142 q.x = p.x - (f->act.x + f->borderwidth); 143 q.y = p.y - (f->act.y + f->borderwidth); 144 if (ptinrect(q, tkrect(f, 1))) 145 break; 146 } 147 p = q; 148 } 149 if (f == nil || f == tk) 150 return tk; 151 tk = f; 152 } 153 return nil; /* for compiler */ 154 } 155 156 Tk* 157 tkfindfocus(TkTop *t, int x, int y, int descend) 158 { 159 Point p, q; 160 Tk *tk, *f; 161 TkWin *tkw; 162 p.x = x; 163 p.y = y; 164 for(f = t->windows; f != nil; f = TKobj(TkWin, f)->next) { 165 assert(f->flag&Tkwindow); 166 if(f->flag & Tkmapped) { 167 tkw = TKobj(TkWin, f); 168 q.x = p.x - (tkw->act.x+f->borderwidth); 169 q.y = p.y - (tkw->act.y+f->borderwidth); 170 tk = tkinwindow(f, q, descend); 171 if(tk != nil) 172 return tk; 173 } 174 } 175 return nil; 176 } 177 178 void 179 tkmovewin(Tk *tk, Point p) 180 { 181 TkWin *tkw; 182 if((tk->flag & Tkwindow) == 0) 183 return; 184 tkw = TKobj(TkWin, tk); 185 if(! eqpt(p, tkw->req)){ 186 tkw->req = p; 187 tkw->changed = 1; 188 } 189 } 190 191 void 192 tkmoveresize(Tk *tk, int x, int y, int w, int h) 193 { 194 TkWin *tkw; 195 USED(x); 196 USED(y); 197 assert(tk->flag&Tkwindow); 198 tkw = TKobj(TkWin, tk); 199 if(w < 0) 200 w = 0; 201 if(h < 0) 202 h = 0; 203 //print("moveresize %s %d %d +[%d %d], callerpc %lux\n", tk->name->name, x, y, w, h, getcallerpc(&tk)); 204 tk->req.width = w; 205 tk->req.height = h; 206 tk->act = tk->req; 207 /* XXX perhaps should actually suspend the window here? */ 208 tkw->changed = 1; 209 } 210 211 static void 212 tkexterncreatewin(Tk *tk, Rectangle r) 213 { 214 TkWin *tkw; 215 TkTop *top; 216 char *name; 217 218 top = tk->env->top; 219 tkw = TKobj(TkWin, tk); 220 221 /* 222 * for a choicebutton menu, use the name of the choicebutton which created it 223 */ 224 if(tk->name == nil){ 225 name = tkw->cbname; 226 assert(name != nil); 227 } else 228 name = tk->name->name; 229 230 tkw->reqid++; 231 tkwreq(top, "!reshape %s %d %d %d %d %d", name, tkw->reqid, r.min.x, r.min.y, r.max.x, r.max.y); 232 tkw->changed = 0; 233 tk->flag |= Tksuspended; 234 } 235 236 /* 237 * return non-zero if the window size has changed (XXX choose better return value/function name!) 238 */ 239 int 240 tkupdatewinsize(Tk *tk) 241 { 242 TkWin *tkw; 243 Image *previ; 244 Rectangle r, or; 245 int bw2; 246 247 tkw = TKobj(TkWin, tk); 248 bw2 = 2*tk->borderwidth; 249 r.min.x = tkw->req.x; 250 r.min.y = tkw->req.y; 251 r.max.x = r.min.x + tk->act.width + bw2; 252 r.max.y = r.min.y + tk->act.height + bw2; 253 previ = tkw->image; 254 if(previ != nil){ 255 or.min.x = tkw->act.x; 256 or.min.y = tkw->act.y; 257 or.max.x = tkw->act.x + Dx(previ->r); 258 or.max.y = tkw->act.y + Dy(previ->r); 259 if(eqrect(or, r)) 260 return 0; 261 } 262 tkexterncreatewin(tk, r); 263 return 1; 264 } 265 266 static char* 267 tkdrawslaves1(Tk *tk, Point orig, Image *dst, int *dirty) 268 { 269 Tk *f; 270 char *e = nil; 271 Point worig; 272 Rectangle r, oclip; 273 274 worig.x = orig.x + tk->act.x + tk->borderwidth; 275 worig.y = orig.y + tk->act.y + tk->borderwidth; 276 277 r = rectaddpt(tk->dirty, worig); 278 if (Dx(r) > 0 && rectXrect(r, dst->clipr)) { 279 e = tkmethod[tk->type]->draw(tk, orig); 280 tk->dirty = bbnil; 281 *dirty = 1; 282 } 283 if(e != nil) 284 return e; 285 286 /* 287 * grids need clipping 288 * XXX BUG: they can't, 'cos text widgets don't clip appropriately. 289 */ 290 if (tk->grid != nil) { 291 r = rectaddpt(tkrect(tk, 0), worig); 292 if (rectclip(&r, dst->clipr) == 0) 293 return nil; 294 oclip = dst->clipr; 295 replclipr(dst, 0, r); 296 } 297 for(f = tk->slave; e == nil && f; f = f->next) 298 e = tkdrawslaves1(f, worig, dst, dirty); 299 if (tk->grid != nil) 300 replclipr(dst, 0, oclip); 301 return e; 302 } 303 304 char* 305 tkdrawslaves(Tk *tk, Point orig, int *dirty) 306 { 307 Image *i; 308 char *e; 309 i = tkimageof(tk); 310 if (i == nil) 311 return nil; 312 e = tkdrawslaves1(tk, orig, i, dirty); 313 return e; 314 } 315 316 char* 317 tkupdate(TkTop *t) 318 { 319 Tk* tk; 320 int locked; 321 TkWin *tkw; 322 Display *d; 323 char *e; 324 int dirty = 0; 325 if(t->noupdate) 326 return nil; 327 328 d = t->display; 329 locked = lockdisplay(d); 330 tk = t->windows; 331 while(tk) { 332 tkw = TKobj(TkWin, tk); 333 if((tk->flag & (Tkmapped|Tksuspended)) == Tkmapped) { 334 if (tkupdatewinsize(tk) == 0){ 335 e = tkdrawslaves(tk, ZP, &dirty); 336 if(e != nil) 337 return e; 338 } 339 } 340 tk = tkw->next; 341 } 342 if (dirty || t->dirty) { 343 flushimage(d, 1); 344 t->dirty = 0; 345 } 346 if(locked) 347 unlockdisplay(d); 348 return nil; 349 } 350 351 int 352 tkischild(Tk *tk, Tk *child) 353 { 354 while(child != nil && child != tk){ 355 if(child->master) 356 child = child->master; 357 else 358 child = child->parent; 359 } 360 return child == tk; 361 } 362 363 void 364 tksetbits(Tk *tk, int mask) 365 { 366 tk->flag |= mask; 367 for(tk = tk->slave; tk; tk = tk->next) 368 tksetbits(tk, mask); 369 } 370 371 char* 372 tkmap(Tk *tk) 373 { 374 /* 375 is this necessary? 376 tkw = TKobj(TkWin, tk); 377 if(tkw->image != nil) 378 tkwreq(tk->env->top, "raise %s", tk->name->name); 379 */ 380 381 if(tk->flag & Tkmapped) 382 return nil; 383 384 tk->flag |= Tkmapped; 385 tkmoveresize(tk, 0, 0, tk->act.width, tk->act.height); 386 tkdeliver(tk, TkMap, nil); 387 return nil; 388 //tkupdate(tk->env->top); 389 } 390 391 void 392 tkclrfocus(Tk *master) 393 { 394 TkCtxt *c; 395 Tk *tk; 396 TkTop *top; 397 398 if(master == nil) 399 return; 400 top = master->env->top; 401 c = top->ctxt; 402 403 tk = c->mgrab; 404 if(tkischild(master, tk)) 405 tksetmgrab(top, nil); 406 407 tk = c->entered; 408 if(tkischild(master, tk)){ 409 c->entered = nil; 410 tkdeliver(tk, TkLeave, nil); 411 } 412 } 413 414 void 415 tkunmap(Tk *tk) 416 { 417 TkTop *t; 418 TkCtxt *c; 419 420 while(tk->master) 421 tk = tk->master; 422 423 if((tk->flag & Tkmapped) == 0) 424 return; 425 426 t = tk->env->top; 427 c = t->ctxt; 428 429 if(tkischild(tk, c->mgrab)) 430 tksetmgrab(t, nil); 431 if(tkischild(tk, c->entered)){ 432 tkdeliver(c->entered, TkLeave, nil); 433 c->entered = nil; 434 } 435 if(tk == t->root) 436 tksetglobalfocus(t, 0); 437 438 tk->flag &= ~(Tkmapped|Tksuspended); 439 440 tkdestroywinimage(tk); 441 tkdeliver(tk, TkUnmap, nil); 442 tkenterleave(t); 443 /* XXX should unmap menus too */ 444 } 445 446 Image* 447 tkimageof(Tk *tk) 448 { 449 while(tk) { 450 if(tk->flag & Tkwindow) 451 return TKobj(TkWin, tk)->image; 452 if(tk->parent != nil) { 453 tk = tk->parent; 454 switch(tk->type) { 455 case TKmenu: 456 return TKobj(TkWin, tk)->image; 457 case TKcanvas: 458 return TKobj(TkCanvas, tk)->image; 459 case TKtext: 460 return TKobj(TkText, tk)->image; 461 } 462 abort(); 463 } 464 tk = tk->master; 465 } 466 return nil; 467 } 468 469 void 470 tktopopt(Tk *tk, char *opt) 471 { 472 TkTop *t; 473 TkWin *tkw; 474 TkOptab tko[4]; 475 476 tkw = TKobj(TkWin, tk); 477 478 t = tk->env->top; 479 480 tko[0].ptr = tkw; 481 tko[0].optab = tktop; 482 tko[1].ptr = tk; 483 tko[1].optab = tkgeneric; 484 tko[2].ptr = t; 485 tko[2].optab = tktopdbg; 486 tko[3].ptr = nil; 487 488 tkparse(t, opt, tko, nil); 489 } 490 491 /* general compare - compare top-left corners, y takes priority */ 492 static int 493 tkfcmpgen(void *ap, void *bp) 494 { 495 TkWinfo *a = ap, *b = bp; 496 497 if (a->r.min.y > b->r.min.y) 498 return 1; 499 if (a->r.min.y < b->r.min.y) 500 return -1; 501 if (a->r.min.x > b->r.min.x) 502 return 1; 503 if (a->r.min.x < b->r.min.x) 504 return -1; 505 return 0; 506 } 507 508 /* compare x-coords only */ 509 static int 510 tkfcmpx(void *ap, void *bp) 511 { 512 TkWinfo *a = ap, *b = bp; 513 return a->r.min.x - b->r.min.x; 514 } 515 516 /* compare y-coords only */ 517 static int 518 tkfcmpy(void *ap, void *bp) 519 { 520 TkWinfo *a = ap, *b = bp; 521 return a->r.min.y - b->r.min.y; 522 } 523 524 static void 525 tkfintervalintersect(int min1, int max1, int min2, int max2, int *min, int *max) 526 { 527 if (min1 < min2) 528 min1 = min2; 529 if (max1 > max2) 530 max1 = max2; 531 if (max1 > min1) { 532 *min = min1; 533 *max = max1; 534 } else 535 *max = *min; /* no intersection */ 536 } 537 538 void 539 tksortfocusorder(TkWinfo *inf, int n) 540 { 541 int i; 542 Rectangle overlap, r; 543 int (*cmpfn)(void*, void*); 544 545 overlap = inf[0].r; 546 for (i = 0; i < n; i++) { 547 r = inf[i].r; 548 tkfintervalintersect(overlap.min.x, overlap.max.x, 549 r.min.x, r.max.x, &overlap.min.x, &overlap.max.x); 550 tkfintervalintersect(overlap.min.y, overlap.max.y, 551 r.min.y, r.max.y, &overlap.min.y, &overlap.max.y); 552 } 553 554 if (Dx(overlap) > 0) 555 cmpfn = tkfcmpy; 556 else if (Dy(overlap) > 0) 557 cmpfn = tkfcmpx; 558 else 559 cmpfn = tkfcmpgen; 560 561 qsort(inf, n, sizeof(*inf), cmpfn); 562 } 563 564 void 565 tkappendfocusorder(Tk *tk) 566 { 567 TkTop *tkt; 568 tkt = tk->env->top; 569 if (tk->flag & Tktakefocus) 570 tkt->focusorder[tkt->nfocus++] = tk; 571 if (tkmethod[tk->type]->focusorder != nil) 572 tkmethod[tk->type]->focusorder(tk); 573 } 574 575 void 576 tkbuildfocusorder(TkTop *tkt) 577 { 578 Tk *tk; 579 int n; 580 581 if (tkt->focusorder != nil) 582 free(tkt->focusorder); 583 n = 0; 584 for (tk = tkt->root; tk != nil; tk = tk->siblings) 585 if (tk->flag & Tktakefocus) 586 n++; 587 if (n == 0) { 588 tkt->focusorder = nil; 589 return; 590 } 591 592 tkt->focusorder = malloc(sizeof(*tkt->focusorder) * n); 593 tkt->nfocus = 0; 594 if (tkt->focusorder == nil) 595 return; 596 597 tkappendfocusorder(tkt->root); 598 } 599 600 void 601 tkdirtyfocusorder(TkTop *tkt) 602 { 603 free(tkt->focusorder); 604 tkt->focusorder = nil; 605 tkt->nfocus = 0; 606 } 607 608 #define O(t, e) ((long)(&((t*)0)->e)) 609 #define OA(t, e) ((long)(((t*)0)->e)) 610 611 typedef struct TkSee TkSee; 612 struct TkSee { 613 int r[4]; 614 int p[2]; 615 int query; 616 }; 617 618 static 619 TkOption seeopts[] = { 620 "rectangle", OPTfrac, OA(TkSee, r), IAUX(4), 621 "point", OPTfrac, OA(TkSee, p), IAUX(2), 622 "where", OPTbool, O(TkSee, query), nil, 623 nil 624 }; 625 626 char* 627 tkseecmd(TkTop *t, char *arg, char **ret) 628 { 629 TkOptab tko[2]; 630 TkSee opts; 631 TkName *names; 632 Tk *tk; 633 char *e; 634 Rectangle vr; 635 Point vp; 636 637 opts.r[0] = bbnil.min.x; 638 opts.r[1] = bbnil.min.y; 639 opts.r[2] = bbnil.max.x; 640 opts.r[3] = bbnil.max.y; 641 opts.p[0] = bbnil.max.x; 642 opts.p[1] = bbnil.max.y; 643 opts.query = 0; 644 645 tko[0].ptr = &opts; 646 tko[0].optab = seeopts; 647 tko[1].ptr = nil; 648 names = nil; 649 e = tkparse(t, arg, tko, &names); 650 if (e != nil) 651 return e; 652 if (names == nil) 653 return TkBadwp; 654 tk = tklook(t, names->name, 0); 655 tkfreename(names); 656 if (tk == nil) 657 return TkBadwp; 658 if (opts.query) { 659 if (!tkvisiblerect(tk, &vr)) 660 return nil; 661 /* XXX should this be converted into screen coords? */ 662 return tkvalue(ret, "%d %d %d %d", vr.min.x, vr.min.y, vr.max.x, vr.max.y); 663 } 664 vr.min.x = opts.r[0]; 665 vr.min.y = opts.r[1]; 666 vr.max.x = opts.r[2]; 667 vr.max.y = opts.r[3]; 668 vp.x = opts.p[0]; 669 vp.y = opts.p[1]; 670 671 if (eqrect(vr, bbnil)) 672 vr = tkrect(tk, 1); 673 if (eqpt(vp, bbnil.max)) 674 vp = vr.min; 675 tksee(tk, vr, vp); 676 return nil; 677 } 678 679 /* 680 * make rectangle r in widget tk visible if possible; 681 * if not possible, at least make point p visible. 682 */ 683 void 684 tksee(Tk *tk, Rectangle r, Point p) 685 { 686 Point g; 687 //print("tksee %R, %P in %s\n", r, p, tk->name->name); 688 g = Pt(tk->borderwidth, tk->borderwidth); 689 if(tk->parent != nil) { 690 g = addpt(g, tkmethod[tk->parent->type]->relpos(tk)); 691 tk = tk->parent; 692 } else { 693 g.x += tk->act.x; 694 g.y += tk->act.y; 695 tk = tk->master; 696 } 697 r = rectaddpt(r, g); 698 p = addpt(p, g); 699 while (tk != nil) { 700 if (tkmethod[tk->type]->see != nil){ 701 //print("see r %R, p %P in %s\n", r, p, tk->name->name); 702 tkmethod[tk->type]->see(tk, &r, &p); 703 //print("now r %R, p %P\n", r, p); 704 } 705 g = Pt(tk->borderwidth, tk->borderwidth); 706 if (tk->parent != nil) { 707 g = addpt(g, tkmethod[tk->parent->type]->relpos(tk)); 708 tk = tk->parent; 709 } else { 710 g.x += tk->act.x; 711 g.y += tk->act.y; 712 tk = tk->master; 713 } 714 r = rectaddpt(r, g); 715 p = addpt(p, g); 716 } 717 } 718