xref: /inferno-os/libtk/windw.c (revision 4eb166cf184c1f102fb79e31b1465ea3e2021c39)
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