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