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