1 #include "lib9.h"
2 #include "interp.h"
3 #include "isa.h"
4 #include "runt.h"
5 #include "draw.h"
6 #include "tk.h"
7 #include "tkmod.h"
8 #include "pool.h"
9 #include "drawif.h"
10 #include "keyboard.h"
11 #include "raise.h"
12 #include "kernel.h"
13
14 extern void tkfreetop(Heap*, int);
15 Type* fakeTkTop;
16 static uchar TktypeMap[] = Tk_Toplevel_map;
17 int tkstylus;
18 void (*tkwiretap)(void*, char*, char*, void*, Rectangle*);
19
20 static void tktopimagedptr(TkTop*, Draw_Image*);
21 static char*tkputwinimage(Tk*, Draw_Image*, int);
22
23 static void
lockctxt(TkCtxt * ctxt)24 lockctxt(TkCtxt *ctxt)
25 {
26 libqlock(ctxt->lock);
27 }
28
29 static void
unlockctxt(TkCtxt * ctxt)30 unlockctxt(TkCtxt *ctxt)
31 {
32 libqunlock(ctxt->lock);
33 }
34
35 static void
tkmarktop(Type * t,void * vw)36 tkmarktop(Type *t, void *vw)
37 {
38 Heap *h;
39 TkVar *v;
40 TkPanelimage *di;
41 TkTop *top;
42 Tk *w, *next;
43 TkWin *tkw;
44
45 markheap(t, vw);
46 top = vw;
47 // XXX do we need to lock context here??
48 for(v = top->vars; v; v = v->link) {
49 if(v->type == TkVchan) {
50 h = D2H(v->value);
51 Setmark(h);
52 }
53 }
54 for (di = top->panelimages; di != nil; di = di->link) {
55 h = D2H(di->image);
56 Setmark(h);
57 }
58 for(w = top->windows; w != nil; w = next){
59 tkw = TKobj(TkWin, w);
60 if(tkw->image != nil){
61 h = D2H(tkw->di);
62 Setmark(h);
63 }
64 next = tkw->next;
65 }
66 }
67
68 void
tkmodinit(void)69 tkmodinit(void)
70 {
71 builtinmod("$Tk", Tkmodtab, Tkmodlen);
72 fmtinstall('v', tkeventfmt); /* XXX */
73
74 fakeTkTop = dtype(tkfreetop, sizeof(TkTop), TktypeMap, sizeof(TktypeMap));
75 fakeTkTop->mark = tkmarktop;
76
77 tksorttable();
78 }
79
80 void
Tk_toplevel(void * a)81 Tk_toplevel(void *a)
82 {
83 Tk *tk;
84 Heap *h;
85 TkTop *t;
86 TkWin *tkw;
87 TkCtxt *ctxt;
88 Display *disp;
89 F_Tk_toplevel *f = a;
90 void *r;
91
92 r = *f->ret;
93 *f->ret = H;
94 destroy(r);
95 disp = checkdisplay(f->d);
96
97 h = heapz(fakeTkTop);
98 t = H2D(TkTop*, h);
99 poolimmutable(h);
100
101 t->dd = f->d;
102 D2H(t->dd)->ref++;
103
104 t->execdepth = -1;
105 t->display = disp;
106
107 tk = tknewobj(t, TKframe, sizeof(Tk)+sizeof(TkWin));
108 if(tk == nil) {
109 destroy(t);
110 return;
111 }
112
113 tk->act.x = 0;
114 tk->act.y = 0;
115 tk->act.width = 1; /* XXX why not zero? */
116 tk->act.height = 1;
117 tk->flag |= Tkwindow;
118
119 tkw = TKobj(TkWin, tk);
120 tkw->di = H;
121
122 tktopopt(tk, string2c(f->arg));
123
124 tk->geom = tkmoveresize;
125 tk->name = tkmkname(".");
126 if(tk->name == nil) {
127 tkfreeobj(tk);
128 destroy(t);
129 return;
130 }
131
132 ctxt = tknewctxt(disp);
133 if(ctxt == nil) {
134 tkfreeobj(tk);
135 destroy(t);
136 return;
137 }
138 t->ctxt = ctxt;
139 t->screenr = disp->image->r;
140
141 tkw->next = t->windows;
142 t->windows = tk;
143 t->root = tk;
144 Setmark(h);
145 poolmutable(h);
146 t->wreq = cnewc(&Tptr, movp, 8);
147 *f->ret = (Tk_Toplevel*)t;
148 }
149
150 void
Tk_cmd(void * a)151 Tk_cmd(void *a)
152 {
153 TkTop *t;
154 char *val, *e;
155 F_Tk_cmd *f = a;
156
157 t = (TkTop*)f->t;
158 if(t == H || D2H(t)->t != fakeTkTop) {
159 retstr(TkNotop, f->ret);
160 return;
161 }
162 lockctxt(t->ctxt);
163 val = nil;
164 e = tkexec(t, string2c(f->arg), &val);
165 unlockctxt(t->ctxt);
166 if(e == TkNomem){
167 free(val);
168 error(exNomem); /* what about f->ret? */
169 }
170 if(e != nil && t->errx[0] != '\0'){
171 char *s = tkerrstr(t, e);
172
173 retstr(s, f->ret);
174 free(s);
175 }
176 else
177 retstr(e == nil ? val : e, f->ret);
178 if(tkwiretap != nil)
179 tkwiretap(t, string2c(f->arg), val, nil, nil);
180 free(val);
181 }
182
183 void
Tk_color(void * fp)184 Tk_color(void *fp)
185 {
186 ulong rgba;
187 F_Tk_color *f = fp;
188 if(tkparsecolor(string2c(f->col), &rgba) != nil)
189 *f->ret = DNotacolor;
190 else
191 *f->ret = rgba;
192 }
193
194 void
Tk_rect(void * fp)195 Tk_rect(void *fp)
196 {
197 F_Tk_rect *f = fp;
198 Tk *tk;
199 TkTop *t;
200 Rectangle r;
201 Point o;
202 int bd, flags, w, h;
203
204 t = (TkTop*)f->t;
205 if(t == H || D2H(t)->t != fakeTkTop){
206 *(Rectangle*)f->ret = ZR;
207 return;
208 }
209 lockctxt(t->ctxt);
210 tk = tklook(t, string2c(f->name), 0);
211 if(tk == nil){
212 *(Rectangle*)f->ret = ZR;
213 unlockctxt(t->ctxt);
214 return;
215 }
216 o = tkposn(tk);
217 flags = f->flags;
218 if(flags & Tk_Local)
219 o = subpt(o, tkposn(tk->env->top->root));
220 if(flags & Tk_Required){
221 h = tk->req.height;
222 w = tk->req.width;
223 }else{
224 h = tk->act.height;
225 w = tk->act.width;
226 }
227 unlockctxt(t->ctxt);
228 if(w < 0)
229 w = 0;
230 if(h < 0)
231 h = 0;
232 bd = tk->borderwidth;
233 if(bd < 0)
234 bd = 0;
235 if(flags & Tk_Border){
236 r.min = o;
237 r.max.x = r.min.x + w + bd + bd;
238 r.max.y = r.min.y + h + bd + bd;
239 }else{
240 r.min.x = o.x + bd;
241 r.min.y = o.y + bd;
242 r.max.x = r.min.x + w;
243 r.max.y = r.min.y + h;
244 }
245 *(Rectangle*)f->ret = r;
246 }
247
248 int
tkdescendant(Tk * p,Tk * c)249 tkdescendant(Tk *p, Tk *c)
250 {
251 int n;
252
253 if(c == nil || p->env->top != c->env->top)
254 return 0;
255
256 if (p->name != nil && c->name != nil) {
257 n = strlen(p->name->name);
258 if(strncmp(p->name->name, c->name->name, n) == 0)
259 return 1;
260 }
261
262 return 0;
263 }
264
265 void
tkenterleave(TkTop * t)266 tkenterleave(TkTop *t)
267 {
268 Tk *fw, *ent;
269 TkMouse m;
270 TkTop *t1, *t2;
271 TkCtxt *c;
272
273 c = t->ctxt;
274 m = c->mstate;
275
276 if (c->mgrab != nil && (c->mgrab->flag & Tknograb)) {
277 fw = tkfindfocus(t, m.x, m.y, 1);
278 if (fw != c->mgrab && fw != nil && (fw->flag & Tknograb) == 0)
279 fw = nil;
280 } else if (c->focused) {
281 fw = tkfindfocus(t, m.x, m.y, 1);
282 if (fw != c->mfocus)
283 fw = nil;
284 } else if (c->mgrab != nil) {
285 fw = tkfindfocus(t, m.x, m.y, 1);
286 if (fw != nil) {
287 if (!tkdescendant(c->mgrab, fw) && !(fw->flag & c->mgrab->flag & Tknograb))
288 fw = nil;
289 }
290 } else if (m.b == 0)
291 fw = tkfindfocus(t, m.x, m.y, 0);
292 else if (tkfindfocus(t, m.x, m.y, 1) == c->entered)
293 return;
294 else
295 fw = nil;
296
297 if (c->entered == fw)
298 return;
299
300 t1 = t2 = nil;
301 if (c->entered != nil) {
302 ent = c->entered;
303 t1 = ent->env->top;
304 c->entered = nil;
305 tkdeliver(ent, TkLeave, nil);
306 }
307
308 if (fw != nil) {
309 t2 = fw->env->top;
310 c->entered = fw;
311 tkdeliver(fw, TkEnter, &m);
312 }
313 if (t1 != nil)
314 tkupdate(t1);
315 if (t2 != nil && t1 != t2)
316 tkupdate(t2);
317 }
318
319 void
Tk_pointer(void * a)320 Tk_pointer(void *a)
321 {
322 static int buttonr[] = {TkButton1R, TkButton2R, TkButton3R, TkButton4R, TkButton5R, TkButton6R};
323 static int buttonp[] = {TkButton1P, TkButton2P, TkButton3P, TkButton4P, TkButton5P, TkButton6P};
324 Tk *fw, *target, *dest, *ent;
325 TkMouse m;
326 TkCtxt *c;
327 TkTop *t, *ot;
328 int d, dtype, etype;
329 F_Tk_pointer *f = a;
330 int b, lastb, inside;
331
332 t = (TkTop*)f->t;
333 if(t == H || D2H(t)->t != fakeTkTop)
334 return;
335
336 c = t->ctxt;
337
338 /* ignore no-button-motion for emulated stylus input */
339 if(tkstylus && c->mstate.b == 0 && (f->p.buttons&0x1f)==0)
340 return;
341
342 lockctxt(c);
343 //if (f->p.buttons != 0 || c->mstate.b != 0)
344 //print("tkmouse %d [%d %d], focused %d[%s], grab %s, entered %s\n",
345 // f->p.buttons, f->p.xy.x, f->p.xy.y, c->focused, tkname(c->mfocus), tkname(c->mgrab), tkname(c->entered));
346 /*
347 * target is the widget that we're deliver the mouse event to.
348 * inside is true if the mouse point is located inside target.
349 */
350 inside = 1;
351 if (c->mgrab != nil && (c->mgrab->flag & Tknograb)) {
352 fw = tkfindfocus(t, f->p.xy.x, f->p.xy.y, 1);
353 if (fw != nil && (fw->flag & Tknograb))
354 target = fw;
355 else {
356 target = c->mgrab;
357 inside = 0;
358 }
359 } else if (c->focused) {
360 if (c->mfocus != nil) {
361 fw = tkfindfocus(t, f->p.xy.x, f->p.xy.y, 1);
362 if (fw != c->mfocus)
363 inside = 0;
364 }
365 target = c->mfocus;
366 } else if (c->mgrab != nil && (c->mgrab->flag & Tkdisabled) == 0) {
367 /*
368 * XXX this isn't quite right, as perhaps we should do a tkinwindow()
369 * (below the grab).
370 * so that events to containers underneath the grab arrive
371 * via the containers (as is usual)
372 */
373 fw = tkfindfocus(t, f->p.xy.x, f->p.xy.y, 1);
374 if (fw != nil && tkdescendant(c->mgrab, fw))
375 target = fw;
376 else {
377 target = c->mgrab;
378 inside = 0;
379 }
380 } else
381 target = tkfindfocus(t, f->p.xy.x, f->p.xy.y, 0);
382
383 lastb = c->mstate.b;
384 c->mstate.x = f->p.xy.x;
385 c->mstate.y = f->p.xy.y;
386 c->mstate.b = f->p.buttons & 0x1f; /* Just the buttons */
387 m = c->mstate;
388
389 /* XXX if the mouse is being moved with the buttons held down
390 * and we've no mfocus and no mgrab then ignore
391 * the event as our original target has gone away (or never existed)
392 */
393 if (lastb && m.b && !c->focused && c->mgrab == nil)
394 target = nil;
395
396 if (target != c->entered || (c->entered != nil && !inside)) {
397 if (c->entered != nil) {
398 fw = c->entered;
399 c->entered = nil;
400 tkdeliver(fw, TkLeave, nil);
401 if (target == nil || fw->env->top != target->env->top)
402 tkupdate(fw->env->top);
403 }
404 if (inside) {
405 c->entered = target;
406 tkdeliver(target, TkEnter, &m);
407 }
408 }
409
410 dest = nil;
411 if (target != nil) {
412 etype = 0;
413 dtype = 0;
414 if(f->p.buttons & (1<<8)) /* Double */
415 dtype = TkDouble;
416
417 d = lastb ^ m.b;
418 if (d) {
419 /* cancel any autorepeat, notifying existing client */
420 tkrepeat(nil, nil, nil, 0, 0);
421 if (d & ~lastb & 1) /* button 1 potentially takes the focus */
422 tkdeliver(target, TkTakefocus|TkButton1P, &m);
423 }
424 for(b=0; b<nelem(buttonp); b++){
425 if(d & (1<<b)){
426 etype = buttonr[b];
427 if(m.b & (1<<b))
428 etype = buttonp[b]|dtype;
429 dest = tkdeliver(target, etype, &m);
430 }
431 }
432 if(tkstylus && m.b==0) {
433 if ((ent = c->entered) != nil) {
434 c->entered = nil;
435 ot = ent->env->top;
436 tkdeliver(ent, TkLeave, nil);
437 if (ot != target->env->top)
438 tkupdate(ot);
439 }
440 } else if(etype == 0) {
441 etype = TkMotion;
442 for(b = 0; b<nelem(buttonp); b++)
443 if (m.b & (1<<b))
444 etype |= buttonp[b];
445 tkdeliver(target, etype, &m);
446 }
447 if (m.b != 0) {
448 if (lastb == 0 && !c->focused) { /* (some deliver might have grabbed it...) */
449 if (dest == nil)
450 dest = target;
451 if ((dest->flag & Tknograb) == 0) {
452 c->focused = 1;
453 c->mfocus = dest;
454 }
455 }
456 } else {
457 c->focused = 0;
458 c->mfocus = nil;
459 if (lastb != 0)
460 tkenterleave(t);
461 }
462 tkupdate(target->env->top);
463 } else if (c->focused && m.b == 0) {
464 c->focused = 0;
465 tkenterleave(t);
466 }
467 unlockctxt(c);
468 }
469
470 void
Tk_keyboard(void * a)471 Tk_keyboard(void *a)
472 {
473 Tk *grab;
474 TkTop *t;
475 TkCtxt *c;
476 F_Tk_keyboard *f = a;
477
478 t = (TkTop*)f->t;
479 if(t == H || D2H(t)->t != fakeTkTop)
480 return;
481 c = t->ctxt;
482 if (c == nil)
483 return;
484 lockctxt(c);
485 if (c->tkmenu != nil)
486 grab = c->tkmenu;
487 else
488 grab = c->tkkeygrab;
489 if(grab == nil){
490 unlockctxt(c);
491 return;
492 }
493
494 t = grab->env->top;
495 tkdeliver(grab, TkKey|TKKEY(f->key), nil);
496 tkupdate(t);
497 unlockctxt(c);
498 }
499
500 TkVar*
tkmkvar(TkTop * t,char * name,int type)501 tkmkvar(TkTop *t, char *name, int type)
502 {
503 TkVar *v;
504
505 for(v = t->vars; v; v = v->link)
506 if(strcmp(v->name, name) == 0)
507 return v;
508
509 if(type == 0)
510 return nil;
511
512 v = malloc(sizeof(TkVar)+strlen(name)+1);
513 if(v == nil)
514 return nil;
515 strcpy(v->name, name);
516 v->link = t->vars;
517 t->vars = v;
518 v->type = type;
519 v->value = nil;
520 if(type == TkVchan)
521 v->value = H;
522 return v;
523 }
524
525 void
tkfreevar(TkTop * t,char * name,int swept)526 tkfreevar(TkTop *t, char *name, int swept)
527 {
528 TkVar **l, *p;
529
530 if(name == nil)
531 return;
532 l = &t->vars;
533 for(p = *l; p != nil; p = p->link) {
534 if(strcmp(p->name, name) == 0) {
535 *l = p->link;
536 switch(p->type) {
537 default:
538 free(p->value);
539 break;
540 case TkVchan:
541 if(!swept)
542 destroy(p->value);
543 break;
544 }
545 free(p);
546 return;
547 }
548 l = &p->link;
549 }
550 }
551
552 void
Tk_namechan(void * a)553 Tk_namechan(void *a)
554 {
555 Heap *h;
556 TkVar *v;
557 TkTop *t;
558 char *name;
559 F_Tk_namechan *f = a;
560
561 t = (TkTop*)f->t;
562 if(t == H || D2H(t)->t != fakeTkTop) {
563 retstr(TkNotop, f->ret);
564 return;
565 }
566 if(f->c == H) {
567 retstr("nil channel", f->ret);
568 return;
569 }
570 name = string2c(f->n);
571 if(name[0] == '\0') {
572 retstr(TkBadvl, f->ret);
573 return;
574 }
575
576 lockctxt(t->ctxt);
577 v = tkmkvar(t, name, TkVchan);
578 if(v == nil) {
579 unlockctxt(t->ctxt);
580 retstr(TkNomem, f->ret);
581 return;
582 }
583 if(v->type != TkVchan) {
584 unlockctxt(t->ctxt);
585 retstr(TkNotvt, f->ret);
586 return;
587 }
588 destroy(v->value);
589 v->value = f->c;
590 unlockctxt(t->ctxt);
591 h = D2H(v->value);
592 h->ref++;
593 Setmark(h);
594 retstr("", f->ret);
595 }
596
597 void
Tk_quote(void * a)598 Tk_quote(void *a)
599 {
600 String *s, *ns;
601 F_Tk_quote *f;
602 void *r;
603 int c, i, need, len, userune, last, n;
604 Rune *sr;
605 char *sc;
606
607 f = a;
608
609 r = *f->ret;
610 *f->ret = H;
611 destroy(r);
612
613 s = f->s;
614 if(s == H){
615 retstr("{}", f->ret);
616 return;
617 }
618 len = s->len;
619 userune = 0;
620 if(len < 0) {
621 len = -len;
622 userune = 1;
623 }
624 need = len+2;
625 for(i = 0; i < len; i++) {
626 c = userune? s->Srune[i]: s->Sascii[i];
627 if(c == '{' || c == '}' || c == '\\')
628 need++;
629 }
630 if(userune) {
631 ns = newrunes(need);
632 sr = ns->Srune;
633 *sr++ = '{';
634 last = 0;
635 for(i = 0;; i++) {
636 if(i >= len || (c = s->Srune[i]) == '{' || c == '}' || c == '\\'){
637 n = i-last;
638 if(n) {
639 memmove(sr, &s->Srune[last], n*sizeof(Rune));
640 sr += n;
641 }
642 if(i >= len)
643 break;
644 *sr++ = '\\';
645 last = i;
646 }
647 }
648 *sr = '}';
649 } else {
650 ns = newstring(need);
651 sc = ns->Sascii;
652 *sc++ = '{';
653 last = 0;
654 for(i = 0;; i++) {
655 if(i >= len || (c = s->Sascii[i]) == '{' || c == '}' || c == '\\'){
656 n = i-last;
657 if(n) {
658 memmove(sc, &s->Sascii[last], n);
659 sc += n;
660 }
661 if(i >= len)
662 break;
663 *sc++ = '\\';
664 last = i;
665 }
666 }
667 *sc= '}';
668 }
669 *f->ret = ns;
670 }
671
672 static void
tkreplimg(TkTop * t,Draw_Image * f,Draw_Image * m,Image ** ximg)673 tkreplimg(TkTop *t, Draw_Image *f, Draw_Image *m, Image **ximg)
674 {
675 Display *d;
676 Image *cimg, *cmask, *new;
677
678 cimg = lookupimage(f);
679 d = t->display;
680 if(cimg == nil || cimg->screen != nil || cimg->display != d)
681 return;
682 cmask = lookupimage(m);
683 if(cmask != nil && (cmask->screen != nil || cmask->display != d))
684 return;
685
686 if (cmask == nil)
687 new = allocimage(d, Rect(0, 0, Dx(cimg->r), Dy(cimg->r)), cimg->chan, 0, DNofill);
688 else {
689 if(cmask->screen != nil || cmask->display != d)
690 return;
691 new = allocimage(d, Rect(0, 0, Dx(cimg->r), Dy(cimg->r)), RGBA32, 0, DTransparent);
692 }
693 if(new == nil)
694 return;
695 draw(new, new->r, cimg, cmask, cimg->r.min);
696 if(tkwiretap != nil)
697 tkwiretap(t, "replimg", nil, cimg, &cimg->r);
698 if(*ximg != nil)
699 freeimage(*ximg);
700 *ximg = new;
701 }
702
703 static char*
tkaddpanelimage(TkTop * t,Draw_Image * di,Image ** i)704 tkaddpanelimage(TkTop *t, Draw_Image *di, Image **i)
705 {
706 TkPanelimage *pi;
707
708 if (di == H) {
709 *i = 0;
710 return nil;
711 }
712
713 *i = lookupimage(di);
714 if (*i == nil || (*i)->display != t->display)
715 return TkNotwm;
716
717 for (pi = t->panelimages; pi != nil; pi = pi->link) {
718 if (pi->image == di) {
719 pi->ref++;
720 return nil;
721 }
722 }
723
724 pi = malloc(sizeof(TkPanelimage));
725 if (pi == nil)
726 return TkNomem;
727 pi->image = di;
728 D2H(di)->ref++;
729 pi->ref = 1;
730 pi->link = t->panelimages;
731 t->panelimages = pi;
732 return nil;
733 }
734
735 void
tkdelpanelimage(TkTop * t,Image * i)736 tkdelpanelimage(TkTop *t, Image *i)
737 {
738 TkPanelimage *pi, *prev;
739 int locked;
740
741 if (i == nil)
742 return;
743
744 prev = nil;
745 for (pi = t->panelimages; pi != nil; pi = pi->link) {
746 if (lookupimage(pi->image) == i)
747 break;
748 prev = pi;
749 }
750 if (pi == nil || --pi->ref > 0)
751 return;
752 if (prev)
753 prev->link = pi->link;
754 else
755 t->panelimages = pi->link;
756 if (D2H(pi->image)->ref == 1) { /* don't bother locking if it's not going away */
757 locked = lockdisplay(t->display);
758 destroy(pi->image);
759 if (locked)
760 unlockdisplay(t->display);
761 }
762
763 free(pi);
764 }
765
766 void
Tk_putimage(void * a)767 Tk_putimage(void *a)
768 {
769 TkTop *t;
770 TkImg *tki;
771 Image *i, *m, *oldi, *oldm;
772 int locked, found, reqid, n;
773 char *words[2];
774 Display *d;
775 F_Tk_putimage *f;
776 void *r;
777 char *name, *e;
778 Tk *tk;
779
780 f = a;
781
782 r = *f->ret;
783 *f->ret = H;
784 destroy(r);
785
786 t = (TkTop*)f->t;
787 if(t == H || D2H(t)->t != fakeTkTop) {
788 retstr(TkNotop, f->ret);
789 return;
790 }
791
792 if(f->i == H) {
793 retstr(TkBadvl, f->ret);
794 return;
795 }
796
797 name = string2c(f->name);
798 lockctxt(t->ctxt);
799 e = nil;
800 found = 0;
801 if(name[0] == '.'){
802 n = getfields(name, words, nelem(words), 1, " ");
803 reqid = -1;
804 if(n > 1){
805 reqid = atoi(words[1]);
806 name = words[0];
807 }
808 if((tk = tklook(t, name, 0)) != nil){
809 if(tk->type == TKchoicebutton){
810 tk = tkfindchoicemenu(tk);
811 if(tk == nil)
812 goto Error;
813 }
814 if(tk->type == TKframe || tk->type == TKmenu){
815 if((tk->flag & Tkwindow) == 0){
816 e = TkNotwm;
817 goto Error;
818 }
819 e = tkputwinimage(tk, f->i, reqid);
820 found = 1;
821 } else
822 if(tk->type == TKpanel){
823 if(n > 1){
824 e = TkBadvl;
825 goto Error;
826 }
827 e = tkaddpanelimage(t, f->i, &i);
828 if(e != nil)
829 goto Error;
830 e = tkaddpanelimage(t, f->m, &m);
831 if(e != nil){
832 tkdelpanelimage(t, i);
833 goto Error;
834 }
835 tkgetpanelimage(tk, &oldi, &oldm);
836 tkdelpanelimage(t, oldi);
837 tkdelpanelimage(t, oldm);
838 tksetpanelimage(tk, i, m);
839 tkdirty(tk);
840 found = 1;
841 }
842 }
843 }
844 if(!found){
845 /* XXX perhaps we shouldn't ever do this if name begins with '.'? */
846 tki = tkname2img(t, name);
847 if(tki == nil) {
848 e = TkBadvl;
849 goto Error;
850 }
851
852 d = t->display;
853 locked = lockdisplay(d);
854 tkreplimg(t, f->i, f->m, &tki->img);
855 if(locked)
856 unlockdisplay(d);
857
858 tksizeimage(t->root, tki);
859 }
860 Error:
861 unlockctxt(t->ctxt);
862 if(e != nil)
863 retstr(e, f->ret);
864 return;
865 }
866
867 Draw_Image*
tkimgcopy(TkTop * t,Image * cimg)868 tkimgcopy(TkTop *t, Image *cimg)
869 {
870 Image *new;
871 Display *dp;
872 Draw_Image *i;
873
874 if(cimg == nil)
875 return H;
876
877 dp = t->display;
878 new = allocimage(dp, cimg->r, cimg->chan, cimg->repl, DNofill);
879 if(new == nil)
880 return H;
881 new->clipr = cimg->clipr;
882
883 drawop(new, new->r, cimg, nil, cimg->r.min, S);
884 if(tkwiretap != nil)
885 tkwiretap(t, "imgcopy", nil, cimg, &cimg->r);
886
887 i = mkdrawimage(new, H, t->dd, nil);
888 if(i == H)
889 freeimage(new);
890
891 return i;
892 }
893
894 void
Tk_getimage(void * a)895 Tk_getimage(void *a)
896 {
897 Tk *tk;
898 char *n;
899 TkImg *i;
900 TkTop *t;
901 int locked;
902 Display *d;
903 F_Tk_getimage *f;
904 void *r;
905 void (*getimgs)(Tk*, Image**, Image**);
906 Image *image, *mask;
907
908 f = a;
909
910 r = f->ret->t0;
911 f->ret->t0 = H;
912 destroy(r);
913 r = f->ret->t1;
914 f->ret->t1 = H;
915 destroy(r);
916 r = f->ret->t2;
917 f->ret->t2 = H;
918 destroy(r);
919
920 t = (TkTop*)f->t;
921 if(t == H || D2H(t)->t != fakeTkTop) {
922 retstr(TkNotop, &f->ret->t2);
923 return;
924 }
925 d = t->ctxt->display;
926 n = string2c(f->name);
927 lockctxt(t->ctxt);
928 i = tkname2img(t, n);
929 if (i != nil) {
930 image = i->img;
931 mask = nil;
932 } else {
933 tk = tklook(t, n, 0);
934 if (tk == nil || (getimgs = tkmethod[tk->type]->getimgs) == nil) {
935 unlockctxt(t->ctxt);
936 retstr(TkBadvl, &f->ret->t2);
937 return;
938 }
939 getimgs(tk, &image, &mask);
940 }
941 locked = lockdisplay(d);
942 f->ret->t0 = tkimgcopy(t, image);
943 if (mask != nil)
944 f->ret->t1 = tkimgcopy(t, mask);
945 if (locked)
946 unlockdisplay(d);
947 unlockctxt(t->ctxt);
948 }
949
950 void
tkfreetop(Heap * h,int swept)951 tkfreetop(Heap *h, int swept)
952 {
953 TkTop *t;
954 Tk *f;
955 TkImg *i, *nexti;
956 TkVar *v, *nextv;
957 int wgtype;
958 void *r;
959 TkPanelimage *pi, *nextpi;
960
961 t = H2D(TkTop*, h);
962 lockctxt(t->ctxt);
963
964 if(swept) {
965 t->di = H;
966 t->dd = H;
967 t->wreq = H;
968 t->wmctxt = H;
969 }
970
971 t->windows = nil;
972
973 for(f = t->root; f; f = f->siblings) {
974 f->flag |= Tkdestroy;
975 tkdeliver(f, TkDestroy, nil);
976 if(f->destroyed != nil)
977 f->destroyed(f);
978 }
979
980 for(f = t->root; f; f = t->root) {
981 t->root = f->siblings;
982 if(swept)
983 f->flag |= Tkswept;
984 tkfreeobj(f);
985 }
986
987 for(v = t->vars; v; v = nextv) {
988 nextv = v->link;
989 switch(v->type) {
990 default:
991 free(v->value);
992 break;
993 case TkVchan:
994 if(!swept)
995 destroy(v->value);
996 break;
997 }
998 free(v);
999 }
1000
1001 for (pi = t->panelimages; pi; pi = nextpi) {
1002 if (!swept)
1003 destroy(pi->image);
1004 nextpi = pi->link;
1005 free(pi);
1006 }
1007
1008 for(i = t->imgs; i; i = nexti) {
1009 if(i->ref != 1)
1010 abort();
1011 nexti = i->link;
1012 tkimgput(i);
1013 }
1014 /* XXX free images inside widgets */
1015
1016 for(wgtype = 0; wgtype < TKwidgets; wgtype++)
1017 if(t->binds[wgtype])
1018 tkfreebind(t->binds[wgtype]);
1019
1020 unlockctxt(t->ctxt);
1021 /* XXX should we leave it locked for this bit? */
1022 tkfreectxt(t->ctxt);
1023 if(!swept) {
1024 r = t->di;
1025 t->di = H;
1026 destroy(r);
1027
1028 r = t->dd;
1029 t->dd = H;
1030 destroy(r);
1031
1032 r = t->wreq;
1033 t->wreq = H;
1034 destroy(r);
1035
1036 r = t->wmctxt;
1037 t->wmctxt = H;
1038 destroy(r);
1039 }
1040 }
1041
1042 static void
tktopimagedptr(TkTop * top,Draw_Image * di)1043 tktopimagedptr(TkTop *top, Draw_Image *di)
1044 {
1045 if(top->di != H){
1046 destroy(top->di);
1047 top->di = H;
1048 }
1049 if(di == H)
1050 return;
1051 D2H(di)->ref++;
1052 top->di = di;
1053 }
1054
1055 static void
tkfreewinimage(TkWin * w)1056 tkfreewinimage(TkWin *w)
1057 {
1058 destroy(w->di);
1059 w->image = nil;
1060 w->di = H;
1061 }
1062
1063 static int
tksetwindrawimage(Tk * tk,Draw_Image * di)1064 tksetwindrawimage(Tk *tk, Draw_Image *di)
1065 {
1066 TkWin *tkw;
1067 char *name;
1068 Image *i;
1069 int locked;
1070 int same;
1071
1072 tkw = TKobj(TkWin, tk);
1073
1074 same = tkw->di == di;
1075 if(!same)
1076 if(tkw->image != nil)
1077 destroy(tkw->di);
1078 if(di == H){
1079 tkw->di = H;
1080 tkw->image = nil;
1081 return same;
1082 }
1083 tkw->di = di;
1084 i = lookupimage(di);
1085 tkw->image = i;
1086
1087 locked = lockdisplay(i->display);
1088 if(originwindow(i, ZP, i->r.min) == -1)
1089 print("tk originwindow failed: %r\n");
1090 di->r = DRECT(i->r);
1091 di->clipr = DRECT(i->clipr);
1092 if(locked)
1093 unlockdisplay(i->display);
1094
1095 if(!same){
1096 D2H(di)->ref++;
1097 if(tk->name){
1098 name = tk->name->name;
1099 if(name[0] == '.' && name[1] == '\0')
1100 tktopimagedptr(tk->env->top, tkw->di);
1101 }
1102 }
1103 return same;
1104 }
1105
1106 void
tkdestroywinimage(Tk * tk)1107 tkdestroywinimage(Tk *tk)
1108 {
1109 TkWin *tkw;
1110 TkTop *top;
1111 char *name;
1112
1113 assert(tk->flag & Tkwindow);
1114 tkw = TKobj(TkWin, tk);
1115 top = tk->env->top;
1116
1117 if(tkw->image != nil && !(tk->flag & Tkswept))
1118 destroy(tkw->di);
1119 tkw->di = H;
1120 tkw->image = nil;
1121 if(tk->name == nil)
1122 name = tkw->cbname;
1123 else
1124 name = tk->name->name;
1125 if(name[0] == '.' && name[1] == '\0' && !(tk->flag & Tkswept))
1126 tktopimagedptr(top, H);
1127 tkw->reqid++;
1128 tkwreq(top, "delete %s", name);
1129 }
1130
1131 static char*
tkputwinimage(Tk * tk,Draw_Image * di,int reqid)1132 tkputwinimage(Tk *tk, Draw_Image *di, int reqid)
1133 {
1134 TkWin *tkw;
1135 TkTop *top;
1136 Image *i;
1137 int bw2, prop, resize;
1138 Rectangle req;
1139
1140 top = tk->env->top;
1141 tkw = TKobj(TkWin, tk);
1142 i = lookupimage(di);
1143 if (i == nil || i->display != top->display)
1144 return TkNotwm;
1145
1146 if(reqid != -1 && reqid < tkw->reqid)
1147 return "!request out of date";
1148
1149 bw2 = 2*tk->borderwidth;
1150 req.min.x = tkw->req.x;
1151 req.min.y = tkw->req.y;
1152 req.max.x = req.min.x + tk->act.width + bw2;
1153 req.max.y = req.min.y + tk->act.height + bw2;
1154
1155 resize = 0;
1156 if(eqrect(req, i->r) == 0){
1157 /*
1158 * if we'd sent a request and our requested rect has now changed,
1159 * then resend the request (via tkupdatewinsize),
1160 * otherwise accept the new size and repack if necessary
1161 */
1162 if(reqid != -1 && tkw->changed){
1163 if(tkupdatewinsize(tk))
1164 return "!requested size has changed";
1165
1166 } else if(Dx(req) != Dx(i->r) || Dy(req) != Dy(i->r)){
1167 tk->flag |= Tksuspended;
1168 tk->act.width = Dx(i->r) - bw2;
1169 tk->act.height = Dy(i->r) - bw2;
1170 tk->req = tk->act;
1171 prop = tk->flag & Tknoprop;
1172 tk->flag |= Tknoprop;
1173 tkpackqit(tk);
1174 tkrunpack(top);
1175 tk->flag = (tk->flag & ~Tknoprop) | prop;
1176 resize = 1;
1177 }
1178 }
1179 if(reqid == -1)
1180 tkw->reqid++; /* invalidate all buffered requests. */
1181 tkw->act = i->r.min;
1182 tkw->req = tkw->act;
1183 tkw->changed = 0;
1184 tk->req.width = Dx(i->r) - bw2;
1185 tk->req.height = Dy(i->r) - bw2;
1186 tk->act = tk->req;
1187 if((tk->flag & Tkmapped) == 0){
1188 tk->flag |= Tkmapped;
1189 tkdeliver(tk, TkMap, nil);
1190 }
1191 if(tksetwindrawimage(tk, di) == 0 || resize){
1192 tk->dirty = tkrect(tk, 1);
1193 tk->flag |= Tkrefresh;
1194 }
1195 tk->flag &= ~Tksuspended;
1196
1197 lookupimage(di); /* make sure limbo image coords correspond correctly */
1198 tkupdate(top);
1199 return nil;
1200 }
1201
1202 void
tkwreq(TkTop * top,char * fmt,...)1203 tkwreq(TkTop *top, char *fmt, ...)
1204 {
1205 char *buf;
1206 va_list arg;
1207
1208 va_start(arg, fmt);
1209 buf = vsmprint(fmt, arg);
1210 va_end(arg);
1211 tktolimbo(top->wreq, buf);
1212 free(buf);
1213 }
1214
1215 int
tktolimbo(void * var,char * msg)1216 tktolimbo(void *var, char *msg)
1217 {
1218 void *ptrs[1];
1219 int r;
1220
1221 if(var==H)
1222 return 0;
1223 ptrs[0] = H;
1224 retstr(msg, (String**) &ptrs[0]);
1225 r = csendalt((Channel *)var, ptrs, &Tptr, TkMaxmsgs);
1226 return r;
1227 }
1228
1229 static void
hexify(char * buf,int n)1230 hexify(char *buf, int n)
1231 {
1232 static char hex[] = "0123456789abcdef";
1233 uchar b;
1234 char *dp, *fp;
1235 fp = buf+n;
1236 dp = buf+n*2;
1237 *dp-- = '\0';
1238 while(fp-- > buf){
1239 b = (uchar)*fp;
1240 *dp-- = hex[b & 0xf];
1241 *dp-- = hex[b >> 4];
1242 }
1243 }
1244
1245 char*
tkcursorswitch(TkTop * top,Image * i,TkImg * img)1246 tkcursorswitch(TkTop *top, Image *i, TkImg *img)
1247 {
1248 Image *ci, *scratch;
1249 char *buf;
1250 Rectangle r;
1251 int n, maxb, nb;
1252
1253 if(i == nil && img == nil){
1254 tktolimbo(top->wreq, "cursor");
1255 return nil;
1256 }
1257
1258 if(img != nil){
1259 if(img->cursor){
1260 tktolimbo(top->wreq, img->cursor);
1261 return nil;
1262 }
1263 i = img->img;
1264 }
1265 if(i->depth != 1 || Dx(i->r)*Dy(i->r) > 16000 || Dy(i->r)%8 != 0 || Dy(i->r)%2 != 0)
1266 return TkBadcursor;
1267 /*
1268 * readjust image, inferring hotspot from origin.
1269 */
1270 if(i->r.min.x != 0 || i->r.min.y != 0){
1271 r.min.x = 0;
1272 r.min.y = 0;
1273 r.max.x = Dx(i->r);
1274 r.max.y = Dy(i->r);
1275 scratch = allocimage(i->display, r, GREY1, 0, DNofill);
1276 if(scratch == nil)
1277 return TkNomem;
1278 draw(scratch, r, i, nil, i->r.min);
1279 ci = scratch;
1280 }else{
1281 scratch = nil;
1282 ci = i;
1283 }
1284 nb = ci->r.max.x/8 * ci->r.max.y;
1285 maxb = 7 + 12*4 + 2*nb + 1;
1286 buf = mallocz(maxb, 0);
1287 if(buf == nil)
1288 return TkNomem;
1289 n = sprint(buf, "cursor %d %d %d %d ", i->r.min.x, i->r.min.y, ci->r.max.x, ci->r.max.y);
1290 unloadimage(ci, ci->r, (uchar*)buf+n, maxb-n);
1291 hexify(buf+n, nb);
1292 tktolimbo(top->wreq, buf);
1293 if(img != nil){
1294 free(img->cursor);
1295 img->cursor = buf;
1296 }
1297 freeimage(scratch);
1298 return nil;
1299 }
1300
1301 void
tkcursorset(TkTop * t,Point p)1302 tkcursorset(TkTop *t, Point p)
1303 {
1304 tkwreq(t, "ptr %d %d", p.x, p.y);
1305 }
1306