ref: 54bac038f411c10a596adf84c06df32f8c7c4c53
dir: /libinterp/tk.c/
#include "lib9.h" #include "interp.h" #include "isa.h" #include "runt.h" #include "draw.h" #include "tk.h" #include "tkmod.h" #include "pool.h" #include "drawif.h" #include "keyboard.h" #include "raise.h" #include "kernel.h" extern void tkfreetop(Heap*, int); Type* fakeTkTop; static uchar TktypeMap[] = Tk_Toplevel_map; int tkstylus; void (*tkwiretap)(void*, char*, char*, void*, Rectangle*); static void tktopimagedptr(TkTop*, Draw_Image*); static char*tkputwinimage(Tk*, Draw_Image*, int); static void lockctxt(TkCtxt *ctxt) { libqlock(ctxt->lock); } static void unlockctxt(TkCtxt *ctxt) { libqunlock(ctxt->lock); } static void tkmarktop(Type *t, void *vw) { Heap *h; TkVar *v; TkPanelimage *di; TkTop *top; Tk *w, *next; TkWin *tkw; markheap(t, vw); top = vw; // XXX do we need to lock context here?? for(v = top->vars; v; v = v->link) { if(v->type == TkVchan) { h = D2H(v->value); Setmark(h); } } for (di = top->panelimages; di != nil; di = di->link) { h = D2H(di->image); Setmark(h); } for(w = top->windows; w != nil; w = next){ tkw = TKobj(TkWin, w); if(tkw->image != nil){ h = D2H(tkw->di); Setmark(h); } next = tkw->next; } } void tkmodinit(void) { builtinmod("$Tk", Tkmodtab, Tkmodlen); fmtinstall('v', tkeventfmt); /* XXX */ fakeTkTop = dtype(tkfreetop, sizeof(TkTop), TktypeMap, sizeof(TktypeMap)); fakeTkTop->mark = tkmarktop; tksorttable(); } void Tk_toplevel(void *a) { Tk *tk; Heap *h; TkTop *t; TkWin *tkw; TkCtxt *ctxt; Display *disp; F_Tk_toplevel *f = a; void *r; r = *f->ret; *f->ret = H; destroy(r); disp = checkdisplay(f->d); h = heapz(fakeTkTop); t = H2D(TkTop*, h); poolimmutable(h); t->dd = f->d; D2H(t->dd)->ref++; t->execdepth = -1; t->display = disp; tk = tknewobj(t, TKframe, sizeof(Tk)+sizeof(TkWin)); if(tk == nil) { destroy(t); return; } tk->act.x = 0; tk->act.y = 0; tk->act.width = 1; /* XXX why not zero? */ tk->act.height = 1; tk->flag |= Tkwindow; tkw = TKobj(TkWin, tk); tkw->di = H; tktopopt(tk, string2c(f->arg)); tk->geom = tkmoveresize; tk->name = tkmkname("."); if(tk->name == nil) { tkfreeobj(tk); destroy(t); return; } ctxt = tknewctxt(disp); if(ctxt == nil) { tkfreeobj(tk); destroy(t); return; } t->ctxt = ctxt; t->screenr = disp->image->r; tkw->next = t->windows; t->windows = tk; t->root = tk; Setmark(h); poolmutable(h); t->wreq = cnewc(&Tptr, movp, 8); *f->ret = (Tk_Toplevel*)t; } void Tk_cmd(void *a) { TkTop *t; char *val, *e; F_Tk_cmd *f = a; t = (TkTop*)f->t; if(t == H || D2H(t)->t != fakeTkTop) { retstr(TkNotop, f->ret); return; } lockctxt(t->ctxt); val = nil; e = tkexec(t, string2c(f->arg), &val); unlockctxt(t->ctxt); if(e == TkNomem){ free(val); error(exNomem); /* what about f->ret? */ } if(e != nil && t->errx[0] != '\0'){ char *s = tkerrstr(t, e); retstr(s, f->ret); free(s); } else retstr(e == nil ? val : e, f->ret); if(tkwiretap != nil) tkwiretap(t, string2c(f->arg), val, nil, nil); free(val); } void Tk_color(void *fp) { ulong rgba; F_Tk_color *f = fp; if(tkparsecolor(string2c(f->col), &rgba) != nil) *f->ret = DNotacolor; else *f->ret = rgba; } void Tk_rect(void *fp) { F_Tk_rect *f = fp; Tk *tk; TkTop *t; Rectangle r; Point o; int bd, flags, w, h; t = (TkTop*)f->t; if(t == H || D2H(t)->t != fakeTkTop){ *(Rectangle*)f->ret = ZR; return; } lockctxt(t->ctxt); tk = tklook(t, string2c(f->name), 0); if(tk == nil){ *(Rectangle*)f->ret = ZR; unlockctxt(t->ctxt); return; } o = tkposn(tk); flags = f->flags; if(flags & Tk_Local) o = subpt(o, tkposn(tk->env->top->root)); if(flags & Tk_Required){ h = tk->req.height; w = tk->req.width; }else{ h = tk->act.height; w = tk->act.width; } unlockctxt(t->ctxt); if(w < 0) w = 0; if(h < 0) h = 0; bd = tk->borderwidth; if(bd < 0) bd = 0; if(flags & Tk_Border){ r.min = o; r.max.x = r.min.x + w + bd + bd; r.max.y = r.min.y + h + bd + bd; }else{ r.min.x = o.x + bd; r.min.y = o.y + bd; r.max.x = r.min.x + w; r.max.y = r.min.y + h; } *(Rectangle*)f->ret = r; } int tkdescendant(Tk *p, Tk *c) { int n; if(c == nil || p->env->top != c->env->top) return 0; if (p->name != nil && c->name != nil) { n = strlen(p->name->name); if(strncmp(p->name->name, c->name->name, n) == 0) return 1; } return 0; } void tkenterleave(TkTop *t) { Tk *fw, *ent; TkMouse m; TkTop *t1, *t2; TkCtxt *c; c = t->ctxt; m = c->mstate; if (c->mgrab != nil && (c->mgrab->flag & Tknograb)) { fw = tkfindfocus(t, m.x, m.y, 1); if (fw != c->mgrab && fw != nil && (fw->flag & Tknograb) == 0) fw = nil; } else if (c->focused) { fw = tkfindfocus(t, m.x, m.y, 1); if (fw != c->mfocus) fw = nil; } else if (c->mgrab != nil) { fw = tkfindfocus(t, m.x, m.y, 1); if (fw != nil) { if (!tkdescendant(c->mgrab, fw) && !(fw->flag & c->mgrab->flag & Tknograb)) fw = nil; } } else if (m.b == 0) fw = tkfindfocus(t, m.x, m.y, 0); else if (tkfindfocus(t, m.x, m.y, 1) == c->entered) return; else fw = nil; if (c->entered == fw) return; t1 = t2 = nil; if (c->entered != nil) { ent = c->entered; t1 = ent->env->top; c->entered = nil; tkdeliver(ent, TkLeave, nil); } if (fw != nil) { t2 = fw->env->top; c->entered = fw; tkdeliver(fw, TkEnter, &m); } if (t1 != nil) tkupdate(t1); if (t2 != nil && t1 != t2) tkupdate(t2); } void Tk_pointer(void *a) { static int buttonr[] = {TkButton1R, TkButton2R, TkButton3R, TkButton4R, TkButton5R, TkButton6R}; static int buttonp[] = {TkButton1P, TkButton2P, TkButton3P, TkButton4P, TkButton5P, TkButton6P}; Tk *fw, *target, *dest, *ent; TkMouse m; TkCtxt *c; TkTop *t, *ot; int d, dtype, etype; F_Tk_pointer *f = a; int b, lastb, inside; t = (TkTop*)f->t; if(t == H || D2H(t)->t != fakeTkTop) return; c = t->ctxt; /* ignore no-button-motion for emulated stylus input */ if(tkstylus && c->mstate.b == 0 && (f->p.buttons&0x1f)==0) return; lockctxt(c); //if (f->p.buttons != 0 || c->mstate.b != 0) //print("tkmouse %d [%d %d], focused %d[%s], grab %s, entered %s\n", // f->p.buttons, f->p.xy.x, f->p.xy.y, c->focused, tkname(c->mfocus), tkname(c->mgrab), tkname(c->entered)); /* * target is the widget that we're deliver the mouse event to. * inside is true if the mouse point is located inside target. */ inside = 1; if (c->mgrab != nil && (c->mgrab->flag & Tknograb)) { fw = tkfindfocus(t, f->p.xy.x, f->p.xy.y, 1); if (fw != nil && (fw->flag & Tknograb)) target = fw; else { target = c->mgrab; inside = 0; } } else if (c->focused) { if (c->mfocus != nil) { fw = tkfindfocus(t, f->p.xy.x, f->p.xy.y, 1); if (fw != c->mfocus) inside = 0; } target = c->mfocus; } else if (c->mgrab != nil && (c->mgrab->flag & Tkdisabled) == 0) { /* * XXX this isn't quite right, as perhaps we should do a tkinwindow() * (below the grab). * so that events to containers underneath the grab arrive * via the containers (as is usual) */ fw = tkfindfocus(t, f->p.xy.x, f->p.xy.y, 1); if (fw != nil && tkdescendant(c->mgrab, fw)) target = fw; else { target = c->mgrab; inside = 0; } } else target = tkfindfocus(t, f->p.xy.x, f->p.xy.y, 0); lastb = c->mstate.b; c->mstate.x = f->p.xy.x; c->mstate.y = f->p.xy.y; c->mstate.b = f->p.buttons & 0x1f; /* Just the buttons */ m = c->mstate; /* XXX if the mouse is being moved with the buttons held down * and we've no mfocus and no mgrab then ignore * the event as our original target has gone away (or never existed) */ if (lastb && m.b && !c->focused && c->mgrab == nil) target = nil; if (target != c->entered || (c->entered != nil && !inside)) { if (c->entered != nil) { fw = c->entered; c->entered = nil; tkdeliver(fw, TkLeave, nil); if (target == nil || fw->env->top != target->env->top) tkupdate(fw->env->top); } if (inside) { c->entered = target; tkdeliver(target, TkEnter, &m); } } dest = nil; if (target != nil) { etype = 0; dtype = 0; if(f->p.buttons & (1<<8)) /* Double */ dtype = TkDouble; d = lastb ^ m.b; if (d) { /* cancel any autorepeat, notifying existing client */ tkrepeat(nil, nil, nil, 0, 0); if (d & ~lastb & 1) /* button 1 potentially takes the focus */ tkdeliver(target, TkTakefocus|TkButton1P, &m); } for(b=0; b<nelem(buttonp); b++){ if(d & (1<<b)){ etype = buttonr[b]; if(m.b & (1<<b)) etype = buttonp[b]|dtype; dest = tkdeliver(target, etype, &m); } } if(tkstylus && m.b==0) { if ((ent = c->entered) != nil) { c->entered = nil; ot = ent->env->top; tkdeliver(ent, TkLeave, nil); if (ot != target->env->top) tkupdate(ot); } } else if(etype == 0) { etype = TkMotion; for(b = 0; b<nelem(buttonp); b++) if (m.b & (1<<b)) etype |= buttonp[b]; tkdeliver(target, etype, &m); } if (m.b != 0) { if (lastb == 0 && !c->focused) { /* (some deliver might have grabbed it...) */ if (dest == nil) dest = target; if ((dest->flag & Tknograb) == 0) { c->focused = 1; c->mfocus = dest; } } } else { c->focused = 0; c->mfocus = nil; if (lastb != 0) tkenterleave(t); } tkupdate(target->env->top); } else if (c->focused && m.b == 0) { c->focused = 0; tkenterleave(t); } unlockctxt(c); } void Tk_keyboard(void *a) { Tk *grab; TkTop *t; TkCtxt *c; F_Tk_keyboard *f = a; t = (TkTop*)f->t; if(t == H || D2H(t)->t != fakeTkTop) return; c = t->ctxt; if (c == nil) return; lockctxt(c); if (c->tkmenu != nil) grab = c->tkmenu; else grab = c->tkkeygrab; if(grab == nil){ unlockctxt(c); return; } t = grab->env->top; tkdeliver(grab, TkKey|TKKEY(f->key), nil); tkupdate(t); unlockctxt(c); } TkVar* tkmkvar(TkTop *t, char *name, int type) { TkVar *v; for(v = t->vars; v; v = v->link) if(strcmp(v->name, name) == 0) return v; if(type == 0) return nil; v = malloc(sizeof(TkVar)+strlen(name)+1); if(v == nil) return nil; strcpy(v->name, name); v->link = t->vars; t->vars = v; v->type = type; v->value = nil; if(type == TkVchan) v->value = H; return v; } void tkfreevar(TkTop *t, char *name, int swept) { TkVar **l, *p; if(name == nil) return; l = &t->vars; for(p = *l; p != nil; p = p->link) { if(strcmp(p->name, name) == 0) { *l = p->link; switch(p->type) { default: free(p->value); break; case TkVchan: if(!swept) destroy(p->value); break; } free(p); return; } l = &p->link; } } void Tk_namechan(void *a) { Heap *h; TkVar *v; TkTop *t; char *name; F_Tk_namechan *f = a; t = (TkTop*)f->t; if(t == H || D2H(t)->t != fakeTkTop) { retstr(TkNotop, f->ret); return; } if(f->c == H) { retstr("nil channel", f->ret); return; } name = string2c(f->n); if(name[0] == '\0') { retstr(TkBadvl, f->ret); return; } lockctxt(t->ctxt); v = tkmkvar(t, name, TkVchan); if(v == nil) { unlockctxt(t->ctxt); retstr(TkNomem, f->ret); return; } if(v->type != TkVchan) { unlockctxt(t->ctxt); retstr(TkNotvt, f->ret); return; } destroy(v->value); v->value = f->c; unlockctxt(t->ctxt); h = D2H(v->value); h->ref++; Setmark(h); retstr("", f->ret); } void Tk_quote(void *a) { String *s, *ns; F_Tk_quote *f; void *r; int c, i, need, len, userune, last, n; Rune *sr; char *sc; f = a; r = *f->ret; *f->ret = H; destroy(r); s = f->s; if(s == H){ retstr("{}", f->ret); return; } len = s->len; userune = 0; if(len < 0) { len = -len; userune = 1; } need = len+2; for(i = 0; i < len; i++) { c = userune? s->Srune[i]: s->Sascii[i]; if(c == '{' || c == '}' || c == '\\') need++; } if(userune) { ns = newrunes(need); sr = ns->Srune; *sr++ = '{'; last = 0; for(i = 0;; i++) { if(i >= len || (c = s->Srune[i]) == '{' || c == '}' || c == '\\'){ n = i-last; if(n) { memmove(sr, &s->Srune[last], n*sizeof(Rune)); sr += n; } if(i >= len) break; *sr++ = '\\'; last = i; } } *sr = '}'; } else { ns = newstring(need); sc = ns->Sascii; *sc++ = '{'; last = 0; for(i = 0;; i++) { if(i >= len || (c = s->Sascii[i]) == '{' || c == '}' || c == '\\'){ n = i-last; if(n) { memmove(sc, &s->Sascii[last], n); sc += n; } if(i >= len) break; *sc++ = '\\'; last = i; } } *sc= '}'; } *f->ret = ns; } static void tkreplimg(TkTop *t, Draw_Image *f, Draw_Image *m, Image **ximg) { Display *d; Image *cimg, *cmask, *new; cimg = lookupimage(f); d = t->display; if(cimg == nil || cimg->screen != nil || cimg->display != d) return; cmask = lookupimage(m); if(cmask != nil && (cmask->screen != nil || cmask->display != d)) return; if (cmask == nil) new = allocimage(d, Rect(0, 0, Dx(cimg->r), Dy(cimg->r)), cimg->chan, 0, DNofill); else { if(cmask->screen != nil || cmask->display != d) return; new = allocimage(d, Rect(0, 0, Dx(cimg->r), Dy(cimg->r)), RGBA32, 0, DTransparent); } if(new == nil) return; draw(new, new->r, cimg, cmask, cimg->r.min); if(tkwiretap != nil) tkwiretap(t, "replimg", nil, cimg, &cimg->r); if(*ximg != nil) freeimage(*ximg); *ximg = new; } static char* tkaddpanelimage(TkTop *t, Draw_Image *di, Image **i) { TkPanelimage *pi; if (di == H) { *i = 0; return nil; } *i = lookupimage(di); if (*i == nil || (*i)->display != t->display) return TkNotwm; for (pi = t->panelimages; pi != nil; pi = pi->link) { if (pi->image == di) { pi->ref++; return nil; } } pi = malloc(sizeof(TkPanelimage)); if (pi == nil) return TkNomem; pi->image = di; D2H(di)->ref++; pi->ref = 1; pi->link = t->panelimages; t->panelimages = pi; return nil; } void tkdelpanelimage(TkTop *t, Image *i) { TkPanelimage *pi, *prev; int locked; if (i == nil) return; prev = nil; for (pi = t->panelimages; pi != nil; pi = pi->link) { if (lookupimage(pi->image) == i) break; prev = pi; } if (pi == nil || --pi->ref > 0) return; if (prev) prev->link = pi->link; else t->panelimages = pi->link; if (D2H(pi->image)->ref == 1) { /* don't bother locking if it's not going away */ locked = lockdisplay(t->display); destroy(pi->image); if (locked) unlockdisplay(t->display); } free(pi); } void Tk_putimage(void *a) { TkTop *t; TkImg *tki; Image *i, *m, *oldi, *oldm; int locked, found, reqid, n; char *words[2]; Display *d; F_Tk_putimage *f; void *r; char *name, *e; Tk *tk; f = a; r = *f->ret; *f->ret = H; destroy(r); t = (TkTop*)f->t; if(t == H || D2H(t)->t != fakeTkTop) { retstr(TkNotop, f->ret); return; } if(f->i == H) { retstr(TkBadvl, f->ret); return; } name = string2c(f->name); lockctxt(t->ctxt); e = nil; found = 0; if(name[0] == '.'){ n = getfields(name, words, nelem(words), 1, " "); reqid = -1; if(n > 1){ reqid = atoi(words[1]); name = words[0]; } if((tk = tklook(t, name, 0)) != nil){ if(tk->type == TKchoicebutton){ tk = tkfindchoicemenu(tk); if(tk == nil) goto Error; } if(tk->type == TKframe || tk->type == TKmenu){ if((tk->flag & Tkwindow) == 0){ e = TkNotwm; goto Error; } e = tkputwinimage(tk, f->i, reqid); found = 1; } else if(tk->type == TKpanel){ if(n > 1){ e = TkBadvl; goto Error; } e = tkaddpanelimage(t, f->i, &i); if(e != nil) goto Error; e = tkaddpanelimage(t, f->m, &m); if(e != nil){ tkdelpanelimage(t, i); goto Error; } tkgetpanelimage(tk, &oldi, &oldm); tkdelpanelimage(t, oldi); tkdelpanelimage(t, oldm); tksetpanelimage(tk, i, m); tkdirty(tk); found = 1; } } } if(!found){ /* XXX perhaps we shouldn't ever do this if name begins with '.'? */ tki = tkname2img(t, name); if(tki == nil) { e = TkBadvl; goto Error; } d = t->display; locked = lockdisplay(d); tkreplimg(t, f->i, f->m, &tki->img); if(locked) unlockdisplay(d); tksizeimage(t->root, tki); } Error: unlockctxt(t->ctxt); if(e != nil) retstr(e, f->ret); return; } Draw_Image* tkimgcopy(TkTop *t, Image *cimg) { Image *new; Display *dp; Draw_Image *i; if(cimg == nil) return H; dp = t->display; new = allocimage(dp, cimg->r, cimg->chan, cimg->repl, DNofill); if(new == nil) return H; new->clipr = cimg->clipr; drawop(new, new->r, cimg, nil, cimg->r.min, S); if(tkwiretap != nil) tkwiretap(t, "imgcopy", nil, cimg, &cimg->r); i = mkdrawimage(new, H, t->dd, nil); if(i == H) freeimage(new); return i; } void Tk_getimage(void *a) { Tk *tk; char *n; TkImg *i; TkTop *t; int locked; Display *d; F_Tk_getimage *f; void *r; void (*getimgs)(Tk*, Image**, Image**); Image *image, *mask; f = a; r = f->ret->t0; f->ret->t0 = H; destroy(r); r = f->ret->t1; f->ret->t1 = H; destroy(r); r = f->ret->t2; f->ret->t2 = H; destroy(r); t = (TkTop*)f->t; if(t == H || D2H(t)->t != fakeTkTop) { retstr(TkNotop, &f->ret->t2); return; } d = t->ctxt->display; n = string2c(f->name); lockctxt(t->ctxt); i = tkname2img(t, n); if (i != nil) { image = i->img; mask = nil; } else { tk = tklook(t, n, 0); if (tk == nil || (getimgs = tkmethod[tk->type]->getimgs) == nil) { unlockctxt(t->ctxt); retstr(TkBadvl, &f->ret->t2); return; } getimgs(tk, &image, &mask); } locked = lockdisplay(d); f->ret->t0 = tkimgcopy(t, image); if (mask != nil) f->ret->t1 = tkimgcopy(t, mask); if (locked) unlockdisplay(d); unlockctxt(t->ctxt); } void tkfreetop(Heap *h, int swept) { TkTop *t; Tk *f; TkImg *i, *nexti; TkVar *v, *nextv; int wgtype; void *r; TkPanelimage *pi, *nextpi; t = H2D(TkTop*, h); lockctxt(t->ctxt); if(swept) { t->di = H; t->dd = H; t->wreq = H; t->wmctxt = H; } t->windows = nil; for(f = t->root; f; f = f->siblings) { f->flag |= Tkdestroy; tkdeliver(f, TkDestroy, nil); if(f->destroyed != nil) f->destroyed(f); } for(f = t->root; f; f = t->root) { t->root = f->siblings; if(swept) f->flag |= Tkswept; tkfreeobj(f); } for(v = t->vars; v; v = nextv) { nextv = v->link; switch(v->type) { default: free(v->value); break; case TkVchan: if(!swept) destroy(v->value); break; } free(v); } for (pi = t->panelimages; pi; pi = nextpi) { if (!swept) destroy(pi->image); nextpi = pi->link; free(pi); } for(i = t->imgs; i; i = nexti) { if(i->ref != 1) abort(); nexti = i->link; tkimgput(i); } /* XXX free images inside widgets */ for(wgtype = 0; wgtype < TKwidgets; wgtype++) if(t->binds[wgtype]) tkfreebind(t->binds[wgtype]); unlockctxt(t->ctxt); /* XXX should we leave it locked for this bit? */ tkfreectxt(t->ctxt); if(!swept) { r = t->di; t->di = H; destroy(r); r = t->dd; t->dd = H; destroy(r); r = t->wreq; t->wreq = H; destroy(r); r = t->wmctxt; t->wmctxt = H; destroy(r); } } static void tktopimagedptr(TkTop *top, Draw_Image *di) { if(top->di != H){ destroy(top->di); top->di = H; } if(di == H) return; D2H(di)->ref++; top->di = di; } static void tkfreewinimage(TkWin *w) { destroy(w->di); w->image = nil; w->di = H; } static int tksetwindrawimage(Tk *tk, Draw_Image *di) { TkWin *tkw; char *name; Image *i; int locked; int same; tkw = TKobj(TkWin, tk); same = tkw->di == di; if(!same) if(tkw->image != nil) destroy(tkw->di); if(di == H){ tkw->di = H; tkw->image = nil; return same; } tkw->di = di; i = lookupimage(di); tkw->image = i; locked = lockdisplay(i->display); if(originwindow(i, ZP, i->r.min) == -1) print("tk originwindow failed: %r\n"); di->r = DRECT(i->r); di->clipr = DRECT(i->clipr); if(locked) unlockdisplay(i->display); if(!same){ D2H(di)->ref++; if(tk->name){ name = tk->name->name; if(name[0] == '.' && name[1] == '\0') tktopimagedptr(tk->env->top, tkw->di); } } return same; } void tkdestroywinimage(Tk *tk) { TkWin *tkw; TkTop *top; char *name; assert(tk->flag & Tkwindow); tkw = TKobj(TkWin, tk); top = tk->env->top; if(tkw->image != nil && !(tk->flag & Tkswept)) destroy(tkw->di); tkw->di = H; tkw->image = nil; if(tk->name == nil) name = tkw->cbname; else name = tk->name->name; if(name[0] == '.' && name[1] == '\0' && !(tk->flag & Tkswept)) tktopimagedptr(top, H); tkw->reqid++; tkwreq(top, "delete %s", name); } static char* tkputwinimage(Tk *tk, Draw_Image *di, int reqid) { TkWin *tkw; TkTop *top; Image *i; int bw2, prop, resize; Rectangle req; top = tk->env->top; tkw = TKobj(TkWin, tk); i = lookupimage(di); if (i == nil || i->display != top->display) return TkNotwm; if(reqid != -1 && reqid < tkw->reqid) return "!request out of date"; bw2 = 2*tk->borderwidth; req.min.x = tkw->req.x; req.min.y = tkw->req.y; req.max.x = req.min.x + tk->act.width + bw2; req.max.y = req.min.y + tk->act.height + bw2; resize = 0; if(eqrect(req, i->r) == 0){ /* * if we'd sent a request and our requested rect has now changed, * then resend the request (via tkupdatewinsize), * otherwise accept the new size and repack if necessary */ if(reqid != -1 && tkw->changed){ if(tkupdatewinsize(tk)) return "!requested size has changed"; } else if(Dx(req) != Dx(i->r) || Dy(req) != Dy(i->r)){ tk->flag |= Tksuspended; tk->act.width = Dx(i->r) - bw2; tk->act.height = Dy(i->r) - bw2; tk->req = tk->act; prop = tk->flag & Tknoprop; tk->flag |= Tknoprop; tkpackqit(tk); tkrunpack(top); tk->flag = (tk->flag & ~Tknoprop) | prop; resize = 1; } } if(reqid == -1) tkw->reqid++; /* invalidate all buffered requests. */ tkw->act = i->r.min; tkw->req = tkw->act; tkw->changed = 0; tk->req.width = Dx(i->r) - bw2; tk->req.height = Dy(i->r) - bw2; tk->act = tk->req; if((tk->flag & Tkmapped) == 0){ tk->flag |= Tkmapped; tkdeliver(tk, TkMap, nil); } if(tksetwindrawimage(tk, di) == 0 || resize){ tk->dirty = tkrect(tk, 1); tk->flag |= Tkrefresh; } tk->flag &= ~Tksuspended; lookupimage(di); /* make sure limbo image coords correspond correctly */ tkupdate(top); return nil; } void tkwreq(TkTop *top, char *fmt, ...) { char *buf; va_list arg; va_start(arg, fmt); buf = vsmprint(fmt, arg); va_end(arg); tktolimbo(top->wreq, buf); free(buf); } int tktolimbo(void *var, char *msg) { void *ptrs[1]; int r; if(var==H) return 0; ptrs[0] = H; retstr(msg, (String**) &ptrs[0]); r = csendalt((Channel *)var, ptrs, &Tptr, TkMaxmsgs); return r; } static void hexify(char *buf, int n) { static char hex[] = "0123456789abcdef"; uchar b; char *dp, *fp; fp = buf+n; dp = buf+n*2; *dp-- = '\0'; while(fp-- > buf){ b = (uchar)*fp; *dp-- = hex[b & 0xf]; *dp-- = hex[b >> 4]; } } char* tkcursorswitch(TkTop *top, Image *i, TkImg *img) { Image *ci, *scratch; char *buf; Rectangle r; int n, maxb, nb; if(i == nil && img == nil){ tktolimbo(top->wreq, "cursor"); return nil; } if(img != nil){ if(img->cursor){ tktolimbo(top->wreq, img->cursor); return nil; } i = img->img; } if(i->depth != 1 || Dx(i->r)*Dy(i->r) > 16000 || Dy(i->r)%8 != 0 || Dy(i->r)%2 != 0) return TkBadcursor; /* * readjust image, inferring hotspot from origin. */ if(i->r.min.x != 0 || i->r.min.y != 0){ r.min.x = 0; r.min.y = 0; r.max.x = Dx(i->r); r.max.y = Dy(i->r); scratch = allocimage(i->display, r, GREY1, 0, DNofill); if(scratch == nil) return TkNomem; draw(scratch, r, i, nil, i->r.min); ci = scratch; }else{ scratch = nil; ci = i; } nb = ci->r.max.x/8 * ci->r.max.y; maxb = 7 + 12*4 + 2*nb + 1; buf = mallocz(maxb, 0); if(buf == nil) return TkNomem; n = sprint(buf, "cursor %d %d %d %d ", i->r.min.x, i->r.min.y, ci->r.max.x, ci->r.max.y); unloadimage(ci, ci->r, (uchar*)buf+n, maxb-n); hexify(buf+n, nb); tktolimbo(top->wreq, buf); if(img != nil){ free(img->cursor); img->cursor = buf; } freeimage(scratch); return nil; } void tkcursorset(TkTop *t, Point p) { tkwreq(t, "ptr %d %d", p.x, p.y); }