xref: /inferno-os/libtk/utils.c (revision adad243147f6102c6e975f48951c05745d56b92d)
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 		r = Rect(0, 0, 1, 1);
253 		x = y = 1;
254 	}
255 	/* TO DO: diagonal */
256 	s = r;
257 	if(dir == Tkhorizontal){
258 		n = x;
259 		r.max.y = r.min.y+1;
260 	}else{
261 		n = y;
262 		r.max.x = r.min.x+1;
263 	}
264 	b = mallocz(3*n, 0);
265 	if(b == nil)
266 		return nil;
267 	locked = lockdisplay(d);
268 	i = allocimage(d, r, RGB24, 1, DNofill);
269 	if(i == nil)
270 		goto Ret;
271 	tkrgbavals(pix0, &c0[2], &c0[1], &c0[0], &a);
272 	tkrgbavals(pix1, &c1[2], &c1[1], &c1[0], &a);
273 	for(j = 0; j < 3; j++){
274 		c0[j] <<= 12;
275 		c1[j] <<= 12;
276 		delta[j] = ((c1[j]-c0[j])+(1<<11))/n;
277 	}
278 	e = b+3*n;
279 	for(p = b; p < e; p += 3) {
280 		p[0] = c0[0]>>12;
281 		p[1] = c0[1]>>12;
282 		p[2] = c0[2]>>12;
283 		c0[0] += delta[0];
284 		c0[1] += delta[1];
285 		c0[2] += delta[2];
286 	}
287 	loadimage(i, r, b, 3*n);
288 	replclipr(i, 1, s);
289 Ret:
290 	if(locked)
291 		unlockdisplay(d);
292 	free(b);
293 	return i;
294 }
295 
296 /*
297  * XXX should be in libdraw?
298  */
299 int
300 tkchanhastype(ulong c, int t)
301 {
302 	for(; c; c>>=8)
303 		if(TYPE(c) == t)
304 			return 1;
305 	return 0;
306 }
307 
308 void
309 tksettransparent(Tk *tk, int transparent)
310 {
311 	if (transparent)
312 		tk->flag |= Tktransparent;
313 	else
314 		tk->flag &= ~Tktransparent;
315 }
316 
317 int
318 tkhasalpha(TkEnv *e, int col)
319 {
320 	return (e->colors[col] & 0xff) != 0xff;
321 }
322 
323 Image*
324 tkgc(TkEnv *e, int col)
325 {
326 	return tkcolor(e->top->ctxt, e->colors[col]);
327 }
328 
329 
330 /*
331  * Todo: improve the fixed-point code
332  * the 255 scale factor is used because RGB ranges 0-255
333  */
334 static void
335 rgb2hsv(int r, int g, int b, int *h, int *s, int *v)
336 {
337 	int min, max, delta;
338 
339 	max = r;
340 	if(g > max)
341 		max = g;
342 	if(b > max)
343 		max = b;
344 	min = r;
345 	if(g < min)
346 		min = g;
347 	if(b < min)
348 		min = b;
349 	*v = max;
350 	if (max != 0)
351 		*s = ((max - min)*255) / max;
352 	else
353 		*s = 0;
354 
355 	if (*s == 0) {
356 		*h = 0;	/* undefined */
357 	} else {
358 		delta = max - min;
359 		if (r == max)
360 			*h = (g - b)*255 / delta;
361 		else if (g == max)
362 			*h = (2*255) + ((b - r)*255) / delta;
363 		else if (b == max)
364 			*h = (4*255) + ((r - g)*255)/ delta;
365 		*h *= 60;
366 		if (*h < 0)
367 			*h += 360*255;
368 		*h /= 255;
369 	}
370 }
371 
372 static void
373 hsv2rgb(int h, int s, int v, int *r, int *g, int *b)
374 {
375 	int	i;
376 	int	f,p,q,t;
377 
378 	if (s == 0 && h == 0) {
379 		*r = *g = *b = v;	/* achromatic case */
380 	} else {
381 		if (h >= 360)
382 			h = 0;
383 		i = h / 60;
384 		h *= 255;
385 		h /= 60;
386 
387 		f = h % 255;
388 		p = v * (255 - s);
389 		q = v * (255 - ((s * f)/255));
390 		t = v * (255- ((s * (255 - f))/255));
391 		p /= 255;
392 		q /= 255;
393 		t /= 255;
394 		switch (i) {
395 		case 0: *r = v; *g = t; *b = p; break;
396 		case 1: *r = q; *g = v; *b = p; break;
397 		case 2: *r = p; *g = v; *b = t; break;
398 		case 3: *r = p; *g = q; *b = v; break;
399 		case 4: *r = t; *g = p; *b = v; break;
400 		case 5: *r = v; *g = p; *b = q; break;
401 		}
402 	}
403 }
404 
405 enum {
406 	MINDELTA	= 0x10,
407 	DELTA	= 0x30,
408 };
409 
410 ulong
411 tkrgbashade(ulong rgba, int shade)
412 {
413 	int R, G, B, A, h, s, v, vl, vd;
414 
415 	if (shade == TkSameshade)
416 		return rgba;
417 
418 	tkrgbavals(rgba, &R, &G, &B, &A);
419 	h = s = v = 0;
420 	rgb2hsv(R, G, B, &h, &s, &v);
421 
422 	if (v < MINDELTA) {
423 		vd = v+DELTA;
424 		vl = vd+DELTA;
425 	} else if (v > 255-MINDELTA) {
426 		vl = v-DELTA;
427 		vd = vl-DELTA;
428 	} else {
429 		vl = v+DELTA;
430 		vd = v-DELTA;
431 	}
432 
433 	v = (shade == TkLightshade)?vl:vd;
434 	if (v < 0)
435 		v = 0;
436 	if (v > 255)
437 		v = 255;
438 	hsv2rgb(h, s, v, &R, &G, &B);
439 
440 	return tkrgba(R, G, B, A);
441 }
442 
443 Image*
444 tkgshade(TkEnv *e, int col, int shade)
445 {
446 	ulong rgba;
447 
448 	if (col == TkCbackgnd || col == TkCselectbgnd || col == TkCactivebgnd)
449 		return tkgc(e, col+shade);
450 	rgba = tkrgbashade(e->colors[col], shade);
451 	return tkcolor(e->top->ctxt, rgba);
452 }
453 
454 TkEnv*
455 tknewenv(TkTop *t)
456 {
457 	TkEnv *e;
458 
459 	e = malloc(sizeof(TkEnv));
460 	if(e == nil)
461 		return nil;
462 
463 	e->ref = 1;
464 	e->top = t;
465 	return e;
466 }
467 
468 TkEnv*
469 tkdefaultenv(TkTop *t)
470 {
471 	int locked;
472 	TkEnv *env;
473 	Display *d;
474 
475 	if(t->env != nil) {
476 		t->env->ref++;
477 		return t->env;
478 	}
479 	t->env = malloc(sizeof(TkEnv));
480 	if(t->env == nil)
481 		return nil;
482 
483 	env = t->env;
484 	env->ref = 1;
485 	env->top = t;
486 
487 	if(tkfont == nil)
488 		tkfont = "/fonts/pelm/unicode.8.font";
489 
490 	d = t->display;
491 	env->font = font_open(d, tkfont);
492 	if(env->font == nil) {
493 		static int warn;
494 		if(warn == 0) {
495 			warn = 1;
496 			print("tk: font not found: %s\n", tkfont);
497 		}
498 		env->font = font_open(d, "*default*");
499 		if(env->font == nil) {
500 			free(t->env);
501 			t->env = nil;
502 			return nil;
503 		}
504 	}
505 
506 	locked = lockdisplay(d);
507 	env->wzero = stringwidth(env->font, "0");
508 	if ( env->wzero <= 0 )
509 		env->wzero = env->font->height / 2;
510 	if(locked)
511 		unlockdisplay(d);
512 
513 	tksetenvcolours(env);
514 	return env;
515 }
516 
517 void
518 tkputenv(TkEnv *env)
519 {
520 	Display *d;
521 	int locked;
522 
523 	if(env == nil)
524 		return;
525 
526 	env->ref--;
527 	if(env->ref != 0)
528 		return;
529 
530 	d = env->top->display;
531 	locked = lockdisplay(d);
532 
533 	if(env->font != nil)
534 		font_close(env->font);
535 
536 	if(locked)
537 		unlockdisplay(d);
538 
539 	free(env);
540 }
541 
542 TkEnv*
543 tkdupenv(TkEnv **env)
544 {
545 	Display *d;
546 	TkEnv *e, *ne;
547 
548 	e = *env;
549 	if(e->ref == 1)
550 		return e;
551 
552 	ne = malloc(sizeof(TkEnv));
553 	if(ne == nil)
554 		return nil;
555 
556 	ne->ref = 1;
557 	ne->top = e->top;
558 
559 	d = e->top->display;
560 	memmove(ne->colors, e->colors, sizeof(e->colors));
561 	ne->set = e->set;
562 	ne->font = font_open(d, e->font->name);
563 	ne->wzero = e->wzero;
564 
565 	e->ref--;
566 	*env = ne;
567 	return ne;
568 }
569 
570 Tk*
571 tknewobj(TkTop *t, int type, int n)
572 {
573 	Tk *tk;
574 
575 	tk = malloc(n);
576 	if(tk == 0)
577 		return 0;
578 
579 	tk->type = type;		/* Defaults */
580 	tk->flag = Tktop;
581 	tk->relief = TKflat;
582 	tk->env = tkdefaultenv(t);
583 	if(tk->env == nil) {
584 		free(tk);
585 		return nil;
586 	}
587 
588 	return tk;
589 }
590 
591 void
592 tkfreebind(TkAction *a)
593 {
594 	TkAction *next;
595 
596 	while(a != nil) {
597 		next = a->link;
598 		if((a->type & 0xff) == TkDynamic)
599 			free(a->arg);
600 		free(a);
601 		a = next;
602 	}
603 }
604 
605 void
606 tkfreename(TkName *f)
607 {
608 	TkName *n;
609 
610 	while(f != nil) {
611 		n = f->link;
612 		free(f);
613 		f = n;
614 	}
615 }
616 
617 void
618 tkfreeobj(Tk *tk)
619 {
620 	TkCtxt *c;
621 
622 	c = tk->env->top->ctxt;
623 	if(c != nil) {
624 		if(c->tkkeygrab == tk)
625 			c->tkkeygrab = nil;
626 		if(c->mgrab == tk)
627 			tksetmgrab(tk->env->top, nil);
628 		if(c->mfocus == tk)
629 			c->mfocus = nil;
630 		if(c->entered == tk)
631 			c->entered = nil;
632 	}
633 
634 	if (tk == rptw) {
635 		/* cancel the autorepeat without notifying the widget */
636 		rptid++;
637 		rptw = nil;
638 	}
639 	if (tk == blinkw)
640 		blinkw = nil;
641 	tkextnfreeobj(tk);
642 	tkmethod[tk->type]->free(tk);
643 	tkputenv(tk->env);
644 	tkfreebind(tk->binds);
645 	if(tk->name != nil)
646 		free(tk->name);
647 	free(tk);
648 }
649 
650 char*
651 tkaddchild(TkTop *t, Tk *tk, TkName **names)
652 {
653 	TkName *n;
654 	Tk *f, **l;
655 	int found, len;
656 	char *s, *ep;
657 
658 	n = *names;
659 	if(n == nil || n->name[0] != '.'){
660 		if(n != nil)
661 			tkerr(t, n->name);
662 		return TkBadwp;
663 	}
664 
665 	if (n->name[1] == '\0')
666 		return TkDupli;
667 
668 	/*
669 	 * check that the name is well-formed.
670 	 * ep will point to end of parent component of the name.
671 	 */
672 	ep = nil;
673 	for (s = n->name + 1; *s; s++) {
674 		if (*s == '.'){
675 			tkerr(t, n->name);
676 			return TkBadwp;
677 		}
678 		for (; *s && *s != '.'; s++)
679 			;
680 		if (*s == '\0')
681 			break;
682 		ep = s;
683 	}
684 	if (ep == s - 1){
685 		tkerr(t, n->name);
686 		return TkBadwp;
687 	}
688 	if (ep == nil)
689 		ep = n->name + 1;
690 	len = ep - n->name;
691 
692 	found = 0;
693 	l = &t->root;
694 	for(f = *l; f; f = f->siblings) {
695 		if (f->name != nil) {
696 			if (strcmp(n->name, f->name->name) == 0)
697 				return TkDupli;
698 			if (!found &&
699 					strncmp(n->name, f->name->name, len) == 0 &&
700 					f->name->name[len] == '\0')
701 				found = 1;
702 		}
703 		l = &f->siblings;
704 	}
705 	if (0) {		/* don't enable this until a reasonably major release... if ever */
706 		/*
707 		 * parent widget must already exist
708 		 */
709 		if (!found){
710 			tkerr(t, n->name);
711 			return TkBadwp;
712 		}
713 	}
714 	*l = tk;
715 	tk->name = n;
716 	*names = n->link;
717 
718 	return nil;
719 }
720 
721 Tk*
722 tklook(TkTop *t, char *wp, int parent)
723 {
724 	Tk *f;
725 	char *p, *q;
726 
727 	if(wp == nil)
728 		return nil;
729 
730 	if(parent) {
731 		p = strdup(wp);
732 		if(p == nil)
733 			return nil;
734 		q = strrchr(p, '.');
735 		if(q == nil)
736 			abort();
737 		if(q == p) {
738 			free(p);
739 			return t->root;
740 		}
741 		*q = '\0';
742 	} else
743 		p = wp;
744 
745 	for(f = t->root; f; f = f->siblings)
746 		if ((f->name != nil) && (strcmp(f->name->name, p) == 0))
747 			break;
748 
749 	if(f != nil && (f->flag & Tkdestroy))
750 		f = nil;
751 
752 	if (parent)
753 		free(p);
754 	return f;
755 }
756 
757 void
758 tktextsdraw(Image *img, Rectangle r, TkEnv *e, int sbw)
759 {
760 	Image *l, *d;
761 	Rectangle s;
762 
763 	draw(img, r, tkgc(e, TkCselectbgnd), nil, ZP);
764 	s.min = r.min;
765 	s.min.x -= sbw;
766 	s.min.y -= sbw;
767 	s.max.x = r.max.x;
768 	s.max.y = r.min.y;
769 	l = tkgc(e, TkCselectbgndlght);
770 	draw(img, s, l, nil, ZP);
771 	s.max.x = s.min.x + sbw;
772 	s.max.y = r.max.y + sbw;
773 	draw(img, s, l, nil, ZP);
774 	s.max = r.max;
775 	s.max.x += sbw;
776 	s.max.y += sbw;
777 	s.min.x = r.min.x;
778 	s.min.y = r.max.y;
779 	d = tkgc(e, TkCselectbgnddark);
780 	draw(img, s, d, nil, ZP);
781 	s.min.x = r.max.x;
782 	s.min.y = r.min.y - sbw;
783 	draw(img, s, d, nil, ZP);
784 }
785 
786 void
787 tkbox(Image *i, Rectangle r, int bd, Image *fill)
788 {
789 	if (bd > 0) {
790 		draw(i, Rect(r.min.x, r.min.y, r.max.x, r.min.y+bd), fill, nil, ZP);
791 		draw(i, Rect(r.min.x, r.min.y+bd, r.min.x+bd, r.max.y-bd), fill, nil, ZP);
792 		draw(i, Rect(r.min.x, r.max.y-bd, r.max.x, r.max.y), fill, nil, ZP);
793 		draw(i, Rect(r.max.x-bd, r.min.y+bd, r.max.x, r.max.y), fill, nil, ZP);
794 	}
795 }
796 
797 void
798 tkbevel(Image *i, Point o, int w, int h, int bw, Image *top, Image *bottom)
799 {
800 	Rectangle r;
801 	int x, border;
802 
803 	border = 2 * bw;
804 
805 	r.min = o;
806 	r.max.x = r.min.x + w + border;
807 	r.max.y = r.min.y + bw;
808 	draw(i, r, top, nil, ZP);
809 
810 	r.max.x = r.min.x + bw;
811 	r.max.y = r.min.y + h + border;
812 	draw(i, r, top, nil, ZP);
813 
814 	r.max.x = o.x + w + border;
815 	r.max.y = o.y + h + border;
816 	r.min.x = o.x + bw;
817 	r.min.y = r.max.y - bw;
818 	for(x = 0; x < bw; x++) {
819 		draw(i, r, bottom, nil, ZP);
820 		r.min.x--;
821 		r.min.y++;
822 	}
823 	r.min.x = o.x + bw + w;
824 	r.min.y = o.y + bw;
825 	for(x = bw; x >= 0; x--) {
826 		draw(i, r, bottom, nil, ZP);
827 		r.min.x++;
828 		r.min.y--;
829 	}
830 }
831 
832 /*
833  * draw a relief border.
834  * color is an index into tk->env->colors and assumes
835  * light and dark versions following immediately after
836  * that index
837  */
838 void
839 tkdrawrelief(Image *i, Tk *tk, Point o, int color, int rlf)
840 {
841 	TkEnv *e;
842 	Image *l, *d, *t;
843 	int h, w, bd, bd1, bd2;
844 
845 	if(tk->borderwidth == 0)
846 		return;
847 
848 	h = tk->act.height;
849 	w = tk->act.width;
850 
851 	e = tk->env;
852 	if (color == TkCbackgnd || color == TkCselectbgnd || color == TkCactivebgnd) {
853 		l = tkgc(e, color+TkLightshade);
854 		d = tkgc(e, color+TkDarkshade);
855 	} else {
856 		l = tkgshade(e, color, TkLightshade);
857 		d = tkgshade(e, color, TkDarkshade);
858 	}
859 	bd = tk->borderwidth;
860 	if(rlf < 0)
861 		rlf = TKraised;
862 	switch(rlf) {
863 	case TKflat:
864 		break;
865 	case TKsunken:
866 		tkbevel(i, o, w, h, bd, d, l);
867 		break;
868 	case TKraised:
869 		tkbevel(i, o, w, h, bd, l, d);
870 		break;
871 	case TKgroove:
872 		t = d;
873 		d = l;
874 		l = t;
875 		/* fall through */
876 	case TKridge:
877 		bd1 = bd/2;
878 		bd2 = bd - bd1;
879 		if(bd1 > 0)
880 			tkbevel(i, o, w + 2*bd2, h + 2*bd2, bd1, l, d);
881 		o.x += bd1;
882 		o.y += bd1;
883 		tkbevel(i, o, w, h, bd2, d, l);
884 		break;
885 	}
886 }
887 
888 Point
889 tkstringsize(Tk *tk, char *text)
890 {
891 	char *q;
892 	int locked;
893 	Display *d;
894 	Point p, t;
895 
896 	if(text == nil) {
897 		p.x = 0;
898 		p.y = tk->env->font->height;
899 		return p;
900 	}
901 
902 	d = tk->env->top->display;
903 	locked = lockdisplay(d);
904 
905 	p = ZP;
906 	while(*text) {
907 		q = strchr(text, '\n');
908 		if(q != nil)
909 			*q = '\0';
910 		t = stringsize(tk->env->font, text);
911 		p.y += t.y;
912 		if(p.x < t.x)
913 			p.x = t.x;
914 		if(q == nil)
915 			break;
916 		text = q+1;
917 		*q = '\n';
918 	}
919 	if(locked)
920 		unlockdisplay(d);
921 
922 	return p;
923 }
924 
925 static void
926 tkulall(Image *i, Point o, Image *col, Font *f, char *text)
927 {
928 	Rectangle r;
929 
930 	r.max = stringsize(f, text);
931 	r.max = addpt(r.max, o);
932 	r.min.x = o.x;
933 	r.min.y = r.max.y - 1;
934 	draw(i, r, col, nil, ZP);
935 }
936 
937 static void
938 tkul(Image *i, Point o, Image *col, int ul, Font *f, char *text)
939 {
940 	char c, *v;
941 	Rectangle r;
942 
943 	v = text+ul+1;
944 	c = *v;
945 	*v = '\0';
946 	r.max = stringsize(f, text);
947 	r.max = addpt(r.max, o);
948 	r.min = stringsize(f, v-1);
949 	*v = c;
950 	r.min.x = r.max.x - r.min.x;
951 	r.min.y = r.max.y - 1;
952 	r.max.y++;
953 	draw(i, r, col, nil, ZP);
954 }
955 
956 void
957 tkdrawstring(Tk *tk, Image *i, Point o, char *text, int ul, Image *col, int j)
958 {
959 	int n, l, maxl, sox;
960 	char *q, *txt;
961 	Point p;
962 	TkEnv *e;
963 
964 	e = tk->env;
965 	sox = maxl = 0;
966 	if(j != Tkleft){
967 		maxl = 0;
968 		txt = text;
969 		while(*txt){
970 			q = strchr(txt, '\n');
971 			if(q != nil)
972 				*q = '\0';
973 			l = stringwidth(e->font, txt);
974 			if(l > maxl)
975 				maxl = l;
976 			if(q == nil)
977 				break;
978 			txt = q+1;
979 			*q = '\n';
980 		}
981 		sox = o.x;
982 	}
983 	while(*text) {
984 		q = strchr(text, '\n');
985 		if(q != nil)
986 			*q = '\0';
987 		if(j != Tkleft){
988 			o.x = sox;
989 			l = stringwidth(e->font, text);
990 			if(j == Tkcenter)
991 				o.x += (maxl-l)/2;
992 			else
993 				o.x += maxl-l;
994 		}
995 		p = string(i, o, col, o, e->font, text);
996 		if(ul >= 0) {
997 			n = strlen(text);
998 			if(ul < n) {
999 				tkul(i, o, col, ul, e->font, text);
1000 				ul = -1;
1001 			} else if(ul == n) {
1002 				tkulall(i, o, col, e->font, text);
1003 				ul = -1;
1004 			} else
1005 				ul -= n;
1006 		}
1007 		o.y += e->font->height;
1008 		if(q == nil)
1009 			break;
1010 		text = q+1;
1011 		*q = '\n';
1012 	}
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