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