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