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