xref: /inferno-os/libtk/utils.c (revision 0db9190e73bd2b1391b1108a354b8cf089ab5374)
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 	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 	while(np-- > 1) {
1914 		b = a+1;
1915 		nx = a->y - b->y;
1916 		ny = b->x - a->x;
1917 		nrm = (nx < 0? -nx : nx) + (ny < 0? -ny : ny);
1918 		if(nrm)
1919 			z = (p.x-b->x)*nx/nrm + (p.y-b->y)*ny/nrm;
1920 		else
1921 			z = (p.x-b->x) + (p.y-b->y);
1922 		if(z < 0)
1923 			z = -z;
1924 		if(z < w)
1925 			return 1;
1926 		a++;
1927 	}
1928 	return 0;
1929 }
1930 
1931 int
1932 tkiswordchar(int c)
1933 {
1934 	return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_' || c >= 0xA0;
1935 }
1936 
1937 int
1938 tkhaskeyfocus(Tk *tk)
1939 {
1940 	if (tk == nil || tk->env->top->focused == 0)
1941 		return 0;
1942 	return tk == tk->env->top->ctxt->tkkeygrab;
1943 }
1944 
1945 static int
1946 rptactive(void *v)
1947 {
1948 	int id = (int)v;
1949 	if (id == rptid)
1950 		return 1;
1951 	return 0;
1952 }
1953 
1954 static int
1955 ckrpt(void *v, int interval)
1956 {
1957 	int id = (int)v;
1958 	if (id != rptid)
1959 		return -1;
1960 	if (interval < rptto)
1961 		return 0;
1962 	return 1;
1963 }
1964 
1965 static void
1966 dorpt(void *v)
1967 {
1968 	int id = (int)v;
1969 
1970 	if (id == rptid) {
1971 		rptto = rptint;
1972 		(*rptcb)(rptw, rptnote, 0);
1973 		if (rptint <= 0) {
1974 			rptid++;
1975 			rptw = nil;
1976 		}
1977 	}
1978 }
1979 
1980 void
1981 tkcancelrepeat(Tk *tk)
1982 {
1983 	if (tk == rptw) {
1984 		rptid++;
1985 		rptw = nil;
1986 	}
1987 }
1988 
1989 void
1990 tkrepeat(Tk *tk, void (*callback)(Tk*, void*, int), void *note, int pause, int interval)
1991 {
1992 	rptid++;
1993 	if (tk != rptw && rptw != nil)
1994 		/* existing callback being replaced- report to owner */
1995 		(*rptcb)(rptw, rptnote, 1);
1996 	rptw = tk;
1997 	if (tk == nil || callback == nil)
1998 		return;
1999 	rptnote = note;
2000 	rptcb = callback;
2001 	rptto = pause;
2002 	rptint = interval;
2003 	if (!autorpt)
2004 		autorpt = rptproc("autorepeat", TkRptclick, (void*)rptid, rptactive, ckrpt, dorpt);
2005 	else
2006 		rptwakeup((void*)rptid, autorpt);
2007 }
2008 
2009 static int
2010 blinkactive(void *v)
2011 {
2012 	USED(v);
2013 	return blinkw != nil;
2014 }
2015 
2016 static int
2017 ckblink(void *v, int interval)
2018 {
2019 	USED(v);
2020 	USED(interval);
2021 
2022 	if (blinkw == nil)
2023 		return -1;
2024 	if (blinkignore) {
2025 		blinkignore = 0;
2026 		return 0;
2027 	}
2028 	return 1;
2029 }
2030 
2031 static void
2032 doblink(void *v)
2033 {
2034 	USED(v);
2035 
2036 	if (blinkw == nil)
2037 		return;
2038 	blinkcb(blinkw, blinkon++ & 1);
2039 	tkupdate(blinkw->env->top);
2040 }
2041 
2042 void
2043 tkblinkreset(Tk *tk)
2044 {
2045 	if (blinkw == tk) {
2046 		blinkignore = 1;
2047 		blinkon = 0;
2048 	}
2049 }
2050 
2051 void
2052 tkblink(Tk *tk, void (*callback)(Tk*, int))
2053 {
2054 	if (tk == nil || callback == nil) {
2055 		blinkw = nil;
2056 		return;
2057 	}
2058 	blinkw = tk;
2059 	blinkcb = callback;
2060 	if (!blinkrpt)
2061 		blinkrpt = rptproc("blinker", TkBlinkinterval, nil, blinkactive, ckblink, doblink);
2062 	else
2063 		rptwakeup(nil, blinkrpt);
2064 }
2065