xref: /inferno-os/libtk/utils.c (revision 5849851a19380dbb62a47d9c4d868a81e42fa79b)
1 #include "lib9.h"
2 #include "draw.h"
3 #include "tk.h"
4 
5 struct TkCol
6 {
7 	ulong	rgba1;
8 	ulong	rgba3;	/* if mixed, otherwise DNotacolor */
9 	Image*	i;
10 	TkCol*	forw;
11 };
12 
13 extern void	rptwakeup(void*, void*);
14 extern void*	rptproc(char*, int, void*, int (*)(void*), int (*)(void*,int), void (*)(void*));
15 
16 typedef struct Cmd Cmd;
17 struct Cmd
18 {
19 	char*	name;
20 	char*	(*fn)(TkTop*, char*, char**);
21 };
22 static struct Cmd cmdmain[] =
23 {
24 	"bind",		tkbind,
25 	"button",	tkbutton,
26 	"canvas",	tkcanvas,
27 	"checkbutton",	tkcheckbutton,
28 	"choicebutton", tkchoicebutton,
29 	"cursor",	tkcursorcmd,
30 	"destroy",	tkdestroy,
31 	"entry",	tkentry,
32 	"focus",	tkfocus,
33 	"frame",	tkframe,
34 	"grab",		tkgrab,
35 	"grid",	tkgrid,
36 	"image",	tkimage,
37 	"label",	tklabel,
38 	"listbox",	tklistbox,
39 	"lower",	tklower,
40 	"menu",		tkmenu,
41 	"menubutton",	tkmenubutton,
42 	"pack",		tkpack,
43 	"panel",		tkpanel,
44 	"puts",		tkputs,
45 	"radiobutton",	tkradiobutton,
46 	"raise",	tkraise,
47 	"scale",	tkscale,
48 	"scrollbar",	tkscrollbar,
49 	"see",	tkseecmd,
50 	"send",		tksend,
51 	"text",		tktext,
52 	"update",	tkupdatecmd,
53 	"variable",	tkvariable,
54 	"winfo",	tkwinfo,
55 };
56 
57 char*	tkfont;
58 
59 /*
60  * auto-repeating support
61  * should perhaps be one rptproc per TkCtxt
62  * This is not done for the moment as there isn't
63  * a mechanism for terminating the rptproc
64  */
65 static void *autorpt;
66 static int rptid;
67 static Tk *rptw;
68 static void *rptnote;
69 static void (*rptcb)(Tk*, void*, int);
70 static long rptto;
71 static int rptint;
72 
73 /* blinking carets - should be per TkCtxt */
74 static void *blinkrpt;
75 static Tk *blinkw;
76 static void (*blinkcb)(Tk*, int);
77 static int blinkignore;
78 static int blinkon;
79 
80 
81 ulong
tkrgba(int r,int g,int b,int a)82 tkrgba(int r, int g, int b, int a)
83 {
84 	ulong p;
85 
86 	if(r < 0)
87 		r = 0;
88 	else if(r > 255)
89 		r = 255;
90 	if(g < 0)
91 		g = 0;
92 	else if(g > 255)
93 		g = 255;
94 	if(b < 0)
95 		b = 0;
96 	else if(b > 255)
97 		b = 255;
98 	p = (r<<24)|(g<<16)|(b<<8)|0xFF;
99 	if(a == 255)
100 		return p;
101 	return setalpha(p, a);
102 }
103 
104 /* to be replaced */
105 static int
revalpha(int c,int a)106 revalpha(int c, int a)
107 {
108 	if (a == 0)
109 		return 0;
110 	return (c & 0xff) * 255 / a;
111 }
112 
113 void
tkrgbavals(ulong rgba,int * R,int * G,int * B,int * A)114 tkrgbavals(ulong rgba, int *R, int *G, int *B, int *A)
115 {
116 	int a;
117 
118 	a = rgba & 0xff;
119 	*A = a;
120 	if (a != 0xff) {
121 		*R = revalpha(rgba>>24, a);
122 		*G = revalpha((rgba>>16) & 0xFF, a);
123 		*B = revalpha((rgba >> 8) & 0xFF, a);
124 	} else {
125 		*R = (rgba>>24);
126 		*G = ((rgba>>16) & 0xFF);
127 		*B = ((rgba >> 8) & 0xFF);
128 	}
129 }
130 
131 static int
tkcachecol(TkCtxt * c,Image * i,ulong one,ulong three)132 tkcachecol(TkCtxt *c, Image *i, ulong one, ulong three)
133 {
134 	TkCol *cc;
135 
136 	cc = malloc(sizeof(*cc));
137 	if(cc == nil)
138 		return 0;
139 	cc->rgba1 = one;
140 	cc->rgba3 = three;
141 	cc->i = i;
142 	cc->forw = c->chead;
143 	c->chead = cc;
144 	c->ncol++;
145 	/* we'll do LRU management at some point */
146 	if(c->ncol > TkColcachesize){
147 		static int warn;
148 		if(warn == 0){
149 			warn = 1;
150 			print("tk: %d colours cached\n", TkColcachesize);
151 		}
152 	}
153 	return 1;
154 }
155 
156 static Image*
tkfindcol(TkCtxt * c,ulong one,ulong three)157 tkfindcol(TkCtxt *c, ulong one, ulong three)
158 {
159 	TkCol *cc, **l;
160 
161 	for(l = &c->chead; (cc = *l) != nil; l = &cc->forw)
162 		if(cc->rgba1 == one && cc->rgba3 == three){
163 			/* move it up in the list */
164 			*l = cc->forw;
165 			cc->forw = c->chead;
166 			c->chead = cc;
167 			/* we assume it will be used right away and not stored */
168 			return cc->i;
169 		}
170 	return nil;
171 }
172 
173 void
tkfreecolcache(TkCtxt * c)174 tkfreecolcache(TkCtxt *c)
175 {
176 	TkCol *cc;
177 
178 	if(c == nil)
179 		return;
180 	while((cc = c->chead) != nil){
181 		c->chead = cc->forw;
182 		freeimage(cc->i);
183 		free(cc);
184 	}
185 	c->ctail = nil;
186 	c->ncol = 0;
187 }
188 
189 Image*
tkcolormix(TkCtxt * c,ulong one,ulong three)190 tkcolormix(TkCtxt *c, ulong one, ulong three)
191 {
192 	Image *i;
193 	Display *d;
194 
195 	i = tkfindcol(c, one, three);
196 	if(i != nil)
197 		return i;
198 	d = c->display;
199 	i = allocimagemix(d, one, three);
200 	if(i == nil)
201 		return d->black;
202 	if(!tkcachecol(c, i, one, three)){
203 		freeimage(i);
204 		return d->black;
205 	}
206 	return i;
207 }
208 
209 Image*
tkcolor(TkCtxt * c,ulong pix)210 tkcolor(TkCtxt *c, ulong pix)
211 {
212 	Image *i;
213 	Display *d;
214 	Rectangle r;
215 
216 	d = c->display;
217 	if(pix == DWhite)
218 		return d->white;
219 	if(pix == DBlack)
220 		return d->black;
221 	i = tkfindcol(c, pix, DNotacolor);
222 	if(i != nil)
223 		return i;
224 	r.min = ZP;
225 	r.max.x = 1;
226 	r.max.y = 1;
227 	if ((pix & 0xff) == 0xff)
228 		i = allocimage(d, r, RGB24, 1, pix);
229 	else
230 		i = allocimage(d, r, RGBA32, 1, pix);
231 	if(i == nil)
232 		return d->black;
233 	if(!tkcachecol(c, i, pix, DNotacolor)) {
234 		freeimage(i);
235 		return d->black;
236 	}
237 	return i;
238 }
239 
240 Image*
tkgradient(TkCtxt * c,Rectangle r,int dir,ulong pix0,ulong pix1)241 tkgradient(TkCtxt *c, Rectangle r, int dir, ulong pix0, ulong pix1)
242 {
243 	Display *d;
244 	Image *i;
245 	uchar *b, *p, *e;
246 	int c0[3], c1[3], delta[3], a, j, x, y, n, locked;
247 	Rectangle s;
248 
249 	d = c->display;
250 	y = Dy(r);
251 	x = Dx(r);
252 	if(x <= 0 || y <= 0) {
253 		r = Rect(0, 0, 1, 1);
254 		x = y = 1;
255 	}
256 	/* TO DO: diagonal */
257 	s = r;
258 	if(dir == Tkhorizontal){
259 		n = x;
260 		r.max.y = r.min.y+1;
261 	}else{
262 		n = y;
263 		r.max.x = r.min.x+1;
264 	}
265 	b = mallocz(3*n, 0);
266 	if(b == nil)
267 		return nil;
268 	locked = lockdisplay(d);
269 	i = allocimage(d, r, RGB24, 1, DNofill);
270 	if(i == nil)
271 		goto Ret;
272 	tkrgbavals(pix0, &c0[2], &c0[1], &c0[0], &a);
273 	tkrgbavals(pix1, &c1[2], &c1[1], &c1[0], &a);
274 	for(j = 0; j < 3; j++){
275 		c0[j] <<= 12;
276 		c1[j] <<= 12;
277 		delta[j] = ((c1[j]-c0[j])+(1<<11))/n;
278 	}
279 	e = b+3*n;
280 	for(p = b; p < e; p += 3) {
281 		p[0] = c0[0]>>12;
282 		p[1] = c0[1]>>12;
283 		p[2] = c0[2]>>12;
284 		c0[0] += delta[0];
285 		c0[1] += delta[1];
286 		c0[2] += delta[2];
287 	}
288 	loadimage(i, r, b, 3*n);
289 	replclipr(i, 1, s);
290 Ret:
291 	if(locked)
292 		unlockdisplay(d);
293 	free(b);
294 	return i;
295 }
296 
297 /*
298  * XXX should be in libdraw?
299  */
300 int
tkchanhastype(ulong c,int t)301 tkchanhastype(ulong c, int t)
302 {
303 	for(; c; c>>=8)
304 		if(TYPE(c) == t)
305 			return 1;
306 	return 0;
307 }
308 
309 void
tksettransparent(Tk * tk,int transparent)310 tksettransparent(Tk *tk, int transparent)
311 {
312 	if (transparent)
313 		tk->flag |= Tktransparent;
314 	else
315 		tk->flag &= ~Tktransparent;
316 }
317 
318 int
tkhasalpha(TkEnv * e,int col)319 tkhasalpha(TkEnv *e, int col)
320 {
321 	return (e->colors[col] & 0xff) != 0xff;
322 }
323 
324 Image*
tkgc(TkEnv * e,int col)325 tkgc(TkEnv *e, int col)
326 {
327 	return tkcolor(e->top->ctxt, e->colors[col]);
328 }
329 
330 
331 /*
332  * Todo: improve the fixed-point code
333  * the 255 scale factor is used because RGB ranges 0-255
334  */
335 static void
rgb2hsv(int r,int g,int b,int * h,int * s,int * v)336 rgb2hsv(int r, int g, int b, int *h, int *s, int *v)
337 {
338 	int min, max, delta;
339 
340 	max = r;
341 	if(g > max)
342 		max = g;
343 	if(b > max)
344 		max = b;
345 	min = r;
346 	if(g < min)
347 		min = g;
348 	if(b < min)
349 		min = b;
350 	*v = max;
351 	if (max != 0)
352 		*s = ((max - min)*255) / max;
353 	else
354 		*s = 0;
355 
356 	if (*s == 0) {
357 		*h = 0;	/* undefined */
358 	} else {
359 		delta = max - min;
360 		if (r == max)
361 			*h = (g - b)*255 / delta;
362 		else if (g == max)
363 			*h = (2*255) + ((b - r)*255) / delta;
364 		else if (b == max)
365 			*h = (4*255) + ((r - g)*255)/ delta;
366 		*h *= 60;
367 		if (*h < 0)
368 			*h += 360*255;
369 		*h /= 255;
370 	}
371 }
372 
373 static void
hsv2rgb(int h,int s,int v,int * r,int * g,int * b)374 hsv2rgb(int h, int s, int v, int *r, int *g, int *b)
375 {
376 	int	i;
377 	int	f,p,q,t;
378 
379 	if (s == 0 && h == 0) {
380 		*r = *g = *b = v;	/* achromatic case */
381 	} else {
382 		if (h >= 360)
383 			h = 0;
384 		i = h / 60;
385 		h *= 255;
386 		h /= 60;
387 
388 		f = h % 255;
389 		p = v * (255 - s);
390 		q = v * (255 - ((s * f)/255));
391 		t = v * (255- ((s * (255 - f))/255));
392 		p /= 255;
393 		q /= 255;
394 		t /= 255;
395 		switch (i) {
396 		case 0: *r = v; *g = t; *b = p; break;
397 		case 1: *r = q; *g = v; *b = p; break;
398 		case 2: *r = p; *g = v; *b = t; break;
399 		case 3: *r = p; *g = q; *b = v; break;
400 		case 4: *r = t; *g = p; *b = v; break;
401 		case 5: *r = v; *g = p; *b = q; break;
402 		}
403 	}
404 }
405 
406 enum {
407 	MINDELTA	= 0x10,
408 	DELTA	= 0x30,
409 };
410 
411 ulong
tkrgbashade(ulong rgba,int shade)412 tkrgbashade(ulong rgba, int shade)
413 {
414 	int R, G, B, A, h, s, v, vl, vd;
415 
416 	if (shade == TkSameshade)
417 		return rgba;
418 
419 	tkrgbavals(rgba, &R, &G, &B, &A);
420 	h = s = v = 0;
421 	rgb2hsv(R, G, B, &h, &s, &v);
422 
423 	if (v < MINDELTA) {
424 		vd = v+DELTA;
425 		vl = vd+DELTA;
426 	} else if (v > 255-MINDELTA) {
427 		vl = v-DELTA;
428 		vd = vl-DELTA;
429 	} else {
430 		vl = v+DELTA;
431 		vd = v-DELTA;
432 	}
433 
434 	v = (shade == TkLightshade)?vl:vd;
435 	if (v < 0)
436 		v = 0;
437 	if (v > 255)
438 		v = 255;
439 	hsv2rgb(h, s, v, &R, &G, &B);
440 
441 	return tkrgba(R, G, B, A);
442 }
443 
444 Image*
tkgshade(TkEnv * e,int col,int shade)445 tkgshade(TkEnv *e, int col, int shade)
446 {
447 	ulong rgba;
448 
449 	if (col == TkCbackgnd || col == TkCselectbgnd || col == TkCactivebgnd)
450 		return tkgc(e, col+shade);
451 	rgba = tkrgbashade(e->colors[col], shade);
452 	return tkcolor(e->top->ctxt, rgba);
453 }
454 
455 TkEnv*
tknewenv(TkTop * t)456 tknewenv(TkTop *t)
457 {
458 	TkEnv *e;
459 
460 	e = malloc(sizeof(TkEnv));
461 	if(e == nil)
462 		return nil;
463 
464 	e->ref = 1;
465 	e->top = t;
466 	return e;
467 }
468 
469 TkEnv*
tkdefaultenv(TkTop * t)470 tkdefaultenv(TkTop *t)
471 {
472 	int locked;
473 	TkEnv *env;
474 	Display *d;
475 
476 	if(t->env != nil) {
477 		t->env->ref++;
478 		return t->env;
479 	}
480 	t->env = malloc(sizeof(TkEnv));
481 	if(t->env == nil)
482 		return nil;
483 
484 	env = t->env;
485 	env->ref = 1;
486 	env->top = t;
487 
488 	if(tkfont == nil)
489 		tkfont = "/fonts/pelm/unicode.8.font";
490 
491 	d = t->display;
492 	env->font = font_open(d, tkfont);
493 	if(env->font == nil) {
494 		static int warn;
495 		if(warn == 0) {
496 			warn = 1;
497 			print("tk: font not found: %s\n", tkfont);
498 		}
499 		env->font = font_open(d, "*default*");
500 		if(env->font == nil) {
501 			free(t->env);
502 			t->env = nil;
503 			return nil;
504 		}
505 	}
506 
507 	locked = lockdisplay(d);
508 	env->wzero = stringwidth(env->font, "0");
509 	if ( env->wzero <= 0 )
510 		env->wzero = env->font->height / 2;
511 	if(locked)
512 		unlockdisplay(d);
513 
514 	tksetenvcolours(env);
515 	return env;
516 }
517 
518 void
tkputenv(TkEnv * env)519 tkputenv(TkEnv *env)
520 {
521 	Display *d;
522 	int locked;
523 
524 	if(env == nil)
525 		return;
526 
527 	env->ref--;
528 	if(env->ref != 0)
529 		return;
530 
531 	d = env->top->display;
532 	locked = lockdisplay(d);
533 
534 	if(env->font != nil)
535 		font_close(env->font);
536 
537 	if(locked)
538 		unlockdisplay(d);
539 
540 	free(env);
541 }
542 
543 TkEnv*
tkdupenv(TkEnv ** env)544 tkdupenv(TkEnv **env)
545 {
546 	Display *d;
547 	TkEnv *e, *ne;
548 
549 	e = *env;
550 	if(e->ref == 1)
551 		return e;
552 
553 	ne = malloc(sizeof(TkEnv));
554 	if(ne == nil)
555 		return nil;
556 
557 	ne->ref = 1;
558 	ne->top = e->top;
559 
560 	d = e->top->display;
561 	memmove(ne->colors, e->colors, sizeof(e->colors));
562 	ne->set = e->set;
563 	ne->font = font_open(d, e->font->name);
564 	ne->wzero = e->wzero;
565 
566 	e->ref--;
567 	*env = ne;
568 	return ne;
569 }
570 
571 Tk*
tknewobj(TkTop * t,int type,int n)572 tknewobj(TkTop *t, int type, int n)
573 {
574 	Tk *tk;
575 
576 	tk = malloc(n);
577 	if(tk == 0)
578 		return 0;
579 
580 	tk->type = type;		/* Defaults */
581 	tk->flag = Tktop;
582 	tk->relief = TKflat;
583 	tk->env = tkdefaultenv(t);
584 	if(tk->env == nil) {
585 		free(tk);
586 		return nil;
587 	}
588 
589 	return tk;
590 }
591 
592 void
tkfreebind(TkAction * a)593 tkfreebind(TkAction *a)
594 {
595 	TkAction *next;
596 
597 	while(a != nil) {
598 		next = a->link;
599 		if((a->type & 0xff) == TkDynamic)
600 			free(a->arg);
601 		free(a);
602 		a = next;
603 	}
604 }
605 
606 void
tkfreename(TkName * f)607 tkfreename(TkName *f)
608 {
609 	TkName *n;
610 
611 	while(f != nil) {
612 		n = f->link;
613 		free(f);
614 		f = n;
615 	}
616 }
617 
618 void
tkfreeobj(Tk * tk)619 tkfreeobj(Tk *tk)
620 {
621 	TkCtxt *c;
622 
623 	c = tk->env->top->ctxt;
624 	if(c != nil) {
625 		if(c->tkkeygrab == tk)
626 			c->tkkeygrab = nil;
627 		if(c->mgrab == tk)
628 			tksetmgrab(tk->env->top, nil);
629 		if(c->mfocus == tk)
630 			c->mfocus = nil;
631 		if(c->entered == tk)
632 			c->entered = nil;
633 	}
634 
635 	if (tk == rptw) {
636 		/* cancel the autorepeat without notifying the widget */
637 		rptid++;
638 		rptw = nil;
639 	}
640 	if (tk == blinkw)
641 		blinkw = nil;
642 	tkextnfreeobj(tk);
643 	tkmethod[tk->type]->free(tk);
644 	tkputenv(tk->env);
645 	tkfreebind(tk->binds);
646 	if(tk->name != nil)
647 		free(tk->name);
648 	free(tk);
649 }
650 
651 char*
tkaddchild(TkTop * t,Tk * tk,TkName ** names)652 tkaddchild(TkTop *t, Tk *tk, TkName **names)
653 {
654 	TkName *n;
655 	Tk *f, **l;
656 	int found, len;
657 	char *s, *ep;
658 
659 	n = *names;
660 	if(n == nil || n->name[0] != '.'){
661 		if(n != nil)
662 			tkerr(t, n->name);
663 		return TkBadwp;
664 	}
665 
666 	if (n->name[1] == '\0')
667 		return TkDupli;
668 
669 	/*
670 	 * check that the name is well-formed.
671 	 * ep will point to end of parent component of the name.
672 	 */
673 	ep = nil;
674 	for (s = n->name + 1; *s; s++) {
675 		if (*s == '.'){
676 			tkerr(t, n->name);
677 			return TkBadwp;
678 		}
679 		for (; *s && *s != '.'; s++)
680 			;
681 		if (*s == '\0')
682 			break;
683 		ep = s;
684 	}
685 	if (ep == s - 1){
686 		tkerr(t, n->name);
687 		return TkBadwp;
688 	}
689 	if (ep == nil)
690 		ep = n->name + 1;
691 	len = ep - n->name;
692 
693 	found = 0;
694 	l = &t->root;
695 	for(f = *l; f; f = f->siblings) {
696 		if (f->name != nil) {
697 			if (strcmp(n->name, f->name->name) == 0)
698 				return TkDupli;
699 			if (!found &&
700 					strncmp(n->name, f->name->name, len) == 0 &&
701 					f->name->name[len] == '\0')
702 				found = 1;
703 		}
704 		l = &f->siblings;
705 	}
706 	if (0) {		/* don't enable this until a reasonably major release... if ever */
707 		/*
708 		 * parent widget must already exist
709 		 */
710 		if (!found){
711 			tkerr(t, n->name);
712 			return TkBadwp;
713 		}
714 	}
715 	*l = tk;
716 	tk->name = n;
717 	*names = n->link;
718 
719 	return nil;
720 }
721 
722 Tk*
tklook(TkTop * t,char * wp,int parent)723 tklook(TkTop *t, char *wp, int parent)
724 {
725 	Tk *f;
726 	char *p, *q;
727 
728 	if(wp == nil)
729 		return nil;
730 
731 	if(parent) {
732 		p = strdup(wp);
733 		if(p == nil)
734 			return nil;
735 		q = strrchr(p, '.');
736 		if(q == nil)
737 			abort();
738 		if(q == p) {
739 			free(p);
740 			return t->root;
741 		}
742 		*q = '\0';
743 	} else
744 		p = wp;
745 
746 	for(f = t->root; f; f = f->siblings)
747 		if ((f->name != nil) && (strcmp(f->name->name, p) == 0))
748 			break;
749 
750 	if(f != nil && (f->flag & Tkdestroy))
751 		f = nil;
752 
753 	if (parent)
754 		free(p);
755 	return f;
756 }
757 
758 void
tktextsdraw(Image * img,Rectangle r,TkEnv * e,int sbw)759 tktextsdraw(Image *img, Rectangle r, TkEnv *e, int sbw)
760 {
761 	Image *l, *d;
762 	Rectangle s;
763 
764 	draw(img, r, tkgc(e, TkCselectbgnd), nil, ZP);
765 	s.min = r.min;
766 	s.min.x -= sbw;
767 	s.min.y -= sbw;
768 	s.max.x = r.max.x;
769 	s.max.y = r.min.y;
770 	l = tkgc(e, TkCselectbgndlght);
771 	draw(img, s, l, nil, ZP);
772 	s.max.x = s.min.x + sbw;
773 	s.max.y = r.max.y + sbw;
774 	draw(img, s, l, nil, ZP);
775 	s.max = r.max;
776 	s.max.x += sbw;
777 	s.max.y += sbw;
778 	s.min.x = r.min.x;
779 	s.min.y = r.max.y;
780 	d = tkgc(e, TkCselectbgnddark);
781 	draw(img, s, d, nil, ZP);
782 	s.min.x = r.max.x;
783 	s.min.y = r.min.y - sbw;
784 	draw(img, s, d, nil, ZP);
785 }
786 
787 void
tkbox(Image * i,Rectangle r,int bd,Image * fill)788 tkbox(Image *i, Rectangle r, int bd, Image *fill)
789 {
790 	if (bd > 0) {
791 		draw(i, Rect(r.min.x, r.min.y, r.max.x, r.min.y+bd), fill, nil, ZP);
792 		draw(i, Rect(r.min.x, r.min.y+bd, r.min.x+bd, r.max.y-bd), fill, nil, ZP);
793 		draw(i, Rect(r.min.x, r.max.y-bd, r.max.x, r.max.y), fill, nil, ZP);
794 		draw(i, Rect(r.max.x-bd, r.min.y+bd, r.max.x, r.max.y), fill, nil, ZP);
795 	}
796 }
797 
798 void
tkbevel(Image * i,Point o,int w,int h,int bw,Image * top,Image * bottom)799 tkbevel(Image *i, Point o, int w, int h, int bw, Image *top, Image *bottom)
800 {
801 	Rectangle r;
802 	int x, border;
803 
804 	border = 2 * bw;
805 
806 	r.min = o;
807 	r.max.x = r.min.x + w + border;
808 	r.max.y = r.min.y + bw;
809 	draw(i, r, top, nil, ZP);
810 
811 	r.max.x = r.min.x + bw;
812 	r.max.y = r.min.y + h + border;
813 	draw(i, r, top, nil, ZP);
814 
815 	r.max.x = o.x + w + border;
816 	r.max.y = o.y + h + border;
817 	r.min.x = o.x + bw;
818 	r.min.y = r.max.y - bw;
819 	for(x = 0; x < bw; x++) {
820 		draw(i, r, bottom, nil, ZP);
821 		r.min.x--;
822 		r.min.y++;
823 	}
824 	r.min.x = o.x + bw + w;
825 	r.min.y = o.y + bw;
826 	for(x = bw; x >= 0; x--) {
827 		draw(i, r, bottom, nil, ZP);
828 		r.min.x++;
829 		r.min.y--;
830 	}
831 }
832 
833 /*
834  * draw a relief border.
835  * color is an index into tk->env->colors and assumes
836  * light and dark versions following immediately after
837  * that index
838  */
839 void
tkdrawrelief(Image * i,Tk * tk,Point o,int color,int rlf)840 tkdrawrelief(Image *i, Tk *tk, Point o, int color, int rlf)
841 {
842 	TkEnv *e;
843 	Image *l, *d, *t;
844 	int h, w, bd, bd1, bd2;
845 
846 	if(tk->borderwidth == 0)
847 		return;
848 
849 	h = tk->act.height;
850 	w = tk->act.width;
851 
852 	e = tk->env;
853 	if (color == TkCbackgnd || color == TkCselectbgnd || color == TkCactivebgnd) {
854 		l = tkgc(e, color+TkLightshade);
855 		d = tkgc(e, color+TkDarkshade);
856 	} else {
857 		l = tkgshade(e, color, TkLightshade);
858 		d = tkgshade(e, color, TkDarkshade);
859 	}
860 	bd = tk->borderwidth;
861 	if(rlf < 0)
862 		rlf = TKraised;
863 	switch(rlf) {
864 	case TKflat:
865 		break;
866 	case TKsunken:
867 		tkbevel(i, o, w, h, bd, d, l);
868 		break;
869 	case TKraised:
870 		tkbevel(i, o, w, h, bd, l, d);
871 		break;
872 	case TKgroove:
873 		t = d;
874 		d = l;
875 		l = t;
876 		/* fall through */
877 	case TKridge:
878 		bd1 = bd/2;
879 		bd2 = bd - bd1;
880 		if(bd1 > 0)
881 			tkbevel(i, o, w + 2*bd2, h + 2*bd2, bd1, l, d);
882 		o.x += bd1;
883 		o.y += bd1;
884 		tkbevel(i, o, w, h, bd2, d, l);
885 		break;
886 	}
887 }
888 
889 Point
tkstringsize(Tk * tk,char * text)890 tkstringsize(Tk *tk, char *text)
891 {
892 	char *q;
893 	int locked;
894 	Display *d;
895 	Point p, t;
896 
897 	if(text == nil) {
898 		p.x = 0;
899 		p.y = tk->env->font->height;
900 		return p;
901 	}
902 
903 	d = tk->env->top->display;
904 	locked = lockdisplay(d);
905 
906 	p = ZP;
907 	while(*text) {
908 		q = strchr(text, '\n');
909 		if(q != nil)
910 			*q = '\0';
911 		t = stringsize(tk->env->font, text);
912 		p.y += t.y;
913 		if(p.x < t.x)
914 			p.x = t.x;
915 		if(q == nil)
916 			break;
917 		text = q+1;
918 		*q = '\n';
919 	}
920 	if(locked)
921 		unlockdisplay(d);
922 
923 	return p;
924 }
925 
926 static void
tkulall(Image * i,Point o,Image * col,Font * f,char * text)927 tkulall(Image *i, Point o, Image *col, Font *f, char *text)
928 {
929 	Rectangle r;
930 
931 	r.max = stringsize(f, text);
932 	r.max = addpt(r.max, o);
933 	r.min.x = o.x;
934 	r.min.y = r.max.y - 1;
935 	draw(i, r, col, nil, ZP);
936 }
937 
938 static void
tkul(Image * i,Point o,Image * col,int ul,Font * f,char * text)939 tkul(Image *i, Point o, Image *col, int ul, Font *f, char *text)
940 {
941 	char c, *v;
942 	Rectangle r;
943 
944 	v = text+ul+1;
945 	c = *v;
946 	*v = '\0';
947 	r.max = stringsize(f, text);
948 	r.max = addpt(r.max, o);
949 	r.min = stringsize(f, v-1);
950 	*v = c;
951 	r.min.x = r.max.x - r.min.x;
952 	r.min.y = r.max.y - 1;
953 	r.max.y++;
954 	draw(i, r, col, nil, ZP);
955 }
956 
957 void
tkdrawstring(Tk * tk,Image * i,Point o,char * text,int ul,Image * col,int j)958 tkdrawstring(Tk *tk, Image *i, Point o, char *text, int ul, Image *col, int j)
959 {
960 	int n, l, maxl, sox;
961 	char *q, *txt;
962 	Point p;
963 	TkEnv *e;
964 
965 	e = tk->env;
966 	sox = maxl = 0;
967 	if(j != Tkleft){
968 		maxl = 0;
969 		txt = text;
970 		while(*txt){
971 			q = strchr(txt, '\n');
972 			if(q != nil)
973 				*q = '\0';
974 			l = stringwidth(e->font, txt);
975 			if(l > maxl)
976 				maxl = l;
977 			if(q == nil)
978 				break;
979 			txt = q+1;
980 			*q = '\n';
981 		}
982 		sox = o.x;
983 	}
984 	while(*text) {
985 		q = strchr(text, '\n');
986 		if(q != nil)
987 			*q = '\0';
988 		if(j != Tkleft){
989 			o.x = sox;
990 			l = stringwidth(e->font, text);
991 			if(j == Tkcenter)
992 				o.x += (maxl-l)/2;
993 			else
994 				o.x += maxl-l;
995 		}
996 		p = string(i, o, col, o, e->font, text);
997 		if(ul >= 0) {
998 			n = strlen(text);
999 			if(ul < n) {
1000 				tkul(i, o, col, ul, e->font, text);
1001 				ul = -1;
1002 			} else if(ul == n) {
1003 				tkulall(i, o, col, e->font, text);
1004 				ul = -1;
1005 			} else
1006 				ul -= n;
1007 		}
1008 		o.y += e->font->height;
1009 		if(q == nil)
1010 			break;
1011 		text = q+1;
1012 		*q = '\n';
1013 	}
1014 }
1015 
1016 /* for debugging */
1017 char*
tkname(Tk * tk)1018 tkname(Tk *tk)
1019 {
1020 	if(tk == nil)
1021 		return "(nil)";
1022 	if(tk->name == nil)
1023 		return "(noname)";
1024 	return tk->name->name;
1025 }
1026 
1027 Tk*
tkdeliver(Tk * tk,int event,void * data)1028 tkdeliver(Tk *tk, int event, void *data)
1029 {
1030 	Tk *dest;
1031 
1032 	if(tk != nil && ((ulong)tk->type >= TKwidgets || (ulong)tk->name < 4096 && tk->name != nil)){
1033 		print("invalid Tk: type %d name %p\n", tk->type, tk->name);
1034 		abort();
1035 	}
1036 //print("tkdeliver %v to %s\n", event, tkname(tk));
1037 	if(tk == nil || ((tk->flag&Tkdestroy) && event != TkDestroy))
1038 		return tk;
1039 	if(event&(TkFocusin|TkFocusout) && (tk->flag&Tktakefocus))
1040 		tk->dirty = tkrect(tk, 1);
1041 
1042 	if (tkmethod[tk->type]->deliver != nil) {
1043 		dest = tkmethod[tk->type]->deliver(tk, event, data);
1044 		if (dest == nil)
1045 			return tk;
1046 		tkdirty(tk);
1047 		return dest;
1048 	}
1049 
1050 	if((tk->flag & Tkdisabled) == 0)
1051 		tksubdeliver(tk, tk->binds, event, data, 0);
1052 	tkdirty(tk);
1053 	return tk;
1054 }
1055 
1056 static int
nullop(char * fmt,...)1057 nullop(char *fmt, ...)
1058 {
1059 	USED(fmt);
1060 	return 0;
1061 }
1062 
1063 int
tksubdeliver(Tk * tk,TkAction * binds,int event,void * data,int extn)1064 tksubdeliver(Tk *tk, TkAction *binds, int event, void *data, int extn)
1065 {
1066 
1067 	TkAction *a;
1068 	int delivered, genkey, delivered2, iskey;
1069 //int (*debug)(char *fmt, ...);
1070 
1071 	if (!extn)
1072 		return tkextndeliver(tk, binds, event, data);
1073 
1074 //debug = (tk->name && !strcmp(tk->name->name, ".cd")) ? print : nullop;
1075 //debug("subdeliver %v\n", event);
1076 
1077 	if (event & TkTakefocus) {
1078 		if (tk->flag & Tktakefocus)
1079 			tksetkeyfocus(tk->env->top, tk, 0);
1080 		return TkDdelivered;
1081 	}
1082 
1083 	delivered = TkDnone;
1084 	genkey = 0;
1085 	for(a = binds; a != nil; a = a->link) {
1086 		if(event == a->event) {
1087 //debug("  exact match on %v\n", a->event);
1088 			tkcmdbind(tk, event, a->arg, data);
1089 			delivered = TkDdelivered;
1090 		} else if (a->event == TkKey && (a->type>>8)==TkAadd)
1091 			genkey = 1;
1092 	}
1093 	if(delivered != TkDnone && !((event & TkKey) && genkey))
1094 		return delivered;
1095 
1096 	delivered2 = delivered;
1097 	for(a = binds; a != nil; a = a->link) {
1098 		/*
1099 		 * only bind to non-specific key events; if a specific
1100 		 * key event has already been delivered, only deliver event if
1101 		 * the non-specific binding was added. (TkAadd)
1102 		 */
1103 		if (a->event & TkExtns)
1104 			continue;
1105 		iskey = (a->event & TkKey);
1106 		if (iskey ^ (event & TkKey))
1107 			continue;
1108 		if(iskey && (TKKEY(a->event) != 0
1109 					|| ((a->type>>8) != TkAadd && delivered != TkDnone)))
1110 			continue;
1111 		if(!iskey && (a->event & TkMotion) && (a->event&TkEpress) != 0)
1112 			continue;
1113 		if(!(event & TkDouble) && (a->event & TkDouble))
1114 			continue;
1115 		if((event & ~TkDouble) & a->event) {
1116 //debug("  partial match on %v\n", a->event);
1117 			tkcmdbind(tk, event, a->arg, data);
1118 			delivered2 = TkDdelivered;
1119 		}
1120 	}
1121 	return delivered2;
1122 }
1123 
1124 void
tkcancel(TkAction ** l,int event)1125 tkcancel(TkAction **l, int event)
1126 {
1127 	TkAction *a;
1128 
1129 	for(a = *l; a; a = *l) {
1130 		if(a->event == event) {
1131 			*l = a->link;
1132 			a->link = nil;
1133 			tkfreebind(a);
1134 			continue;
1135 		}
1136 		l = &a->link;
1137 	}
1138 }
1139 
1140 static void
tkcancela(TkAction ** l,int event,int type,char * arg)1141 tkcancela(TkAction **l, int event, int type, char *arg)
1142 {
1143 	TkAction *a;
1144 
1145 	for(a = *l; a; a = *l) {
1146 		if(a->event == event && strcmp(a->arg, arg) == 0 && (a->type&0xff) == type){
1147 			*l = a->link;
1148 			a->link = nil;
1149 			tkfreebind(a);
1150 			continue;
1151 		}
1152 		l = &a->link;
1153 	}
1154 }
1155 
1156 char*
tkaction(TkAction ** l,int event,int type,char * arg,int how)1157 tkaction(TkAction **l, int event, int type, char *arg, int how)
1158 {
1159 	TkAction *a;
1160 
1161 	if(arg == nil)
1162 		return nil;
1163 	if(how == TkArepl)
1164 		tkcancel(l, event);
1165 	else if(how == TkAadd){
1166 		for(a = *l; a; a = a->link)
1167 			if(a->event == event && strcmp(a->arg, arg) == 0 && (a->type&0xff) == type){
1168 				a->type = type + (how << 8);
1169 				return nil;
1170 			}
1171 	}
1172 	else if(how == TkAsub){
1173 		tkcancela(l, event, type, arg);
1174 		if(type == TkDynamic)	/* should always be the case */
1175 			free(arg);
1176 		return nil;
1177 	}
1178 
1179 	a = malloc(sizeof(TkAction));
1180 	if(a == nil) {
1181 		if(type == TkDynamic)
1182 			free(arg);
1183 		return TkNomem;
1184 	}
1185 
1186 	a->event = event;
1187 	a->arg = arg;
1188 	a->type = type + (how << 8);
1189 
1190 	a->link = *l;
1191 	*l = a;
1192 
1193 	return nil;
1194 }
1195 
1196 char*
tkitem(char * buf,char * a)1197 tkitem(char *buf, char *a)
1198 {
1199 	char *e;
1200 
1201 	while(*a && (*a == ' ' || *a == '\t'))
1202 		a++;
1203 
1204 	e = buf + Tkmaxitem - 1;
1205 	while(*a && *a != ' ' && *a != '\t' && buf < e)
1206 		*buf++ = *a++;
1207 
1208 	*buf = '\0';
1209 	while(*a && (*a == ' ' || *a == '\t'))
1210 		a++;
1211 	return a;
1212 }
1213 
1214 /*
1215  * if tk is a subwindow or a descendent, return the subwindow;
1216  * return nil otherwise
1217  */
1218 Tk*
tkfindsub(Tk * tk)1219 tkfindsub(Tk *tk)
1220 {
1221 	for(; tk != nil; tk = tk->master){
1222 		if(tk->parent != nil)
1223 			return tk;	/* tk->parent is canvas or text */
1224 	}
1225 	return nil;
1226 }
1227 
1228 /*
1229  * Return absolute screen position of tk (just outside its top-left border).
1230  * When a widget is embedded in a text or canvas widget, we need to
1231  * use the text or canvas's relpos() function instead of act{x,y}, and we
1232  * need to folow up the parent pointer rather than the master one.
1233  */
1234 Point
tkposn(Tk * tk)1235 tkposn(Tk *tk)
1236 {
1237 	Tk *f, *last;
1238 	Point g;
1239 
1240 	last = tk;
1241 	if(tk->parent != nil) {
1242 		g = tkmethod[tk->parent->type]->relpos(tk);
1243 		f = tk->parent;
1244 	} else {
1245 		g.x = tk->act.x;
1246 		g.y = tk->act.y;
1247 		f = tk->master;
1248 	}
1249 	while(f != nil) {
1250 		g.x += f->borderwidth;
1251 		g.y += f->borderwidth;
1252 		last = f;
1253 		if(f->parent != nil) {
1254 			g = addpt(g, tkmethod[f->parent->type]->relpos(f));
1255 			f = f->parent;
1256 		} else {
1257 			g.x += f->act.x;
1258 			g.y += f->act.y;
1259 			f = f->master;
1260 		}
1261 	}
1262 	if (last->flag & Tkwindow)
1263 		g = addpt(g, TKobj(TkWin, last)->req);
1264 	return g;
1265 }
1266 
1267 /*
1268  * convert screen coords to local widget coords
1269  */
1270 Point
tkscrn2local(Tk * tk,Point p)1271 tkscrn2local(Tk *tk, Point p)
1272 {
1273 	p = subpt(p, tkposn(tk));
1274 	p.x -= tk->borderwidth;
1275 	p.y -= tk->borderwidth;
1276 	return p;
1277 }
1278 
1279 int
tkvisiblerect(Tk * tk,Rectangle * rr)1280 tkvisiblerect(Tk *tk, Rectangle *rr)
1281 {
1282 	Rectangle r;
1283 	Point g;
1284 	Tk *f, *last;
1285 	g = Pt(tk->borderwidth, tk->borderwidth);
1286 	last = tk;
1287 	if(tk->parent != nil) {
1288 		g = addpt(g, tkmethod[tk->parent->type]->relpos(tk));
1289 		f = tk->parent;
1290 	} else {
1291 		g.x += tk->act.x;
1292 		g.y += tk->act.y;
1293 		f = tk->master;
1294 	}
1295 	if (f == nil) {
1296 		*rr = tkrect(tk, 1);
1297 		return 1;
1298 	}
1299 	r = rectaddpt(tkrect(tk, 1), g);
1300 	while (f) {
1301 		if (!rectclip(&r, tkrect(f, 0)))
1302 			return 0;
1303 		g.x = f->borderwidth;
1304 		g.y = f->borderwidth;
1305 		last = f;
1306 		if (f->parent != nil) {
1307 			g = addpt(g, tkmethod[f->parent->type]->relpos(f));
1308 			f = f->parent;
1309 		} else {
1310 			g.x += f->act.x;
1311 			g.y += f->act.y;
1312 			f = f->master;
1313 		}
1314 		r = rectaddpt(r, g);
1315 	}
1316 	if (last->flag & Tkwindow)
1317 		r = rectaddpt(r, TKobj(TkWin, last)->act);
1318 	/*
1319 	 * now we have the visible rectangle in screen coords;
1320 	 * subtract actx+borderwidth and we've got it back in
1321 	 * widget-local coords again
1322 	 */
1323 	r = rectsubpt(r, tkposn(tk));
1324 	*rr = rectsubpt(r, Pt(tk->borderwidth, tk->borderwidth));
1325 	return 1;
1326 }
1327 
1328 Point
tkanchorpoint(Rectangle r,Point size,int anchor)1329 tkanchorpoint(Rectangle r, Point size, int anchor)
1330 {
1331 	int dx, dy;
1332 	Point p;
1333 
1334 	p = r.min;
1335 	dx = Dx(r) - size.x;
1336 	dy = Dy(r) - size.y;
1337 	if((anchor & (Tknorth|Tksouth)) == 0)
1338 		p.y += dy/2;
1339 	else if(anchor & Tksouth)
1340 		p.y += dy;
1341 
1342 	if((anchor & (Tkeast|Tkwest)) == 0)
1343 		p.x += dx/2;
1344 	else if(anchor & Tkeast)
1345 		p.x += dx;
1346 	return p;
1347 }
1348 
1349 static char*
tkunits(char c,int * d,TkEnv * e)1350 tkunits(char c, int *d, TkEnv *e)
1351 {
1352 	switch(c) {
1353 	default:
1354 		if(c >= '0' || c <= '9' || c == '.')
1355 			break;
1356 		return TkBadvl;
1357 	case '\0':
1358 		break;
1359 	case 'c':		/* Centimeters */
1360 		*d *= (Tkdpi*100)/254;
1361 		break;
1362 	case 'm':		/* Millimeters */
1363 		*d *= (Tkdpi*10)/254;
1364 		break;
1365 	case 'i':		/* Inches */
1366 		*d *= Tkdpi;
1367 		break;
1368 	case 'p':		/* Points */
1369 		*d = (*d*Tkdpi)/72;
1370 		break;
1371 	case 'w':		/* Character width */
1372 		if(e == nil)
1373 			return TkBadvl;
1374 		*d = *d * e->wzero;
1375 		break;
1376 	case 'h':		/* Character height */
1377 		if(e == nil)
1378 			return TkBadvl;
1379 		*d = *d * e->font->height;
1380 		break;
1381 	}
1382 	return nil;
1383 }
1384 
1385 int
TKF2I(int f)1386 TKF2I(int f)
1387 {
1388 	if (f >= 0)
1389 		return (f + Tkfpscalar/2) / Tkfpscalar;
1390 	return (f - Tkfpscalar/2) / Tkfpscalar;
1391 }
1392 
1393 /*
1394  * Parse a floating point number into a decimal fixed point representation
1395  */
1396 char*
tkfrac(char ** arg,int * f,TkEnv * env)1397 tkfrac(char **arg, int *f, TkEnv *env)
1398 {
1399 	int c, minus, i, fscale, seendigit;
1400 	char *p, *e;
1401 
1402 	seendigit = 0;
1403 
1404 	p = *arg;
1405 	p = tkskip(p, " \t");
1406 
1407 	minus = 0;
1408 	if(*p == '-') {
1409 		minus = 1;
1410 		p++;
1411 	}
1412 	i = 0;
1413 	while(*p) {
1414 		c = *p;
1415 		if(c == '.')
1416 			break;
1417 		if(c < '0' || c > '9')
1418 			break;
1419 		i = i*10 + (c - '0');
1420 		seendigit = 1;
1421 		p++;
1422 	}
1423 	i *= Tkfpscalar;
1424 	if(*p == '.')
1425 		p++;
1426 	fscale = Tkfpscalar;
1427 	while(*p && *p >= '0' && *p <= '9') {
1428 		fscale /= 10;
1429 		i += fscale * (*p++ - '0');
1430 		seendigit = 1;
1431 	}
1432 
1433 	if(minus)
1434 		i = -i;
1435 
1436 	if(!seendigit)
1437 		return TkBadvl;
1438 	e = tkunits(*p, &i, env);
1439 	if (e != nil)
1440 		return e;
1441 	while (*p && *p != ' ' && *p != '\t')
1442 		p++;
1443 	*arg = p;
1444 	*f = i;
1445 	return nil;
1446 }
1447 
1448 char*
tkfracword(TkTop * t,char ** arg,int * f,TkEnv * env)1449 tkfracword(TkTop *t, char **arg, int *f, TkEnv *env)
1450 {
1451 	char *p;
1452 	char buf[Tkminitem];
1453 
1454 	*arg = tkword(t, *arg, buf, buf+sizeof(buf), nil);
1455 	p = buf;
1456 	return tkfrac(&p, f, env);
1457 }
1458 
1459 char*
tkfprint(char * v,int frac)1460 tkfprint(char *v, int frac)
1461 {
1462 	int fscale;
1463 
1464 	if(frac < 0) {
1465 		*v++ = '-';
1466 		frac = -frac;
1467 	}
1468 	v += sprint(v, "%d", frac/Tkfpscalar);
1469 	frac = frac%Tkfpscalar;
1470 	if(frac != 0)
1471 		*v++ = '.';
1472 	fscale = Tkfpscalar/10;
1473 	while(frac) {
1474 		*v++ = '0' + frac/fscale;
1475 		frac %= fscale;
1476 		fscale /= 10;
1477 	}
1478 	*v = '\0';
1479 	return v;
1480 }
1481 
1482 char*
tkvalue(char ** val,char * fmt,...)1483 tkvalue(char **val, char *fmt, ...)
1484 {
1485 	va_list arg;
1486 	Fmt fmtx;
1487 
1488 	if(val == nil)
1489 		return nil;
1490 
1491 	fmtstrinit(&fmtx);
1492 	if(*val != nil)
1493 		if(fmtprint(&fmtx, "%s", *val) < 0)
1494 			return TkNomem;
1495 	va_start(arg, fmt);
1496 	fmtvprint(&fmtx, fmt, arg);
1497 	va_end(arg);
1498 	free(*val);
1499 	*val = fmtstrflush(&fmtx);
1500 	if(*val == nil)
1501 		return TkNomem;
1502 	return nil;
1503 }
1504 
1505 static char*
tkwidgetcmd(TkTop * t,Tk * tk,char * arg,char ** val)1506 tkwidgetcmd(TkTop *t, Tk *tk, char *arg, char **val)
1507 {
1508 	TkMethod *cm;
1509 	TkCmdtab *ct;
1510 	int bot, top, new, r;
1511 	char *e, *buf;
1512 
1513 	buf = mallocz(Tkmaxitem, 0);
1514 	if(buf == nil)
1515 		return TkNomem;
1516 
1517 	arg = tkword(t, arg, buf, buf+Tkmaxitem, nil);
1518 	if(val != nil)
1519 		*val = nil;
1520 
1521 	cm = tkmethod[tk->type];
1522 
1523 	e = TkBadcm;
1524 	bot = 0;
1525 	top = cm->ncmd - 1;
1526 
1527 	while(bot <= top) {
1528 		new = (bot + top)/2;
1529 		ct = &cm->cmd[new];
1530 		r = strcmp(ct->name, buf);
1531 		if(r == 0) {
1532 			e = ct->fn(tk, arg, val);
1533 			break;
1534 		}
1535 		if(r < 0)
1536 			bot = new + 1;
1537 		else
1538 			top = new - 1;
1539 	}
1540 	free(buf);
1541 	tkdirty(tk);
1542 	return e;
1543 }
1544 
1545 Rectangle
tkrect(Tk * tk,int withborder)1546 tkrect(Tk *tk, int withborder)
1547 {
1548 	Rectangle r;
1549 	int bd;
1550 
1551 	bd = withborder? tk->borderwidth: 0;
1552 	r.min.x = -bd;
1553 	r.min.y = -bd;
1554 	r.max.x = tk->act.width + bd;
1555 	r.max.y = tk->act.height + bd;
1556 	return r;
1557 }
1558 
1559 void
tkdirty(Tk * tk)1560 tkdirty(Tk *tk)
1561 {
1562 	Tk *sub;
1563 	Point rel;
1564 	Rectangle dirty;
1565 	int isdirty, transparent;
1566 
1567 	/*
1568 	 * mark as dirty all views underneath a dirty transparent widget
1569 	 *	down to the first opaque widget.
1570 	 * inform parents about any dirtiness.
1571 
1572 	 * XXX as Tksubsub never gets reset, testing against Tksubsub doesn't *exactly* test
1573 	 * whether we're in a canvas/text widget, but merely
1574 	 * whether it has ever been. Tksubsub should probably be reset on unpack.
1575 	 */
1576 	isdirty = Dx(tk->dirty) > 0;
1577 	transparent = tk->flag & Tktransparent;
1578 	sub = tk;
1579 	while (isdirty && ((tk->flag&Tksubsub) || transparent)) {
1580 		if (tk->master != nil) {
1581 			if (transparent) {
1582 				rel.x = tk->act.x + tk->borderwidth;
1583 				rel.y = tk->act.y + tk->borderwidth;
1584 				dirty = rectaddpt(sub->dirty, rel);
1585 				sub = tk->master;
1586 				combinerect(&sub->dirty, dirty);
1587 				transparent = sub->flag & Tktransparent;
1588 			}
1589 			tk = tk->master;
1590 		} else if (tk->parent != nil) {
1591 			tkmethod[tk->parent->type]->dirtychild(sub);
1592 			tk = sub = tk->parent;
1593 			isdirty = Dx(sub->dirty) > 0;
1594 			transparent = sub->flag & Tktransparent;
1595 		} else
1596 			break;
1597 	}
1598 }
1599 
1600 static int
qcmdcmp(const void * a,const void * b)1601 qcmdcmp(const void *a, const void *b)
1602 {
1603 	return strcmp(((TkCmdtab*)a)->name, ((TkCmdtab*)b)->name);
1604 }
1605 
1606 void
tksorttable(void)1607 tksorttable(void)
1608 {
1609 	int i;
1610 	TkMethod *c;
1611 	TkCmdtab *cmd;
1612 
1613 	for(i = 0; i < TKwidgets; i++) {
1614 		c = tkmethod[i];
1615 		if(c->cmd == nil)
1616 			continue;
1617 
1618 		for(cmd = c->cmd; cmd->name != nil; cmd++)
1619 			;
1620 		c->ncmd = cmd - c->cmd;
1621 
1622 		qsort(c->cmd, c->ncmd, sizeof(TkCmdtab), qcmdcmp);
1623 	}
1624 }
1625 
1626 static char*
tksinglecmd(TkTop * t,char * arg,char ** val)1627 tksinglecmd(TkTop *t, char *arg, char **val)
1628 {
1629 	Tk *tk;
1630 	int bot, top, new;
1631 	char *e, *buf;
1632 
1633 	if(t->debug)
1634 		print("tk: '%s'\n", arg);
1635 
1636 	buf = mallocz(Tkmaxitem, 0);
1637 	if(buf == nil)
1638 		return TkNomem;
1639 
1640 	arg = tkword(t, arg, buf, buf+Tkmaxitem, nil);
1641 	switch(buf[0]) {
1642 	case '\0':
1643 		free(buf);
1644 		return nil;
1645 	case '.':
1646 		tk = tklook(t, buf, 0);
1647 		if(tk == nil){
1648 			tkerr(t, buf);
1649 			free(buf);
1650 			return TkBadwp;
1651 		}
1652 		e = tkwidgetcmd(t, tk, arg, val);
1653 		free(buf);
1654 		return e;
1655 	}
1656 
1657 	bot = 0;
1658 	top = nelem(cmdmain) - 1;
1659 	e = TkBadcm;
1660 	while(bot <= top) {
1661 		int rc;
1662 		new = (bot + top)/2;
1663 		rc = strcmp(cmdmain[new].name, buf);
1664 		if(!rc) {
1665 			e = cmdmain[new].fn(t, arg, val);
1666 			break;
1667 		}
1668 
1669 		if(rc < 0)
1670 			bot = new + 1;
1671 		else
1672 			top = new - 1;
1673 	}
1674 	free(buf);
1675 	return e;
1676 }
1677 
1678 static char*
tkmatch(int inc,int dec,char * p)1679 tkmatch(int inc, int dec, char *p)
1680 {
1681 	int depth, esc, c;
1682 
1683 	esc = 0;
1684 	depth = 1;
1685 	while(*p) {
1686 		c = *p;
1687 		if(esc == 0) {
1688 			if(c == inc)
1689 				depth++;
1690 			if(c == dec)
1691 				depth--;
1692 			if(depth == 0)
1693 				return p;
1694 		}
1695 		if(c == '\\' && esc == 0)
1696 			esc = 1;
1697 		else
1698 			esc = 0;
1699 		p++;
1700 	}
1701 	return nil;
1702 }
1703 
1704 char*
tkexec(TkTop * t,char * arg,char ** val)1705 tkexec(TkTop *t, char *arg, char **val)
1706 {
1707 	int cmdsz, n;
1708 	char *p, *cmd, *e, *c;
1709 
1710 	if(t->execdepth >= 0 && ++t->execdepth > 128)
1711 		return TkDepth;
1712 
1713 	cmd = nil;
1714 	cmdsz = 0;
1715 
1716 	p = arg;
1717 	for(;;) {
1718 		switch(*p++) {
1719 		case '[':
1720 			p = tkmatch('[', ']', p);
1721 			if(p == nil){
1722 				free(cmd);
1723 				return TkSyntx;
1724 			}
1725 			break;
1726 		case '{':
1727 			p = tkmatch('{', '}', p);
1728 			if(p == nil){
1729 				free(cmd);
1730 				return TkSyntx;
1731 			}
1732 			break;
1733 		case ';':
1734 			n = p - arg - 1;
1735 			if(cmdsz < n)
1736 				cmdsz = n;
1737 			c = realloc(cmd, cmdsz+1);
1738 			if(c == nil){
1739 				free(cmd);
1740 				return TkNomem;
1741 			}
1742 			cmd = c;
1743 			memmove(cmd, arg, n);
1744 			cmd[n] = '\0';
1745 			e = tksinglecmd(t, cmd, nil);
1746 			if(e != nil) {
1747 				t->err = e;
1748 				strncpy(t->errcmd, cmd, sizeof(t->errcmd));
1749 				t->errcmd[sizeof(t->errcmd)-1] = '\0';
1750 				free(cmd);
1751 				return e;
1752 			}
1753 			arg = p;
1754 			break;
1755 		case '\0':
1756 		case '\'':
1757 			free(cmd);
1758 			e = tksinglecmd(t, arg, val);
1759 			if(e != nil) {
1760 				t->err = e;
1761 				strncpy(t->errcmd, arg, sizeof(t->errcmd));
1762 				t->errcmd[sizeof(t->errcmd)-1] = '\0';
1763 			}
1764 			return e;
1765 		}
1766 	}
1767 }
1768 
1769 static struct {
1770 	char *name;
1771 	int mask;
1772 } events[] = {
1773 	"Button1P",	TkButton1P,
1774 	"Button1R",	TkButton1R,
1775 	"Button2P",	TkButton2P,
1776 	"Button2R",	TkButton2R,
1777 	"Button3P",	TkButton3P,
1778 	"Button3R",	TkButton3R,
1779 	"Button4P",	TkButton4P,
1780 	"Button4R",	TkButton4R,
1781 	"Button5P",	TkButton5P,
1782 	"Button5R",	TkButton5R,
1783 	"Button6P",	TkButton6P,
1784 	"Button6R",	TkButton6R,
1785 	"Extn1",		TkExtn1,
1786 	"Extn2",		TkExtn2,
1787 	"Takefocus",	TkTakefocus,
1788 	"Destroy",		TkDestroy,
1789 	"Enter",		TkEnter,
1790 	"Leave",		TkLeave,
1791 	"Motion",		TkMotion,
1792 	"Map",		TkMap,
1793 	"Unmap",		TkUnmap,
1794 	"Key",		TkKey,
1795 	"Focusin",		TkFocusin,
1796 	"Focusout",	TkFocusout,
1797 	"Configure",	TkConfigure,
1798 	"Double",		TkDouble,
1799 	0
1800 };
1801 
1802 int
tkeventfmt(Fmt * f)1803 tkeventfmt(Fmt *f)
1804 {
1805 	int k, i, d;
1806 	int e;
1807 
1808 	e = va_arg(f->args, int);
1809 
1810 	if ((f->flags & FmtSharp) && e == TkMotion)
1811 		return 0;
1812 	fmtprint(f, "<");
1813 	k = -1;
1814 	if (e & TkKey) {
1815 		k = e & 0xffff;
1816 		e &= ~0xffff;
1817 	}
1818 	d = 0;
1819 	for (i = 0; events[i].name; i++) {
1820 		if (e & events[i].mask) {
1821 			if (d++)
1822 				fmtprint(f, "|");
1823 			fmtprint(f, "%s", events[i].name);
1824 		}
1825 	}
1826 	if (k != -1) {
1827 		fmtprint(f, "[%c]", k);
1828 	} else if (e == 0)
1829 		fmtprint(f, "Noevent");
1830 	fmtprint(f, ">");
1831 	return 0;
1832 }
1833 
1834 void
tkerr(TkTop * t,char * e)1835 tkerr(TkTop *t, char *e)
1836 {
1837 	if(t != nil && e != nil){
1838 		strncpy(t->errx, e, sizeof(t->errx));
1839 		t->errx[sizeof(t->errx)-1] = '\0';
1840 	}
1841 }
1842 
1843 char*
tkerrstr(TkTop * t,char * e)1844 tkerrstr(TkTop *t, char *e)
1845 {
1846 	char *s = malloc(strlen(e)+1+strlen(t->errx)+1);
1847 
1848 	if(s == nil)
1849 		return nil;
1850 	strcpy(s, e);
1851 	if(*e == '!'){
1852 		strcat(s, " ");
1853 		strcat(s, t->errx);
1854 	}
1855 	t->errx[0] = '\0';
1856 	return s;
1857 }
1858 
1859 char*
tksetmgrab(TkTop * t,Tk * tk)1860 tksetmgrab(TkTop *t, Tk *tk)
1861 {
1862 	Tk *omgrab;
1863 	TkCtxt *c;
1864 	c = t->ctxt;
1865 	if (tk == nil) {
1866 		omgrab = c->mgrab;
1867 		c->mgrab = nil;
1868 		/*
1869 		 * don't enterleave if grab reset would cause no leave event
1870 		 */
1871 		if (!(omgrab != nil && (omgrab->flag & Tknograb) &&
1872 				c->entered != nil && (c->entered->flag & Tknograb)))
1873 			tkenterleave(t);
1874 	} else {
1875 		if (c->focused && c->mfocus != nil && c->mfocus->env->top != tk->env->top)
1876 			return "!grab already taken on another toplevel";
1877 		c->mgrab = tk;
1878 		if (tk->flag & Tknograb) {
1879 			if (c->focused) {
1880 				c->focused = 0;
1881 				c->mfocus = nil;
1882 			}
1883 		} else if (c->focused || c->mstate.b != 0) {
1884 			c->focused = 1;
1885 			c->mfocus = tk;
1886 		}
1887 //print("setmgrab(%s) focus now %s\n", tkname(tk), tkname(c->mfocus));
1888 		tkenterleave(t);
1889 	}
1890 	return nil;
1891 }
1892 
1893 int
tkinsidepoly(Point * poly,int np,int winding,Point p)1894 tkinsidepoly(Point *poly, int np, int winding, Point p)
1895 {
1896 	Point pi, pj;
1897 	int i, j, hit;
1898 
1899 	hit = 0;
1900 	j = np - 1;
1901 	for(i = 0; i < np; j = i++) {
1902 		pi = poly[i];
1903 		pj = poly[j];
1904 		if((pi.y <= p.y && p.y < pj.y || pj.y <= p.y && p.y < pi.y) &&
1905 				p.x < (pj.x - pi.x) * (p.y - pi.y) / (pj.y - pi.y) + pi.x) {
1906 			if(winding == 1 || pi.y > p.y)
1907 				hit++;
1908 			else
1909 				hit--;
1910 		}
1911 	}
1912 	return (hit & winding) != 0;
1913 }
1914 
1915 int
tklinehit(Point * a,int np,int w,Point p)1916 tklinehit(Point *a, int np, int w, Point p)
1917 {
1918 	Point *b;
1919 	int z, nx, ny, nrm;
1920 
1921 	while(np-- > 1) {
1922 		b = a+1;
1923 		nx = a->y - b->y;
1924 		ny = b->x - a->x;
1925 		nrm = (nx < 0? -nx : nx) + (ny < 0? -ny : ny);
1926 		if(nrm)
1927 			z = (p.x-b->x)*nx/nrm + (p.y-b->y)*ny/nrm;
1928 		else
1929 			z = (p.x-b->x) + (p.y-b->y);
1930 		if(z < 0)
1931 			z = -z;
1932 		if(z < w)
1933 			return 1;
1934 		a++;
1935 	}
1936 	return 0;
1937 }
1938 
1939 int
tkiswordchar(int c)1940 tkiswordchar(int c)
1941 {
1942 	return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_' || c >= 0xA0;
1943 }
1944 
1945 int
tkhaskeyfocus(Tk * tk)1946 tkhaskeyfocus(Tk *tk)
1947 {
1948 	if (tk == nil || tk->env->top->focused == 0)
1949 		return 0;
1950 	return tk == tk->env->top->ctxt->tkkeygrab;
1951 }
1952 
1953 static int
rptactive(void * v)1954 rptactive(void *v)
1955 {
1956 	int id = (int)v;
1957 	if (id == rptid)
1958 		return 1;
1959 	return 0;
1960 }
1961 
1962 static int
ckrpt(void * v,int interval)1963 ckrpt(void *v, int interval)
1964 {
1965 	int id = (int)v;
1966 	if (id != rptid)
1967 		return -1;
1968 	if (interval < rptto)
1969 		return 0;
1970 	return 1;
1971 }
1972 
1973 static void
dorpt(void * v)1974 dorpt(void *v)
1975 {
1976 	int id = (int)v;
1977 
1978 	if (id == rptid) {
1979 		rptto = rptint;
1980 		(*rptcb)(rptw, rptnote, 0);
1981 		if (rptint <= 0) {
1982 			rptid++;
1983 			rptw = nil;
1984 		}
1985 	}
1986 }
1987 
1988 void
tkcancelrepeat(Tk * tk)1989 tkcancelrepeat(Tk *tk)
1990 {
1991 	if (tk == rptw) {
1992 		rptid++;
1993 		rptw = nil;
1994 	}
1995 }
1996 
1997 void
tkrepeat(Tk * tk,void (* callback)(Tk *,void *,int),void * note,int pause,int interval)1998 tkrepeat(Tk *tk, void (*callback)(Tk*, void*, int), void *note, int pause, int interval)
1999 {
2000 	rptid++;
2001 	if (tk != rptw && rptw != nil)
2002 		/* existing callback being replaced- report to owner */
2003 		(*rptcb)(rptw, rptnote, 1);
2004 	rptw = tk;
2005 	if (tk == nil || callback == nil)
2006 		return;
2007 	rptnote = note;
2008 	rptcb = callback;
2009 	rptto = pause;
2010 	rptint = interval;
2011 	if (!autorpt)
2012 		autorpt = rptproc("autorepeat", TkRptclick, (void*)rptid, rptactive, ckrpt, dorpt);
2013 	else
2014 		rptwakeup((void*)rptid, autorpt);
2015 }
2016 
2017 static int
blinkactive(void * v)2018 blinkactive(void *v)
2019 {
2020 	USED(v);
2021 	return blinkw != nil;
2022 }
2023 
2024 static int
ckblink(void * v,int interval)2025 ckblink(void *v, int interval)
2026 {
2027 	USED(v);
2028 	USED(interval);
2029 
2030 	if (blinkw == nil)
2031 		return -1;
2032 	if (blinkignore) {
2033 		blinkignore = 0;
2034 		return 0;
2035 	}
2036 	return 1;
2037 }
2038 
2039 static void
doblink(void * v)2040 doblink(void *v)
2041 {
2042 	USED(v);
2043 
2044 	if (blinkw == nil)
2045 		return;
2046 	blinkcb(blinkw, blinkon++ & 1);
2047 	tkupdate(blinkw->env->top);
2048 }
2049 
2050 void
tkblinkreset(Tk * tk)2051 tkblinkreset(Tk *tk)
2052 {
2053 	if (blinkw == tk) {
2054 		blinkignore = 1;
2055 		blinkon = 0;
2056 	}
2057 }
2058 
2059 void
tkblink(Tk * tk,void (* callback)(Tk *,int))2060 tkblink(Tk *tk, void (*callback)(Tk*, int))
2061 {
2062 	if (tk == nil || callback == nil) {
2063 		blinkw = nil;
2064 		return;
2065 	}
2066 	blinkw = tk;
2067 	blinkcb = callback;
2068 	if (!blinkrpt)
2069 		blinkrpt = rptproc("blinker", TkBlinkinterval, nil, blinkactive, ckblink, doblink);
2070 	else
2071 		rptwakeup(nil, blinkrpt);
2072 }
2073 
2074 /*
2075  * debugging
2076  */
2077 void
tkdump(Tk * tk)2078 tkdump(Tk *tk)
2079 {
2080 	Tk *sl;
2081 
2082 	if(tk == nil)
2083 		return;
2084 	if((uint)tk->type < TKwidgets)
2085 		print("%s", tkmethod[tk->type]->name);
2086 	else
2087 		print("TYPE#%#ux", tk->type);
2088 	if(tk->name == nil || (ulong)tk->name < 512)
2089 		print(" NAME %p", tk->name);
2090 	else
2091 		print(" %s", tkname(tk));
2092 	print(" # tk %#p flag %#ux grid %#p", tk, tk->flag, tk->grid);
2093 	if(tk->parent != nil)
2094 		print(" parent [%#p %q]", tk->parent, tkname(tk->parent));
2095 	if(tk->master != nil)
2096 		print(" master [%#p %q]", tk->master, tkname(tk->master));
2097 	if(tk->slave != nil){
2098 		print(" slaves");
2099 		for(sl = tk->slave; sl != nil; sl = sl->next)
2100 			print(" [%#p %q]", sl, tkname(sl));
2101 	}
2102 	print("\n");
2103 	if(tk->type != TKcanvas)
2104 		return;
2105 	tkcvsdump(tk);
2106 }
2107 
2108 void
tktopdump(Tk * tk)2109 tktopdump(Tk *tk)
2110 {
2111 	TkTop *top;
2112 	Tk *sub;
2113 
2114 	if(tk == nil || tk->env == nil){
2115 		print("# %#p no top\n", tk);
2116 		return;
2117 	}
2118 	top = tk->env->top;
2119 	print("# env %#p top %#p\n", tk->env, top);
2120 	if(top != nil){
2121 		for(sub = top->root; sub != nil; sub = sub->siblings)
2122 			tkdump(sub);
2123 	}
2124 }
2125