xref: /inferno-os/libtk/windw.c (revision 1981fff245dfce579ef416fa767eb69d462039e9)
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 }
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 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*
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
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
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
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
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
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
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
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
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
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*
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
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