xref: /inferno-os/libtk/ebind.c (revision 5849851a19380dbb62a47d9c4d868a81e42fa79b)
1 #include "lib9.h"
2 #include "draw.h"
3 #include "tk.h"
4 #include <kernel.h>
5 #include <interp.h>
6 
7 enum
8 {
9 	Cmask,
10 	Cctl,
11 	Ckey,
12 	Cbp,
13 	Cbr,
14 };
15 
16 struct
17 {
18 	char*	event;
19 	int	mask;
20 	int	action;
21 } etab[] =
22 {
23 	"Motion",		TkMotion,	Cmask,
24 	"Double",		TkDouble,	Cmask,
25 	"Map",			TkMap,		Cmask,
26 	"Unmap",		TkUnmap,	Cmask,
27 	"Destroy",		TkDestroy, Cmask,
28 	"Enter",		TkEnter,	Cmask,
29 	"Leave",		TkLeave,	Cmask,
30 	"FocusIn",		TkFocusin,	Cmask,
31 	"FocusOut",		TkFocusout,	Cmask,
32 	"Configure",		TkConfigure,	Cmask,
33 	"Control",		0,		Cctl,
34 	"Key",			0,		Ckey,
35 	"KeyPress",		0,		Ckey,
36 	"Button",		0,		Cbp,
37 	"ButtonPress",		0,		Cbp,
38 	"ButtonRelease",	0,		Cbr,
39 };
40 
41 static
42 TkOption tkcurop[] =
43 {
44 	"x",		OPTdist,	O(TkCursor, p.x),	nil,
45 	"y",		OPTdist,	O(TkCursor, p.y),	nil,
46 	"bitmap",	OPTbmap,	O(TkCursor, bit),	nil,
47 	"image",	OPTimag,	O(TkCursor, img),	nil,
48 	"default",	OPTbool,	O(TkCursor, def),	nil,
49 	nil
50 };
51 
52 static
53 TkOption focusopts[] = {
54 	"global",			OPTbool,	0,	nil,
55 	nil
56 };
57 
58 static char*
tkseqitem(char * buf,char * arg)59 tkseqitem(char *buf, char *arg)
60 {
61 	while(*arg && (*arg == ' ' || *arg == '-'))
62 		arg++;
63 	while(*arg && *arg != ' ' && *arg != '-' && *arg != '>')
64 		*buf++ = *arg++;
65 	*buf = '\0';
66 	return arg;
67 }
68 
69 static char*
tkseqkey(Rune * r,char * arg)70 tkseqkey(Rune *r, char *arg)
71 {
72 	char *narg;
73 
74 	while(*arg && (*arg == ' ' || *arg == '-'))
75 		arg++;
76 	if (*arg == '\\') {
77 		if (*++arg == '\0') {
78 			*r = 0;
79 			return arg;
80 		}
81 	} else if (*arg == '\0' || *arg == '>' || *arg == '-') {
82 		*r = 0;
83 		return arg;
84 	}
85 	narg = arg + chartorune(r, arg);
86 	return narg;
87 }
88 
89 int
tkseqparse(char * seq)90 tkseqparse(char *seq)
91 {
92 	Rune r;
93 	int i, event;
94 	char *buf;
95 
96 	buf = mallocz(Tkmaxitem, 0);
97 	if(buf == nil)
98 		return -1;
99 
100 	event = 0;
101 
102 	while(*seq && *seq != '>') {
103 		seq = tkseqitem(buf, seq);
104 
105 		for(i = 0; i < nelem(etab); i++)
106 			if(strcmp(buf, etab[i].event) == 0)
107 				break;
108 
109 		if(i >= nelem(etab)) {
110 			seq = tkextnparseseq(buf, seq, &event);
111 			if (seq == nil) {
112 				free(buf);
113 				return -1;
114 			}
115 			continue;
116 		}
117 
118 
119 		switch(etab[i].action) {
120 		case Cmask:
121 			event |= etab[i].mask;
122 			break;
123 		case Cctl:
124 			seq = tkseqkey(&r, seq);
125 			if(r == 0) {
126 				free(buf);
127 				return -1;
128 			}
129 			if(r <= '~')
130 				r &= 0x1f;
131 			event |= TkKey|TKKEY(r);
132 			break;
133 		case Ckey:
134 			seq = tkseqkey(&r, seq);
135 			if(r != 0)
136 				event |= TKKEY(r);
137 			event |= TkKey;
138 			break;
139 		case Cbp:
140 			seq = tkseqitem(buf, seq);
141 			switch(buf[0]) {
142 			default:
143 				free(buf);
144 				return -1;
145 			case '\0':
146 				event |= TkEpress;
147 				break;
148 			case '1':
149 				event |= TkButton1P;
150 				break;
151 			case '2':
152 				event |= TkButton2P;
153 				break;
154 			case '3':
155 				event |= TkButton3P;
156 				break;
157 			case '4':
158 				event |= TkButton4P;
159 				break;
160 			case '5':
161 				event |= TkButton5P;
162 				break;
163 			case '6':
164 				event |= TkButton6P;
165 				break;
166 			}
167 			break;
168 		case Cbr:
169 			seq = tkseqitem(buf, seq);
170 			switch(buf[0]) {
171 			default:
172 				free(buf);
173 				return -1;
174 			case '\0':
175 				event |= TkErelease;
176 				break;
177 			case '1':
178 				event |= TkButton1R;
179 				break;
180 			case '2':
181 				event |= TkButton2R;
182 				break;
183 			case '3':
184 				event |= TkButton3R;
185 				break;
186 			case '4':
187 				event |= TkButton4R;
188 				break;
189 			case '5':
190 				event |= TkButton5R;
191 				break;
192 			case '6':
193 				event |= TkButton6R;
194 				break;
195 			}
196 			break;
197 		}
198 	}
199 	free(buf);
200 	return event;
201 }
202 
203 void
tkcmdbind(Tk * tk,int event,char * s,void * data)204 tkcmdbind(Tk *tk, int event, char *s, void *data)
205 {
206 	Point p;
207 	TkMouse *m;
208 	TkGeom *g;
209 	int v, len;
210 	char *e, *c, *ec, *cmd;
211 	TkTop *t;
212 
213 	if(s == nil)
214 		return;
215 	cmd = malloc(2*Tkmaxitem);
216 	if (cmd == nil) {
217 		print("tk: bind command \"%s\": %s\n",
218 			tk->name ? tk->name->name : "(noname)", TkNomem);
219 		return;
220 	}
221 
222 	m = (TkMouse*)data;
223 	c = cmd;
224 	ec = cmd+2*Tkmaxitem-1;
225 	while(*s && c < ec) {
226 		if(*s != '%') {
227 			*c++ = *s++;
228 			continue;
229 		}
230 		s++;
231 		len = ec-c;
232 		switch(*s++) {
233 		def:
234 		default:
235 			*c++ = s[-1];
236 			break;
237 		case '%':
238 			*c++ = '%';
239 			break;
240 		case 'b':
241 			v = 0;
242 			if (!(event & TkKey)) {
243 				if(event & (TkButton1P|TkButton1R))
244 					v = 1;
245 				else if(event & (TkButton2P|TkButton2R))
246 					v = 2;
247 				else if(event & (TkButton3P|TkButton3R))
248 					v = 3;
249 			}
250 			c += snprint(c, len, "%d", v);
251 			break;
252 		case 'h':
253 			if((event & TkConfigure) == 0)
254 				goto def;
255 			g = (TkGeom*)data;
256 			c += snprint(c, len, "%d", g->height);
257 			break;
258 		case 's':
259 			if((event & TkKey))
260 				c += snprint(c, len, "%d", TKKEY(event));
261 			else if((event & (TkEmouse|TkEnter)))
262 				c += snprint(c, len, "%d", m->b);
263 			else if((event & TkFocusin))
264 				c += snprint(c, len, "%d", (int)data);
265 			else
266 				goto def;
267 			break;
268 		case 'w':
269 			if((event & TkConfigure) == 0)
270 				goto def;
271 			g = (TkGeom*)data;
272 			c += snprint(c, len, "%d", g->width);
273 			break;
274 		case 'x':		/* Relative mouse coords */
275 		case 'y':
276 			if((event & TkKey) || (event & (TkEmouse|TkEnter)) == 0)
277 				goto def;
278 			p = tkposn(tk);
279 			if(s[-1] == 'x')
280 				v = m->x - p.x;
281 			else
282 				v = m->y - p.y;
283 			c += snprint(c, len, "%d", v - tk->borderwidth);
284 			break;
285 		case 'X':		/* Absolute mouse coords */
286 		case 'Y':
287 			if((event & TkKey) || (event & TkEmouse) == 0)
288 				goto def;
289 			c += snprint(c, len, "%d", s[-1] == 'X' ? m->x : m->y);
290 			break;
291 		case 'A':
292 			if((event & TkKey) == 0)
293 				goto def;
294 			v = TKKEY(event);
295 			if(v == '{' || v == '}' || v == '\\')
296 				c += snprint(c, len, "\\%C", v);
297 			else if(v != '\0')
298 				c += snprint(c, len, "%C", v);
299 			break;
300 		case 'K':
301 			if((event & TkKey) == 0)
302 				goto def;
303 			c += snprint(c, len, "%.4X", TKKEY(event));
304 			break;
305 		case 'W':
306 		        if (tk->name != nil)
307 			  c += snprint(c, len, "%s", tk->name->name);
308 			break;
309 		}
310 	}
311 	*c = '\0';
312 	e = nil;
313 	t = tk->env->top;
314 	t->execdepth = 0;
315 	if(cmd[0] == '|')
316 		tkexec(t, cmd+1, nil);
317 	else if(cmd[0] != '\0')
318 		e = tkexec(t, cmd, nil);
319 	t->execdepth = -1;
320 
321 	if(e == nil) {
322 		free(cmd);
323 		return;
324 	}
325 
326 	if(tk->name != nil){
327 		char *s;
328 
329 		if(t->errx[0] != '\0')
330 			s = tkerrstr(t, e);
331 		else
332 			s = e;
333 		print("tk: bind command \"%s\": %s: %s\n", tk->name->name, cmd, s);
334 		if(s != e)
335 			free(s);
336 	}
337 	free(cmd);
338 }
339 
340 char*
tkbind(TkTop * t,char * arg,char ** ret)341 tkbind(TkTop *t, char *arg, char **ret)
342 {
343 	Rune r;
344 	Tk *tk;
345 	TkAction **ap;
346 	int i, mode, event;
347 	char *cmd, *tag, *seq;
348 	char *e;
349 
350 	USED(ret);
351 
352 	tag = mallocz(Tkmaxitem, 0);
353 	if(tag == nil)
354 		return TkNomem;
355 	seq = mallocz(Tkmaxitem, 0);
356 	if(seq == nil) {
357 		free(tag);
358 		return TkNomem;
359 	}
360 
361 	arg = tkword(t, arg, tag, tag+Tkmaxitem, nil);
362 	if(tag[0] == '\0') {
363 		e = TkBadtg;
364 		goto err;
365 	}
366 
367 	arg = tkword(t, arg, seq, seq+Tkmaxitem, nil);
368 	if(seq[0] == '<') {
369 		event = tkseqparse(seq+1);
370 		if(event == -1) {
371 			e = TkBadsq;
372 			goto err;
373 		}
374 	}
375 	else {
376 		chartorune(&r, seq);
377 		event = TkKey | r;
378 	}
379 	if(event == 0) {
380 		e = TkBadsq;
381 		goto err;
382 	}
383 
384 	arg = tkskip(arg, " \t");
385 
386 	mode = TkArepl;
387 	if(*arg == '+') {
388 		mode = TkAadd;
389 		arg++;
390 	}
391 	else if(*arg == '-'){
392 		mode = TkAsub;
393 		arg++;
394 	}
395 
396 	if(*arg == '{') {
397 		cmd = tkskip(arg+1, " \t");
398 		if(*cmd == '}') {
399 			tk = tklook(t, tag, 0);
400 			if(tk == nil) {
401 				for(i = 0; ; i++) {
402 					if(i >= TKwidgets) {
403 						e = TkBadwp;
404 						tkerr(t, tag);
405 						goto err;
406 					}
407 					if(strcmp(tag, tkmethod[i]->name) == 0) {
408 						ap = &(t->binds[i]);
409 						break;
410 					}
411 				}
412 			}
413 			else
414 				ap = &tk->binds;
415 			tkcancel(ap, event);
416 		}
417 	}
418 
419 	tkword(t, arg, seq, seq+Tkmaxitem, nil);
420 	if(tag[0] == '.') {
421 		tk = tklook(t, tag, 0);
422 		if(tk == nil) {
423 			e = TkBadwp;
424 			tkerr(t, tag);
425 			goto err;
426 		}
427 
428 		cmd = strdup(seq);
429 		if(cmd == nil) {
430 			e = TkNomem;
431 			goto err;
432 		}
433 		e = tkaction(&tk->binds, event, TkDynamic, cmd, mode);
434 		if(e != nil)
435 			goto err;	/* tkaction does free(cmd) */
436 		free(tag);
437 		free(seq);
438 		return nil;
439 	}
440 	/* documented but doesn't work */
441 	if(strcmp(tag, "all") == 0) {
442 		for(tk = t->root; tk; tk = tk->next) {
443 			cmd = strdup(seq);
444 			if(cmd == nil) {
445 				e = TkNomem;
446 				goto err;
447 			}
448 			e = tkaction(&tk->binds, event, TkDynamic, cmd, mode);
449 			if(e != nil)
450 				goto err;
451 		}
452 		free(tag);
453 		free(seq);
454 		return nil;
455 	}
456 	/* undocumented, probably unused, and doesn't work consistently */
457 	for(i = 0; i < TKwidgets; i++) {
458 		if(strcmp(tag, tkmethod[i]->name) == 0) {
459 			cmd = strdup(seq);
460 			if(cmd == nil) {
461 				e = TkNomem;
462 				goto err;
463 			}
464 			e = tkaction(t->binds + i,event, TkDynamic, cmd, mode);
465 			if(e != nil)
466 				goto err;
467 			free(tag);
468 			free(seq);
469 			return nil;
470 		}
471 	}
472 
473 	e = TkBadtg;
474 err:
475 	free(tag);
476 	free(seq);
477 
478 	return e;
479 }
480 
481 char*
tksend(TkTop * t,char * arg,char ** ret)482 tksend(TkTop *t, char *arg, char **ret)
483 {
484 
485 	TkVar *v;
486 	char *var;
487 
488 	USED(ret);
489 
490 	var = mallocz(Tkmaxitem, 0);
491 	if(var == nil)
492 		return TkNomem;
493 
494 	arg = tkword(t, arg, var, var+Tkmaxitem, nil);
495 	v = tkmkvar(t, var, 0);
496 	free(var);
497 	if(v == nil)
498 		return TkBadvr;
499 	if(v->type != TkVchan)
500 		return TkNotvt;
501 
502 	arg = tkskip(arg, " \t");
503 	if(tktolimbo(v->value, arg) == 0)
504 		return TkMovfw;
505 
506 	return nil;
507 }
508 
509 static Tk*
tknextfocus(TkTop * t,int d)510 tknextfocus(TkTop *t, int d)
511 {
512 	int i, n, j, k;
513 	Tk *oldfocus;
514 
515 	if (t->focusorder == nil)
516 		tkbuildfocusorder(t);
517 
518 	oldfocus = t->ctxt->tkkeygrab;
519 	n = t->nfocus;
520 	if (n == 0)
521 		return oldfocus;
522 	for (i = 0; i < n; i++)
523 		if (t->focusorder[i] == oldfocus)
524 			break;
525 	if (i == n) {
526 		for (i = 0; i < n; i++)
527 			if ((t->focusorder[i]->flag & Tkdisabled) == 0)
528 				return t->focusorder[i];
529 		return oldfocus;
530 	}
531 	for (j = 1; j < n; j++) {
532 		k = (i + d * j + n) % n;
533 		if ((t->focusorder[k]->flag & Tkdisabled) == 0)
534 			return t->focusorder[k];
535 	}
536 	return oldfocus;
537 }
538 
539 /* our dirty little secret */
540 static void
focusdirty(Tk * tk)541 focusdirty(Tk *tk)
542 {
543 	if(tk->highlightwidth > 0){
544 		tk->dirty = tkrect(tk, 1);
545 		tkdirty(tk);
546 	}
547 }
548 
549 void
tksetkeyfocus(TkTop * top,Tk * new,int dir)550 tksetkeyfocus(TkTop *top, Tk *new, int dir)
551 {
552 	TkCtxt *c;
553 	Tk *old;
554 
555 	c = top->ctxt;
556 	old = c->tkkeygrab;
557 
558 	if(old == new)
559 		return;
560 	c->tkkeygrab = new;
561 	if(top->focused == 0)
562 		return;
563 	if(old != nil && old != top->root){
564 		tkdeliver(old, TkFocusout, nil);
565 		focusdirty(old);
566 	}
567 	if(new != nil && new != top->root){
568 		tkdeliver(new, TkFocusin, (void*)dir);
569 		focusdirty(new);
570 	}
571 }
572 
573 void
tksetglobalfocus(TkTop * top,int in)574 tksetglobalfocus(TkTop *top, int in)
575 {
576 	Tk *tk;
577 	in = (in != 0);
578 	if (in != top->focused){
579 		top->focused = in;
580 		tk = top->ctxt->tkkeygrab;
581 		if(in){
582 			tkdeliver(top->root, TkFocusin, (void*)0);
583 			if(tk != nil && tk != top->root){
584 				tkdeliver(tk, TkFocusin, (void*)0);
585 				focusdirty(tk);
586 			}
587 		}else{
588 			if(tk != nil && tk != top->root){
589 				tkdeliver(tk, TkFocusout, nil);
590 				focusdirty(tk);
591 			}
592 			tkdeliver(top->root, TkFocusout, nil);
593 		}
594 	}
595 }
596 
597 char*
tkfocus(TkTop * top,char * arg,char ** ret)598 tkfocus(TkTop *top, char *arg, char **ret)
599 {
600 	Tk *tk;
601 	char *wp, *e;
602 	int dir, global;
603 	TkOptab tko[2];
604 	TkName *names;
605 
606 	tko[0].ptr = &global;
607 	tko[0].optab = focusopts;
608 	tko[1].ptr = nil;
609 
610 	global = 0;
611 
612 	names = nil;
613 	e = tkparse(top, arg, tko, &names);
614 	if (e != nil)
615 		return e;
616 
617 	if(names == nil){
618 		if(global)
619 			return tkvalue(ret, "%d", top->focused);
620 		tk = top->ctxt->tkkeygrab;
621 		if (tk != nil && tk->name != nil)
622 			return tkvalue(ret, "%s", tk->name->name);
623 		return nil;
624 	}
625 
626 	if(global){
627 		tksetglobalfocus(top, atoi(names->name));
628 		return nil;
629 	}
630 
631 	wp = mallocz(Tkmaxitem, 0);
632 	if(wp == nil)
633 		return TkNomem;
634 
635 	tkword(top, arg, wp, wp+Tkmaxitem, nil);
636 	if (!strcmp(wp, "next")) {
637 		tk = tknextfocus(top, 1);		/* can only return nil if c->tkkeygrab is already nil */
638 		dir = +1;
639 	} else if (!strcmp(wp, "previous")) {
640 		tk = tknextfocus(top, -1);
641 		dir = -1;
642 	} else if(*wp == '\0') {
643 		tk = nil;
644 		dir = 0;
645 	} else {
646 		tk = tklook(top, wp, 0);
647 		if(tk == nil){
648 			tkerr(top, wp);
649 			free(wp);
650 			return TkBadwp;
651 		}
652 		dir = 0;
653 	}
654 	free(wp);
655 
656 	tksetkeyfocus(top, tk, dir);
657 	return nil;
658 }
659 
660 char*
tkraise(TkTop * t,char * arg,char ** ret)661 tkraise(TkTop *t, char *arg, char **ret)
662 {
663 	Tk *tk;
664 	char *wp;
665 
666 	USED(ret);
667 
668 	wp = mallocz(Tkmaxitem, 0);
669 	if(wp == nil)
670 		return TkNomem;
671 	tkword(t, arg, wp, wp+Tkmaxitem, nil);
672 	tk = tklook(t, wp, 0);
673 	if(tk == nil){
674 		tkerr(t, wp);
675 		free(wp);
676 		return TkBadwp;
677 	}
678 	free(wp);
679 
680 	if((tk->flag & Tkwindow) == 0)
681 		return TkNotwm;
682 
683 	tkwreq(tk->env->top, "raise %s", tk->name->name);
684 	return nil;
685 }
686 
687 char*
tklower(TkTop * t,char * arg,char ** ret)688 tklower(TkTop *t, char *arg, char **ret)
689 {
690 	Tk *tk;
691 	char *wp;
692 
693 	USED(ret);
694 	wp = mallocz(Tkmaxitem, 0);
695 	if(wp == nil)
696 		return TkNomem;
697 	tkword(t, arg, wp, wp+Tkmaxitem, nil);
698 	tk = tklook(t, wp, 0);
699 	if(tk == nil){
700 		tkerr(t, wp);
701 		free(wp);
702 		return TkBadwp;
703 	}
704 	free(wp);
705 
706 	if((tk->flag & Tkwindow) == 0)
707 		return TkNotwm;
708 
709 	tkwreq(tk->env->top, "lower %s", tk->name->name);
710 	return nil;
711 }
712 
713 char*
tkgrab(TkTop * t,char * arg,char ** ret)714 tkgrab(TkTop *t, char *arg, char **ret)
715 {
716 	Tk *tk;
717 	TkCtxt *c;
718 	char *r, *buf, *wp;
719 
720 	USED(ret);
721 
722 	buf = mallocz(Tkmaxitem, 0);
723 	if(buf == nil)
724 		return TkNomem;
725 
726 	wp = mallocz(Tkmaxitem, 0);
727 	if(wp == nil) {
728 		free(buf);
729 		return TkNomem;
730 	}
731 	arg = tkword(t, arg, buf, buf+Tkmaxitem, nil);
732 
733 	tkword(t, arg, wp, wp+Tkmaxitem, nil);
734 	tk = tklook(t, wp, 0);
735 	if(tk == nil) {
736 		free(buf);
737 		tkerr(t, wp);
738 		free(wp);
739 		return TkBadwp;
740 	}
741 	free(wp);
742 
743 	c = t->ctxt;
744 	if(strcmp(buf, "release") == 0) {
745 		free(buf);
746 		if(c->mgrab == tk)
747 			tksetmgrab(t, nil);
748 		return nil;
749 	}
750 	if(strcmp(buf, "set") == 0) {
751 		free(buf);
752 		return tksetmgrab(t, tk);
753 	}
754 	if(strcmp(buf, "ifunset") == 0) {
755 		free(buf);
756 		if(c->mgrab == nil)
757 			return tksetmgrab(t, tk);
758 		return nil;
759 	}
760 	if(strcmp(buf, "status") == 0) {
761 		free(buf);
762 		r = "none";
763 		if ((c->mgrab != nil) && (c->mgrab->name != nil))
764 			r = c->mgrab->name->name;
765 		return tkvalue(ret, "%s", r);
766 	}
767 	free(buf);
768 	return TkBadcm;
769 }
770 
771 char*
tkputs(TkTop * t,char * arg,char ** ret)772 tkputs(TkTop *t, char *arg, char **ret)
773 {
774 	char *buf;
775 
776 	USED(ret);
777 
778 	buf = mallocz(Tkmaxitem, 0);
779 	if(buf == nil)
780 		return TkNomem;
781 	tkword(t, arg, buf, buf+Tkmaxitem, nil);
782 	print("%s\n", buf);
783 	free(buf);
784 	return nil;
785 }
786 
787 char*
tkdestroy(TkTop * t,char * arg,char ** ret)788 tkdestroy(TkTop *t, char *arg, char **ret)
789 {
790 	int found, len, isroot;
791 	Tk *tk, **l, *next, *slave;
792 	char *n, *e, *buf;
793 
794 	USED(ret);
795 	buf = mallocz(Tkmaxitem, 0);
796 	if(buf == nil)
797 		return TkNomem;
798 	e = nil;
799 	for(;;) {
800 		arg = tkword(t, arg, buf, buf+Tkmaxitem, nil);
801 		if(buf[0] == '\0')
802 			break;
803 
804 		len = strlen(buf);
805 		found = 0;
806 		isroot = (strcmp(buf, ".") == 0);
807 		for(tk = t->root; tk; tk = tk->siblings) {
808 		        if (tk->name != nil) {
809 				n = tk->name->name;
810 				if(strcmp(buf, n) == 0) {
811 					tk->flag |= Tkdestroy;
812 					found = 1;
813 				} else if(isroot || (strncmp(buf, n, len) == 0 && n[len] == '.'))
814 					tk->flag |= Tkdestroy;
815 			}
816 		}
817 		if(!found) {
818 			e = TkBadwp;
819 			tkerr(t, buf);
820 			break;
821 		}
822 	}
823 	free(buf);
824 
825 	for(tk = t->root; tk; tk = tk->siblings) {
826 		if((tk->flag & Tkdestroy) == 0)
827 			continue;
828 		if(tk->flag & Tkwindow) {
829 			tkunmap(tk);
830 			if(tk->name != nil &&
831 			   strcmp(tk->name->name, ".") == 0)
832 				tk->flag &= ~Tkdestroy;
833 			else
834 				tkdeliver(tk, TkDestroy, nil);
835 		} else
836 			tkdeliver(tk, TkDestroy, nil);
837 if(0)print("tkdestroy %q\n", tkname(tk));
838 		if(tk->destroyed != nil)
839 			tk->destroyed(tk);
840 		tkpackqit(tk->master);
841 		tkdelpack(tk);
842 		for (slave = tk->slave; slave != nil; slave = next) {
843 			next = slave->next;
844 			slave->master = nil;
845 			slave->next = nil;
846 		}
847 		tk->slave = nil;
848 		if(tk->parent != nil && tk->geom != nil)		/* XXX this appears to be bogus */
849 			tk->geom(tk, 0, 0, 0, 0);
850 		if(tk->grid){
851 			tkfreegrid(tk->grid);
852 			tk->grid = nil;
853 		}
854 	}
855 	tkrunpack(t);
856 
857 	l = &t->windows;
858 	for(tk = t->windows; tk; tk = next) {
859 		next = TKobj(TkWin, tk)->next;
860 		if(tk->flag & Tkdestroy) {
861 			*l = next;
862 			continue;
863 		}
864 		l = &TKobj(TkWin, tk)->next;
865 	}
866 	l = &t->root;
867 	for(tk = t->root; tk; tk = next) {
868 		next = tk->siblings;
869 		if(tk->flag & Tkdestroy) {
870 			*l = next;
871 			tkfreeobj(tk);
872 			continue;
873 		}
874 		l = &tk->siblings;
875 	}
876 
877 	return e;
878 }
879 
880 char*
tkupdatecmd(TkTop * t,char * arg,char ** ret)881 tkupdatecmd(TkTop *t, char *arg, char **ret)
882 {
883 	Tk *tk;
884 	int x, y;
885 	Rectangle *dr;
886 	char buf[Tkmaxitem];
887 
888 	USED(ret);
889 
890 	tkword(t, arg, buf, buf+sizeof(buf), nil);
891 	if(strcmp(buf, "-onscreen") == 0){
892 		tk = t->root;
893 		dr = &t->screenr;
894 		x = tk->act.x;
895 		if(x+tk->act.width > dr->max.x)
896 			x = dr->max.x - tk->act.width;
897 		if(x < 0)
898 			x = 0;
899 		y = tk->act.y;
900 		if(y+tk->act.height > dr->max.y)
901 			y = dr->max.y - tk->act.height;
902 		if(y < 0)
903 			y = 0;
904 		tkmovewin(tk, Pt(x, y));
905 	}else if(strcmp(buf, "-disable") == 0){
906 		t->noupdate = 1;
907 	}else if(strcmp(buf, "-enable") == 0){
908 		t->noupdate = 0;
909 	}
910 	return tkupdate(t);
911 }
912 
913 char*
tkwinfo(TkTop * t,char * arg,char ** ret)914 tkwinfo(TkTop *t, char *arg, char **ret)
915 {
916 	Tk *tk;
917 	char *cmd, *arg1;
918 
919 	cmd = mallocz(Tkmaxitem, 0);
920 	if(cmd == nil)
921 		return TkNomem;
922 
923 	arg = tkword(t, arg, cmd, cmd+Tkmaxitem, nil);
924 	if(strcmp(cmd, "class") == 0) {
925 		arg1 = mallocz(Tkmaxitem, 0);
926 		if(arg1 == nil) {
927 			free(cmd);
928 			return TkNomem;
929 		}
930 		tkword(t, arg, arg1, arg1+Tkmaxitem, nil);
931 		tk = tklook(t, arg1, 0);
932 		if(tk == nil){
933 			tkerr(t, arg1);
934 			free(arg1);
935 			free(cmd);
936 			return TkBadwp;
937 		}
938 		free(arg1);
939 		free(cmd);
940 		return tkvalue(ret, "%s", tkmethod[tk->type]->name);
941 	}
942 	free(cmd);
943 	return TkBadvl;
944 }
945 
946 char*
tkcursorcmd(TkTop * t,char * arg,char ** ret)947 tkcursorcmd(TkTop *t, char *arg, char **ret)
948 {
949 	char *e;
950 	int locked;
951 	Display *d;
952 	TkCursor c;
953 	TkOptab tko[3];
954 	enum {Notset = 0x80000000};
955 
956 	c.def = 0;
957 	c.p.x = Notset;
958 	c.p.y = Notset;
959 	c.bit = nil;
960 	c.img = nil;
961 
962 	USED(ret);
963 
964 	c.def = 0;
965 	tko[0].ptr = &c;
966 	tko[0].optab = tkcurop;
967 	tko[1].ptr = nil;
968 	e = tkparse(t, arg, tko, nil);
969 	if(e != nil)
970 		return e;
971 
972 	d = t->display;
973 	locked = lockdisplay(d);
974 	if(c.def)
975 		tkcursorswitch(t, nil, nil);
976 	if(c.img != nil || c.bit != nil){
977 		e = tkcursorswitch(t, c.bit, c.img);
978 		tkimgput(c.img);
979 		freeimage(c.bit);
980 	}
981 	if(e == nil){
982 		if(c.p.x != Notset && c.p.y != Notset)
983 			tkcursorset(t, c.p);
984 	}
985 	if(locked)
986 		unlockdisplay(d);
987 	return e;
988 }
989 
990 char *
tkbindings(TkTop * t,Tk * tk,TkEbind * b,int blen)991 tkbindings(TkTop *t, Tk *tk, TkEbind *b, int blen)
992 {
993 	TkAction *a, **ap;
994 	char *cmd, *e;
995 	int i;
996 
997 	e = nil;
998 	for(i = 0; e == nil && i < blen; i++)	/* default bindings */ {
999 		int how = TkArepl;
1000 		char *cmd = b[i].cmd;
1001 		if(cmd[0] == '+') {
1002 			how = TkAadd;
1003 			cmd++;
1004 		}
1005 		else if(cmd[0] == '-'){
1006 			how = TkAsub;
1007 			cmd++;
1008 		}
1009 		e = tkaction(&tk->binds, b[i].event, TkStatic, cmd, how);
1010 	}
1011 
1012 	if(e != nil)
1013 		return e;
1014 
1015 	ap = &tk->binds;
1016 	for(a = t->binds[tk->type]; a; a = a->link) {	/* user "defaults" */
1017 		cmd = strdup(a->arg);
1018 		if(cmd == nil)
1019 			return TkNomem;
1020 
1021 		e = tkaction(ap, a->event, TkDynamic, cmd,
1022 						(a->type >> 8) & 0xff);
1023 		if(e != nil)
1024 			return e;
1025 		ap = &(*ap)->link;
1026 	}
1027 	return nil;
1028 }
1029