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*
tknewctxt(Display * d)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
tkfreectxt(TkCtxt * c)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*
tkitmp(TkEnv * e,Point p,int fillcol)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
tkgeomchg(Tk * tk,TkGeom * g,int bd)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*
tkinwindow(Tk * tk,Point p,int descend)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 }
154
155 Tk*
tkfindfocus(TkTop * t,int x,int y,int descend)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
tkmovewin(Tk * tk,Point p)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
tkmoveresize(Tk * tk,int x,int y,int w,int h)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
tkexterncreatewin(Tk * tk,Rectangle r)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
tkupdatewinsize(Tk * tk)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*
tkdrawslaves1(Tk * tk,Point orig,Image * dst,int * dirty)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*
tkdrawslaves(Tk * tk,Point orig,int * dirty)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*
tkupdate(TkTop * t)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
tkischild(Tk * tk,Tk * child)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
tksetbits(Tk * tk,int mask)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*
tkmap(Tk * tk)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
tkunmap(Tk * tk)391 tkunmap(Tk *tk)
392 {
393 TkTop *t;
394 TkCtxt *c;
395
396 while(tk->master)
397 tk = tk->master;
398
399 if((tk->flag & Tkmapped) == 0)
400 return;
401
402 t = tk->env->top;
403 c = t->ctxt;
404
405 if(tkischild(tk, c->mgrab))
406 tksetmgrab(t, nil);
407 if(tkischild(tk, c->entered)){
408 tkdeliver(c->entered, TkLeave, nil);
409 c->entered = nil;
410 }
411 if(tk == t->root)
412 tksetglobalfocus(t, 0);
413
414 tk->flag &= ~(Tkmapped|Tksuspended);
415
416 tkdestroywinimage(tk);
417 tkdeliver(tk, TkUnmap, nil);
418 tkenterleave(t);
419 /* XXX should unmap menus too */
420 }
421
422 Image*
tkimageof(Tk * tk)423 tkimageof(Tk *tk)
424 {
425 while(tk) {
426 if(tk->flag & Tkwindow)
427 return TKobj(TkWin, tk)->image;
428 if(tk->parent != nil) {
429 tk = tk->parent;
430 switch(tk->type) {
431 case TKmenu:
432 return TKobj(TkWin, tk)->image;
433 case TKcanvas:
434 return TKobj(TkCanvas, tk)->image;
435 case TKtext:
436 return TKobj(TkText, tk)->image;
437 }
438 abort();
439 }
440 tk = tk->master;
441 }
442 return nil;
443 }
444
445 void
tktopopt(Tk * tk,char * opt)446 tktopopt(Tk *tk, char *opt)
447 {
448 TkTop *t;
449 TkWin *tkw;
450 TkOptab tko[4];
451
452 tkw = TKobj(TkWin, tk);
453
454 t = tk->env->top;
455
456 tko[0].ptr = tkw;
457 tko[0].optab = tktop;
458 tko[1].ptr = tk;
459 tko[1].optab = tkgeneric;
460 tko[2].ptr = t;
461 tko[2].optab = tktopdbg;
462 tko[3].ptr = nil;
463
464 tkparse(t, opt, tko, nil);
465 }
466
467 /* general compare - compare top-left corners, y takes priority */
468 static int
tkfcmpgen(void * ap,void * bp)469 tkfcmpgen(void *ap, void *bp)
470 {
471 TkWinfo *a = ap, *b = bp;
472
473 if (a->r.min.y > b->r.min.y)
474 return 1;
475 if (a->r.min.y < b->r.min.y)
476 return -1;
477 if (a->r.min.x > b->r.min.x)
478 return 1;
479 if (a->r.min.x < b->r.min.x)
480 return -1;
481 return 0;
482 }
483
484 /* compare x-coords only */
485 static int
tkfcmpx(void * ap,void * bp)486 tkfcmpx(void *ap, void *bp)
487 {
488 TkWinfo *a = ap, *b = bp;
489 return a->r.min.x - b->r.min.x;
490 }
491
492 /* compare y-coords only */
493 static int
tkfcmpy(void * ap,void * bp)494 tkfcmpy(void *ap, void *bp)
495 {
496 TkWinfo *a = ap, *b = bp;
497 return a->r.min.y - b->r.min.y;
498 }
499
500 static void
tkfintervalintersect(int min1,int max1,int min2,int max2,int * min,int * max)501 tkfintervalintersect(int min1, int max1, int min2, int max2, int *min, int *max)
502 {
503 if (min1 < min2)
504 min1 = min2;
505 if (max1 > max2)
506 max1 = max2;
507 if (max1 > min1) {
508 *min = min1;
509 *max = max1;
510 } else
511 *max = *min; /* no intersection */
512 }
513
514 void
tksortfocusorder(TkWinfo * inf,int n)515 tksortfocusorder(TkWinfo *inf, int n)
516 {
517 int i;
518 Rectangle overlap, r;
519 int (*cmpfn)(void*, void*);
520
521 overlap = inf[0].r;
522 for (i = 0; i < n; i++) {
523 r = inf[i].r;
524 tkfintervalintersect(overlap.min.x, overlap.max.x,
525 r.min.x, r.max.x, &overlap.min.x, &overlap.max.x);
526 tkfintervalintersect(overlap.min.y, overlap.max.y,
527 r.min.y, r.max.y, &overlap.min.y, &overlap.max.y);
528 }
529
530 if (Dx(overlap) > 0)
531 cmpfn = tkfcmpy;
532 else if (Dy(overlap) > 0)
533 cmpfn = tkfcmpx;
534 else
535 cmpfn = tkfcmpgen;
536
537 qsort(inf, n, sizeof(*inf), cmpfn);
538 }
539
540 void
tkappendfocusorder(Tk * tk)541 tkappendfocusorder(Tk *tk)
542 {
543 TkTop *tkt;
544 tkt = tk->env->top;
545 if (tk->flag & Tktakefocus)
546 tkt->focusorder[tkt->nfocus++] = tk;
547 if (tkmethod[tk->type]->focusorder != nil)
548 tkmethod[tk->type]->focusorder(tk);
549 }
550
551 void
tkbuildfocusorder(TkTop * tkt)552 tkbuildfocusorder(TkTop *tkt)
553 {
554 Tk *tk;
555 int n;
556
557 if (tkt->focusorder != nil)
558 free(tkt->focusorder);
559 n = 0;
560 for (tk = tkt->root; tk != nil; tk = tk->siblings)
561 if (tk->flag & Tktakefocus)
562 n++;
563 if (n == 0) {
564 tkt->focusorder = nil;
565 return;
566 }
567
568 tkt->focusorder = malloc(sizeof(*tkt->focusorder) * n);
569 tkt->nfocus = 0;
570 if (tkt->focusorder == nil)
571 return;
572
573 tkappendfocusorder(tkt->root);
574 }
575
576 void
tkdirtyfocusorder(TkTop * tkt)577 tkdirtyfocusorder(TkTop *tkt)
578 {
579 free(tkt->focusorder);
580 tkt->focusorder = nil;
581 tkt->nfocus = 0;
582 }
583
584 #define O(t, e) ((long)(&((t*)0)->e))
585 #define OA(t, e) ((long)(((t*)0)->e))
586
587 typedef struct TkSee TkSee;
588 struct TkSee {
589 int r[4];
590 int p[2];
591 int query;
592 };
593
594 static
595 TkOption seeopts[] = {
596 "rectangle", OPTfrac, OA(TkSee, r), IAUX(4),
597 "point", OPTfrac, OA(TkSee, p), IAUX(2),
598 "where", OPTbool, O(TkSee, query), nil,
599 nil
600 };
601
602 char*
tkseecmd(TkTop * t,char * arg,char ** ret)603 tkseecmd(TkTop *t, char *arg, char **ret)
604 {
605 TkOptab tko[2];
606 TkSee opts;
607 TkName *names;
608 Tk *tk;
609 char *e;
610 Rectangle vr;
611 Point vp;
612
613 opts.r[0] = bbnil.min.x;
614 opts.r[1] = bbnil.min.y;
615 opts.r[2] = bbnil.max.x;
616 opts.r[3] = bbnil.max.y;
617 opts.p[0] = bbnil.max.x;
618 opts.p[1] = bbnil.max.y;
619 opts.query = 0;
620
621 tko[0].ptr = &opts;
622 tko[0].optab = seeopts;
623 tko[1].ptr = nil;
624 names = nil;
625 e = tkparse(t, arg, tko, &names);
626 if (e != nil)
627 return e;
628 if (names == nil)
629 return TkBadwp;
630 tk = tklook(t, names->name, 0);
631 tkfreename(names);
632 if (tk == nil)
633 return TkBadwp;
634 if (opts.query) {
635 if (!tkvisiblerect(tk, &vr))
636 return nil;
637 /* XXX should this be converted into screen coords? */
638 return tkvalue(ret, "%d %d %d %d", vr.min.x, vr.min.y, vr.max.x, vr.max.y);
639 }
640 vr.min.x = opts.r[0];
641 vr.min.y = opts.r[1];
642 vr.max.x = opts.r[2];
643 vr.max.y = opts.r[3];
644 vp.x = opts.p[0];
645 vp.y = opts.p[1];
646
647 if (eqrect(vr, bbnil))
648 vr = tkrect(tk, 1);
649 if (eqpt(vp, bbnil.max))
650 vp = vr.min;
651 tksee(tk, vr, vp);
652 return nil;
653 }
654
655 /*
656 * make rectangle r in widget tk visible if possible;
657 * if not possible, at least make point p visible.
658 */
659 void
tksee(Tk * tk,Rectangle r,Point p)660 tksee(Tk *tk, Rectangle r, Point p)
661 {
662 Point g;
663 //print("tksee %R, %P in %s\n", r, p, tk->name->name);
664 g = Pt(tk->borderwidth, tk->borderwidth);
665 if(tk->parent != nil) {
666 g = addpt(g, tkmethod[tk->parent->type]->relpos(tk));
667 tk = tk->parent;
668 } else {
669 g.x += tk->act.x;
670 g.y += tk->act.y;
671 tk = tk->master;
672 }
673 r = rectaddpt(r, g);
674 p = addpt(p, g);
675 while (tk != nil) {
676 if (tkmethod[tk->type]->see != nil){
677 //print("see r %R, p %P in %s\n", r, p, tk->name->name);
678 tkmethod[tk->type]->see(tk, &r, &p);
679 //print("now r %R, p %P\n", r, p);
680 }
681 g = Pt(tk->borderwidth, tk->borderwidth);
682 if (tk->parent != nil) {
683 g = addpt(g, tkmethod[tk->parent->type]->relpos(tk));
684 tk = tk->parent;
685 } else {
686 g.x += tk->act.x;
687 g.y += tk->act.y;
688 tk = tk->master;
689 }
690 r = rectaddpt(r, g);
691 p = addpt(p, g);
692 }
693 }
694