xref: /inferno-os/libtk/utils.c (revision 7668638ccd1063300e675b4df44cea2a2dcdbfa5)
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 void
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 }
1013 
1014 /* for debugging */
1015 char*
1016 tkname(Tk *tk)
1017 {
1018 	return tk ? (tk->name ? tk->name->name : "(noname)") : "(nil)";
1019 }
1020 
1021 Tk*
1022 tkdeliver(Tk *tk, int event, void *data)
1023 {
1024 	Tk *dest;
1025 //print("tkdeliver %v to %s\n", event, tkname(tk));
1026 	if(tk == nil || ((tk->flag&Tkdestroy) && event != TkDestroy))
1027 		return tk;
1028 
1029 	if(event&(TkFocusin|TkFocusout) && (tk->flag&Tktakefocus))
1030 		tk->dirty = tkrect(tk, 1);
1031 
1032 	if (tkmethod[tk->type]->deliver != nil) {
1033 		dest = tkmethod[tk->type]->deliver(tk, event, data);
1034 		if (dest == nil)
1035 			return tk;
1036 		tkdirty(tk);
1037 		return dest;
1038 	}
1039 
1040 	if((tk->flag & Tkdisabled) == 0)
1041 		tksubdeliver(tk, tk->binds, event, data, 0);
1042 	tkdirty(tk);
1043 	return tk;
1044 }
1045 
1046 static int
1047 nullop(char *fmt, ...)
1048 {
1049 	USED(fmt);
1050 	return 0;
1051 }
1052 
1053 int
1054 tksubdeliver(Tk *tk, TkAction *binds, int event, void *data, int extn)
1055 {
1056 
1057 	TkAction *a;
1058 	int delivered, genkey, delivered2, iskey;
1059 //int (*debug)(char *fmt, ...);
1060 	if (!extn)
1061 		return tkextndeliver(tk, binds, event, data);
1062 
1063 //debug = (tk->name && !strcmp(tk->name->name, ".cd")) ? print : nullop;
1064 //debug("subdeliver %v\n", event);
1065 
1066 	if (event & TkTakefocus) {
1067 		if (tk->flag & Tktakefocus)
1068 			tksetkeyfocus(tk->env->top, tk, 0);
1069 		return TkDdelivered;
1070 	}
1071 
1072 	delivered = TkDnone;
1073 	genkey = 0;
1074 	for(a = binds; a != nil; a = a->link) {
1075 		if(event == a->event) {
1076 //debug("  exact match on %v\n", a->event);
1077 			tkcmdbind(tk, event, a->arg, data);
1078 			delivered = TkDdelivered;
1079 		} else if (a->event == TkKey && (a->type>>8)==TkAadd)
1080 			genkey = 1;
1081 	}
1082 	if(delivered != TkDnone && !((event & TkKey) && genkey))
1083 		return delivered;
1084 
1085 	delivered2 = delivered;
1086 	for(a = binds; a != nil; a = a->link) {
1087 		/*
1088 		 * only bind to non-specific key events; if a specific
1089 		 * key event has already been delivered, only deliver event if
1090 		 * the non-specific binding was added. (TkAadd)
1091 		 */
1092 		if (a->event & TkExtns)
1093 			continue;
1094 		iskey = (a->event & TkKey);
1095 		if (iskey ^ (event & TkKey))
1096 			continue;
1097 		if(iskey && (TKKEY(a->event) != 0
1098 					|| ((a->type>>8) != TkAadd && delivered != TkDnone)))
1099 			continue;
1100 		if(!iskey && (a->event & TkMotion) && (a->event&TkEpress) != 0)
1101 			continue;
1102 		if(!(event & TkDouble) && (a->event & TkDouble))
1103 			continue;
1104 		if((event & ~TkDouble) & a->event) {
1105 //debug("  partial match on %v\n", a->event);
1106 			tkcmdbind(tk, event, a->arg, data);
1107 			delivered2 = TkDdelivered;
1108 		}
1109 	}
1110 	return delivered2;
1111 }
1112 
1113 void
1114 tkcancel(TkAction **l, int event)
1115 {
1116 	TkAction *a;
1117 
1118 	for(a = *l; a; a = *l) {
1119 		if(a->event == event) {
1120 			*l = a->link;
1121 			a->link = nil;
1122 			tkfreebind(a);
1123 			continue;
1124 		}
1125 		l = &a->link;
1126 	}
1127 }
1128 
1129 static void
1130 tkcancela(TkAction **l, int event, int type, char *arg)
1131 {
1132 	TkAction *a;
1133 
1134 	for(a = *l; a; a = *l) {
1135 		if(a->event == event && strcmp(a->arg, arg) == 0 && (a->type&0xff) == type){
1136 			*l = a->link;
1137 			a->link = nil;
1138 			tkfreebind(a);
1139 			continue;
1140 		}
1141 		l = &a->link;
1142 	}
1143 }
1144 
1145 char*
1146 tkaction(TkAction **l, int event, int type, char *arg, int how)
1147 {
1148 	TkAction *a;
1149 
1150 	if(arg == nil)
1151 		return nil;
1152 	if(how == TkArepl)
1153 		tkcancel(l, event);
1154 	else if(how == TkAadd){
1155 		for(a = *l; a; a = a->link)
1156 			if(a->event == event && strcmp(a->arg, arg) == 0 && (a->type&0xff) == type){
1157 				a->type = type + (how << 8);
1158 				return nil;
1159 			}
1160 	}
1161 	else if(how == TkAsub){
1162 		tkcancela(l, event, type, arg);
1163 		if(type == TkDynamic)	/* should always be the case */
1164 			free(arg);
1165 		return nil;
1166 	}
1167 
1168 	a = malloc(sizeof(TkAction));
1169 	if(a == nil) {
1170 		if(type == TkDynamic)
1171 			free(arg);
1172 		return TkNomem;
1173 	}
1174 
1175 	a->event = event;
1176 	a->arg = arg;
1177 	a->type = type + (how << 8);
1178 
1179 	a->link = *l;
1180 	*l = a;
1181 
1182 	return nil;
1183 }
1184 
1185 char*
1186 tkitem(char *buf, char *a)
1187 {
1188 	char *e;
1189 
1190 	while(*a && (*a == ' ' || *a == '\t'))
1191 		a++;
1192 
1193 	e = buf + Tkmaxitem - 1;
1194 	while(*a && *a != ' ' && *a != '\t' && buf < e)
1195 		*buf++ = *a++;
1196 
1197 	*buf = '\0';
1198 	while(*a && (*a == ' ' || *a == '\t'))
1199 		a++;
1200 	return a;
1201 }
1202 
1203 int
1204 tkismapped(Tk *tk)
1205 {
1206 	while(tk->master)
1207 		tk = tk->master;
1208 
1209 	/* We need subwindows of text & canvas to appear mapped always
1210 	 * so that the geom function update are seen by the parent
1211 	 * widget
1212 	 */
1213 	if((tk->flag & Tkwindow) == 0)
1214 		return 1;
1215 
1216 	return tk->flag & Tkmapped;
1217 }
1218 
1219 /*
1220  * Return absolute screen position of tk (just outside its top-left border).
1221  * When a widget is embedded in a text or canvas widget, we need to
1222  * use the text or canvas's relpos() function instead of act{x,y}, and we
1223  * need to folow up the parent pointer rather than the master one.
1224  */
1225 Point
1226 tkposn(Tk *tk)
1227 {
1228 	Tk *f, *last;
1229 	Point g;
1230 
1231 	last = tk;
1232 	if(tk->parent != nil) {
1233 		g = tkmethod[tk->parent->type]->relpos(tk);
1234 		f = tk->parent;
1235 	}
1236 	else {
1237 		g.x = tk->act.x;
1238 		g.y = tk->act.y;
1239 		f = tk->master;
1240 	}
1241 	while(f) {
1242 		g.x += f->borderwidth;
1243 		g.y += f->borderwidth;
1244 		last = f;
1245 		if(f->parent != nil) {
1246 			g = addpt(g, tkmethod[f->parent->type]->relpos(f));
1247 			f = f->parent;
1248 		}
1249 		else {
1250 			g.x += f->act.x;
1251 			g.y += f->act.y;
1252 			f = f->master;
1253 		}
1254 	}
1255 	if (last->flag & Tkwindow)
1256 		g = addpt(g, TKobj(TkWin, last)->req);
1257 	return g;
1258 }
1259 
1260 /*
1261  * convert screen coords to local widget coords
1262  */
1263 Point
1264 tkscrn2local(Tk *tk, Point p)
1265 {
1266 	p = subpt(p, tkposn(tk));
1267 	p.x -= tk->borderwidth;
1268 	p.y -= tk->borderwidth;
1269 	return p;
1270 }
1271 
1272 int
1273 tkvisiblerect(Tk *tk, Rectangle *rr)
1274 {
1275 	Rectangle r;
1276 	Point g;
1277 	Tk *f, *last;
1278 	g = Pt(tk->borderwidth, tk->borderwidth);
1279 	last = tk;
1280 	if(tk->parent != nil) {
1281 		g = addpt(g, tkmethod[tk->parent->type]->relpos(tk));
1282 		f = tk->parent;
1283 	} else {
1284 		g.x += tk->act.x;
1285 		g.y += tk->act.y;
1286 		f = tk->master;
1287 	}
1288 	if (f == nil) {
1289 		*rr = tkrect(tk, 1);
1290 		return 1;
1291 	}
1292 	r = rectaddpt(tkrect(tk, 1), g);
1293 	while (f) {
1294 		if (!rectclip(&r, tkrect(f, 0)))
1295 			return 0;
1296 		g.x = f->borderwidth;
1297 		g.y = f->borderwidth;
1298 		last = f;
1299 		if (f->parent != nil) {
1300 			g = addpt(g, tkmethod[f->parent->type]->relpos(f));
1301 			f = f->parent;
1302 		} else {
1303 			g.x += f->act.x;
1304 			g.y += f->act.y;
1305 			f = f->master;
1306 		}
1307 		r = rectaddpt(r, g);
1308 	}
1309 	if (last->flag & Tkwindow)
1310 		r = rectaddpt(r, TKobj(TkWin, last)->act);
1311 	/*
1312 	 * now we have the visible rectangle in screen coords;
1313 	 * subtract actx+borderwidth and we've got it back in
1314 	 * widget-local coords again
1315 	 */
1316 	r = rectsubpt(r, tkposn(tk));
1317 	*rr = rectsubpt(r, Pt(tk->borderwidth, tk->borderwidth));
1318 	return 1;
1319 }
1320 
1321 Point
1322 tkanchorpoint(Rectangle r, Point size, int anchor)
1323 {
1324 	int dx, dy;
1325 	Point p;
1326 
1327 	p = r.min;
1328 	dx = Dx(r) - size.x;
1329 	dy = Dy(r) - size.y;
1330 	if((anchor & (Tknorth|Tksouth)) == 0)
1331 		p.y += dy/2;
1332 	else if(anchor & Tksouth)
1333 		p.y += dy;
1334 
1335 	if((anchor & (Tkeast|Tkwest)) == 0)
1336 		p.x += dx/2;
1337 	else if(anchor & Tkeast)
1338 		p.x += dx;
1339 	return p;
1340 }
1341 
1342 static char*
1343 tkunits(char c, int *d, TkEnv *e)
1344 {
1345 	switch(c) {
1346 	default:
1347 		if(c >= '0' || c <= '9' || c == '.')
1348 			break;
1349 		return TkBadvl;
1350 	case '\0':
1351 		break;
1352 	case 'c':		/* Centimeters */
1353 		*d *= (Tkdpi*100)/254;
1354 		break;
1355 	case 'm':		/* Millimeters */
1356 		*d *= (Tkdpi*10)/254;
1357 		break;
1358 	case 'i':		/* Inches */
1359 		*d *= Tkdpi;
1360 		break;
1361 	case 'p':		/* Points */
1362 		*d = (*d*Tkdpi)/72;
1363 		break;
1364 	case 'w':		/* Character width */
1365 		if(e == nil)
1366 			return TkBadvl;
1367 		*d = *d * e->wzero;
1368 		break;
1369 	case 'h':		/* Character height */
1370 		if(e == nil)
1371 			return TkBadvl;
1372 		*d = *d * e->font->height;
1373 		break;
1374 	}
1375 	return nil;
1376 }
1377 
1378 int
1379 TKF2I(int f)
1380 {
1381 	if (f >= 0)
1382 		return (f + Tkfpscalar/2) / Tkfpscalar;
1383 	return (f - Tkfpscalar/2) / Tkfpscalar;
1384 }
1385 
1386 /*
1387  * Parse a floating point number into a decimal fixed point representation
1388  */
1389 char*
1390 tkfrac(char **arg, int *f, TkEnv *env)
1391 {
1392 	int c, minus, i, fscale, seendigit;
1393 	char *p, *e;
1394 
1395 	seendigit = 0;
1396 
1397 	p = *arg;
1398 	p = tkskip(p, " \t");
1399 
1400 	minus = 0;
1401 	if(*p == '-') {
1402 		minus = 1;
1403 		p++;
1404 	}
1405 	i = 0;
1406 	while(*p) {
1407 		c = *p;
1408 		if(c == '.')
1409 			break;
1410 		if(c < '0' || c > '9')
1411 			break;
1412 		i = i*10 + (c - '0');
1413 		seendigit = 1;
1414 		p++;
1415 	}
1416 	i *= Tkfpscalar;
1417 	if(*p == '.')
1418 		p++;
1419 	fscale = Tkfpscalar;
1420 	while(*p && *p >= '0' && *p <= '9') {
1421 		fscale /= 10;
1422 		i += fscale * (*p++ - '0');
1423 		seendigit = 1;
1424 	}
1425 
1426 	if(minus)
1427 		i = -i;
1428 
1429 	if(!seendigit)
1430 		return TkBadvl;
1431 	e = tkunits(*p, &i, env);
1432 	if (e != nil)
1433 		return e;
1434 	while (*p && *p != ' ' && *p != '\t')
1435 		p++;
1436 	*arg = p;
1437 	*f = i;
1438 	return nil;
1439 }
1440 
1441 char*
1442 tkfracword(TkTop *t, char **arg, int *f, TkEnv *env)
1443 {
1444 	char *p;
1445 	char buf[Tkminitem];
1446 
1447 	*arg = tkword(t, *arg, buf, buf+sizeof(buf), nil);
1448 	p = buf;
1449 	return tkfrac(&p, f, env);
1450 }
1451 
1452 char*
1453 tkfprint(char *v, int frac)
1454 {
1455 	int fscale;
1456 
1457 	if(frac < 0) {
1458 		*v++ = '-';
1459 		frac = -frac;
1460 	}
1461 	v += sprint(v, "%d", frac/Tkfpscalar);
1462 	frac = frac%Tkfpscalar;
1463 	if(frac != 0)
1464 		*v++ = '.';
1465 	fscale = Tkfpscalar/10;
1466 	while(frac) {
1467 		*v++ = '0' + frac/fscale;
1468 		frac %= fscale;
1469 		fscale /= 10;
1470 	}
1471 	*v = '\0';
1472 	return v;
1473 }
1474 
1475 char*
1476 tkvalue(char **val, char *fmt, ...)
1477 {
1478 	va_list arg;
1479 	Fmt fmtx;
1480 
1481 	if(val == nil)
1482 		return nil;
1483 
1484 	fmtstrinit(&fmtx);
1485 	if(*val != nil)
1486 		if(fmtprint(&fmtx, "%s", *val) < 0)
1487 			return TkNomem;
1488 	va_start(arg, fmt);
1489 	fmtvprint(&fmtx, fmt, arg);
1490 	va_end(arg);
1491 	free(*val);
1492 	*val = fmtstrflush(&fmtx);
1493 	if(*val == nil)
1494 		return TkNomem;
1495 	return nil;
1496 }
1497 
1498 static char*
1499 tkwidgetcmd(TkTop *t, Tk *tk, char *arg, char **val)
1500 {
1501 	TkMethod *cm;
1502 	TkCmdtab *ct;
1503 	int bot, top, new, r;
1504 	char *e, *buf;
1505 
1506 	buf = mallocz(Tkmaxitem, 0);
1507 	if(buf == nil)
1508 		return TkNomem;
1509 
1510 	arg = tkword(t, arg, buf, buf+Tkmaxitem, nil);
1511 	if(val != nil)
1512 		*val = nil;
1513 
1514 	cm = tkmethod[tk->type];
1515 
1516 	e = TkBadcm;
1517 	bot = 0;
1518 	top = cm->ncmd - 1;
1519 
1520 	while(bot <= top) {
1521 		new = (bot + top)/2;
1522 		ct = &cm->cmd[new];
1523 		r = strcmp(ct->name, buf);
1524 		if(r == 0) {
1525 			e = ct->fn(tk, arg, val);
1526 			break;
1527 		}
1528 		if(r < 0)
1529 			bot = new + 1;
1530 		else
1531 			top = new - 1;
1532 	}
1533 	free(buf);
1534 	tkdirty(tk);
1535 	return e;
1536 }
1537 
1538 Rectangle
1539 tkrect(Tk *tk, int withborder)
1540 {
1541 	Rectangle r;
1542 	int bd;
1543 
1544 	bd = withborder? tk->borderwidth: 0;
1545 	r.min.x = -bd;
1546 	r.min.y = -bd;
1547 	r.max.x = tk->act.width + bd;
1548 	r.max.y = tk->act.height + bd;
1549 	return r;
1550 }
1551 
1552 void
1553 tkdirty(Tk *tk)
1554 {
1555 	Tk *sub;
1556 	Point rel;
1557 	Rectangle dirty;
1558 	int isdirty, transparent;
1559 
1560 	/*
1561 	 * mark as dirty all views underneath a dirty transparent widget
1562 	 *	down to the first opaque widget.
1563 	 * inform parents about any dirtiness.
1564 
1565 	 * XXX as Tksubsub never gets reset, testing against Tksubsub doesn't *exactly* test
1566 	 * whether we're in a canvas/text widget, but merely
1567 	 * whether it has ever been. Tksubsub should probably be reset on unpack.
1568 	 */
1569 	isdirty = Dx(tk->dirty) > 0;
1570 	transparent = tk->flag & Tktransparent;
1571 	sub = tk;
1572 	while (isdirty && ((tk->flag&Tksubsub) || transparent)) {
1573 		if (tk->master != nil) {
1574 			if (transparent) {
1575 				rel.x = tk->act.x + tk->borderwidth;
1576 				rel.y = tk->act.y + tk->borderwidth;
1577 				dirty = rectaddpt(sub->dirty, rel);
1578 				sub = tk->master;
1579 				combinerect(&sub->dirty, dirty);
1580 				transparent = sub->flag & Tktransparent;
1581 			}
1582 			tk = tk->master;
1583 		} else if (tk->parent != nil) {
1584 			tkmethod[tk->parent->type]->dirtychild(sub);
1585 			tk = sub = tk->parent;
1586 			isdirty = Dx(sub->dirty) > 0;
1587 			transparent = sub->flag & Tktransparent;
1588 		} else
1589 			break;
1590 	}
1591 }
1592 
1593 static int
1594 qcmdcmp(const void *a, const void *b)
1595 {
1596 	return strcmp(((TkCmdtab*)a)->name, ((TkCmdtab*)b)->name);
1597 }
1598 
1599 void
1600 tksorttable(void)
1601 {
1602 	int i;
1603 	TkMethod *c;
1604 	TkCmdtab *cmd;
1605 
1606 	for(i = 0; i < TKwidgets; i++) {
1607 		c = tkmethod[i];
1608 		if(c->cmd == nil)
1609 			continue;
1610 
1611 		for(cmd = c->cmd; cmd->name != nil; cmd++)
1612 			;
1613 		c->ncmd = cmd - c->cmd;
1614 
1615 		qsort(c->cmd, c->ncmd, sizeof(TkCmdtab), qcmdcmp);
1616 	}
1617 }
1618 
1619 static char*
1620 tksinglecmd(TkTop *t, char *arg, char **val)
1621 {
1622 	Tk *tk;
1623 	int bot, top, new;
1624 	char *e, *buf;
1625 
1626 	if(t->debug)
1627 		print("tk: '%s'\n", arg);
1628 
1629 	buf = mallocz(Tkmaxitem, 0);
1630 	if(buf == nil)
1631 		return TkNomem;
1632 
1633 	arg = tkword(t, arg, buf, buf+Tkmaxitem, nil);
1634 	switch(buf[0]) {
1635 	case '\0':
1636 		free(buf);
1637 		return nil;
1638 	case '.':
1639 		tk = tklook(t, buf, 0);
1640 		if(tk == nil){
1641 			tkerr(t, buf);
1642 			free(buf);
1643 			return TkBadwp;
1644 		}
1645 		e = tkwidgetcmd(t, tk, arg, val);
1646 		free(buf);
1647 		return e;
1648 	}
1649 
1650 	bot = 0;
1651 	top = nelem(cmdmain) - 1;
1652 	e = TkBadcm;
1653 	while(bot <= top) {
1654 		int rc;
1655 		new = (bot + top)/2;
1656 		rc = strcmp(cmdmain[new].name, buf);
1657 		if(!rc) {
1658 			e = cmdmain[new].fn(t, arg, val);
1659 			break;
1660 		}
1661 
1662 		if(rc < 0)
1663 			bot = new + 1;
1664 		else
1665 			top = new - 1;
1666 	}
1667 	free(buf);
1668 	return e;
1669 }
1670 
1671 static char*
1672 tkmatch(int inc, int dec, char *p)
1673 {
1674 	int depth, esc, c;
1675 
1676 	esc = 0;
1677 	depth = 1;
1678 	while(*p) {
1679 		c = *p;
1680 		if(esc == 0) {
1681 			if(c == inc)
1682 				depth++;
1683 			if(c == dec)
1684 				depth--;
1685 			if(depth == 0)
1686 				return p;
1687 		}
1688 		if(c == '\\' && esc == 0)
1689 			esc = 1;
1690 		else
1691 			esc = 0;
1692 		p++;
1693 	}
1694 	return nil;
1695 }
1696 
1697 char*
1698 tkexec(TkTop *t, char *arg, char **val)
1699 {
1700 	int cmdsz, n;
1701 	char *p, *cmd, *e, *c;
1702 
1703 	if(t->execdepth >= 0 && ++t->execdepth > 128)
1704 		return TkDepth;
1705 
1706 	cmd = nil;
1707 	cmdsz = 0;
1708 
1709 	p = arg;
1710 	for(;;) {
1711 		switch(*p++) {
1712 		case '[':
1713 			p = tkmatch('[', ']', p);
1714 			if(p == nil){
1715 				free(cmd);
1716 				return TkSyntx;
1717 			}
1718 			break;
1719 		case '{':
1720 			p = tkmatch('{', '}', p);
1721 			if(p == nil){
1722 				free(cmd);
1723 				return TkSyntx;
1724 			}
1725 			break;
1726 		case ';':
1727 			n = p - arg - 1;
1728 			if(cmdsz < n)
1729 				cmdsz = n;
1730 			c = realloc(cmd, cmdsz+1);
1731 			if(c == nil){
1732 				free(cmd);
1733 				return TkNomem;
1734 			}
1735 			cmd = c;
1736 			memmove(cmd, arg, n);
1737 			cmd[n] = '\0';
1738 			e = tksinglecmd(t, cmd, nil);
1739 			if(e != nil) {
1740 				t->err = e;
1741 				strncpy(t->errcmd, cmd, sizeof(t->errcmd));
1742 				t->errcmd[sizeof(t->errcmd)-1] = '\0';
1743 				free(cmd);
1744 				return e;
1745 			}
1746 			arg = p;
1747 			break;
1748 		case '\0':
1749 		case '\'':
1750 			free(cmd);
1751 			e = tksinglecmd(t, arg, val);
1752 			if(e != nil) {
1753 				t->err = e;
1754 				strncpy(t->errcmd, arg, sizeof(t->errcmd));
1755 				t->errcmd[sizeof(t->errcmd)-1] = '\0';
1756 			}
1757 			return e;
1758 		}
1759 	}
1760 }
1761 
1762 static struct {
1763 	char *name;
1764 	int mask;
1765 } events[] = {
1766 	"Button1P",	TkButton1P,
1767 	"Button1R",	TkButton1R,
1768 	"Button2P",	TkButton2P,
1769 	"Button2R",	TkButton2R,
1770 	"Button3P",	TkButton3P,
1771 	"Button3R",	TkButton3R,
1772 	"Button4P",	TkButton4P,
1773 	"Button4R",	TkButton4R,
1774 	"Button5P",	TkButton5P,
1775 	"Button5R",	TkButton5R,
1776 	"Button6P",	TkButton6P,
1777 	"Button6R",	TkButton6R,
1778 	"Extn1",		TkExtn1,
1779 	"Extn2",		TkExtn2,
1780 	"Takefocus",	TkTakefocus,
1781 	"Destroy",		TkDestroy,
1782 	"Enter",		TkEnter,
1783 	"Leave",		TkLeave,
1784 	"Motion",		TkMotion,
1785 	"Map",		TkMap,
1786 	"Unmap",		TkUnmap,
1787 	"Key",		TkKey,
1788 	"Focusin",		TkFocusin,
1789 	"Focusout",	TkFocusout,
1790 	"Configure",	TkConfigure,
1791 	"Double",		TkDouble,
1792 	0
1793 };
1794 
1795 int
1796 tkeventfmt(Fmt *f)
1797 {
1798 	int k, i, d;
1799 	int e;
1800 
1801 	e = va_arg(f->args, int);
1802 
1803 	if ((f->flags & FmtSharp) && e == TkMotion)
1804 		return 0;
1805 	fmtprint(f, "<");
1806 	k = -1;
1807 	if (e & TkKey) {
1808 		k = e & 0xffff;
1809 		e &= ~0xffff;
1810 	}
1811 	d = 0;
1812 	for (i = 0; events[i].name; i++) {
1813 		if (e & events[i].mask) {
1814 			if (d++)
1815 				fmtprint(f, "|");
1816 			fmtprint(f, "%s", events[i].name);
1817 		}
1818 	}
1819 	if (k != -1) {
1820 		fmtprint(f, "[%c]", k);
1821 	} else if (e == 0)
1822 		fmtprint(f, "Noevent");
1823 	fmtprint(f, ">");
1824 	return 0;
1825 }
1826 
1827 void
1828 tkerr(TkTop *t, char *e)
1829 {
1830 	if(t != nil && e != nil){
1831 		strncpy(t->errx, e, sizeof(t->errx));
1832 		t->errx[sizeof(t->errx)-1] = '\0';
1833 	}
1834 }
1835 
1836 char*
1837 tkerrstr(TkTop *t, char *e)
1838 {
1839 	char *s = malloc(strlen(e)+1+strlen(t->errx)+1);
1840 
1841 	if(s == nil)
1842 		return nil;
1843 	strcpy(s, e);
1844 	if(*e == '!'){
1845 		strcat(s, " ");
1846 		strcat(s, t->errx);
1847 	}
1848 	t->errx[0] = '\0';
1849 	return s;
1850 }
1851 
1852 char*
1853 tksetmgrab(TkTop *t, Tk *tk)
1854 {
1855 	Tk *omgrab;
1856 	TkCtxt *c;
1857 	c = t->ctxt;
1858 	if (tk == nil) {
1859 		omgrab = c->mgrab;
1860 		c->mgrab = nil;
1861 		/*
1862 		 * don't enterleave if grab reset would cause no leave event
1863 		 */
1864 		if (!(omgrab != nil && (omgrab->flag & Tknograb) &&
1865 				c->entered != nil && (c->entered->flag & Tknograb)))
1866 			tkenterleave(t);
1867 	} else {
1868 		if (c->focused && c->mfocus != nil && c->mfocus->env->top != tk->env->top)
1869 			return "!grab already taken on another toplevel";
1870 		c->mgrab = tk;
1871 		if (tk->flag & Tknograb) {
1872 			if (c->focused) {
1873 				c->focused = 0;
1874 				c->mfocus = nil;
1875 			}
1876 		} else if (c->focused || c->mstate.b != 0) {
1877 			c->focused = 1;
1878 			c->mfocus = tk;
1879 		}
1880 //print("setmgrab(%s) focus now %s\n", tkname(tk), tkname(c->mfocus));
1881 		tkenterleave(t);
1882 	}
1883 	return nil;
1884 }
1885 
1886 int
1887 tkinsidepoly(Point *poly, int np, int winding, Point p)
1888 {
1889 	Point pi, pj;
1890 	int i, j, hit;
1891 
1892 	hit = 0;
1893 	j = np - 1;
1894 	for(i = 0; i < np; j = i++) {
1895 		pi = poly[i];
1896 		pj = poly[j];
1897 		if((pi.y <= p.y && p.y < pj.y || pj.y <= p.y && p.y < pi.y) &&
1898 				p.x < (pj.x - pi.x) * (p.y - pi.y) / (pj.y - pi.y) + pi.x) {
1899 			if(winding == 1 || pi.y > p.y)
1900 				hit++;
1901 			else
1902 				hit--;
1903 		}
1904 	}
1905 	return (hit & winding) != 0;
1906 }
1907 
1908 int
1909 tklinehit(Point *a, int np, int w, Point p)
1910 {
1911 	Point *b;
1912 	int z, nx, ny, nrm;
1913 
1914 	while(np-- > 1) {
1915 		b = a+1;
1916 		nx = a->y - b->y;
1917 		ny = b->x - a->x;
1918 		nrm = (nx < 0? -nx : nx) + (ny < 0? -ny : ny);
1919 		if(nrm)
1920 			z = (p.x-b->x)*nx/nrm + (p.y-b->y)*ny/nrm;
1921 		else
1922 			z = (p.x-b->x) + (p.y-b->y);
1923 		if(z < 0)
1924 			z = -z;
1925 		if(z < w)
1926 			return 1;
1927 		a++;
1928 	}
1929 	return 0;
1930 }
1931 
1932 int
1933 tkiswordchar(int c)
1934 {
1935 	return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_' || c >= 0xA0;
1936 }
1937 
1938 int
1939 tkhaskeyfocus(Tk *tk)
1940 {
1941 	if (tk == nil || tk->env->top->focused == 0)
1942 		return 0;
1943 	return tk == tk->env->top->ctxt->tkkeygrab;
1944 }
1945 
1946 static int
1947 rptactive(void *v)
1948 {
1949 	int id = (int)v;
1950 	if (id == rptid)
1951 		return 1;
1952 	return 0;
1953 }
1954 
1955 static int
1956 ckrpt(void *v, int interval)
1957 {
1958 	int id = (int)v;
1959 	if (id != rptid)
1960 		return -1;
1961 	if (interval < rptto)
1962 		return 0;
1963 	return 1;
1964 }
1965 
1966 static void
1967 dorpt(void *v)
1968 {
1969 	int id = (int)v;
1970 
1971 	if (id == rptid) {
1972 		rptto = rptint;
1973 		(*rptcb)(rptw, rptnote, 0);
1974 		if (rptint <= 0) {
1975 			rptid++;
1976 			rptw = nil;
1977 		}
1978 	}
1979 }
1980 
1981 void
1982 tkcancelrepeat(Tk *tk)
1983 {
1984 	if (tk == rptw) {
1985 		rptid++;
1986 		rptw = nil;
1987 	}
1988 }
1989 
1990 void
1991 tkrepeat(Tk *tk, void (*callback)(Tk*, void*, int), void *note, int pause, int interval)
1992 {
1993 	rptid++;
1994 	if (tk != rptw && rptw != nil)
1995 		/* existing callback being replaced- report to owner */
1996 		(*rptcb)(rptw, rptnote, 1);
1997 	rptw = tk;
1998 	if (tk == nil || callback == nil)
1999 		return;
2000 	rptnote = note;
2001 	rptcb = callback;
2002 	rptto = pause;
2003 	rptint = interval;
2004 	if (!autorpt)
2005 		autorpt = rptproc("autorepeat", TkRptclick, (void*)rptid, rptactive, ckrpt, dorpt);
2006 	else
2007 		rptwakeup((void*)rptid, autorpt);
2008 }
2009 
2010 static int
2011 blinkactive(void *v)
2012 {
2013 	USED(v);
2014 	return blinkw != nil;
2015 }
2016 
2017 static int
2018 ckblink(void *v, int interval)
2019 {
2020 	USED(v);
2021 	USED(interval);
2022 
2023 	if (blinkw == nil)
2024 		return -1;
2025 	if (blinkignore) {
2026 		blinkignore = 0;
2027 		return 0;
2028 	}
2029 	return 1;
2030 }
2031 
2032 static void
2033 doblink(void *v)
2034 {
2035 	USED(v);
2036 
2037 	if (blinkw == nil)
2038 		return;
2039 	blinkcb(blinkw, blinkon++ & 1);
2040 	tkupdate(blinkw->env->top);
2041 }
2042 
2043 void
2044 tkblinkreset(Tk *tk)
2045 {
2046 	if (blinkw == tk) {
2047 		blinkignore = 1;
2048 		blinkon = 0;
2049 	}
2050 }
2051 
2052 void
2053 tkblink(Tk *tk, void (*callback)(Tk*, int))
2054 {
2055 	if (tk == nil || callback == nil) {
2056 		blinkw = nil;
2057 		return;
2058 	}
2059 	blinkw = tk;
2060 	blinkcb = callback;
2061 	if (!blinkrpt)
2062 		blinkrpt = rptproc("blinker", TkBlinkinterval, nil, blinkactive, ckblink, doblink);
2063 	else
2064 		rptwakeup(nil, blinkrpt);
2065 }
2066