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