#include #include #include "interp.h" #include "isa.h" #include "runt.h" #include "raise.h" #include "drawmod.h" #include "draw.h" #include "drawif.h" #include "memdraw.h" #include "memlayer.h" #define DP if(1){}else print /* * When a Display is remote, it must be locked to synchronize the * outgoing message buffer with the refresh demon, which runs as a * different process. When it is local, the refresh demon does nothing * and it is sufficient to use the interpreter's own acquire/release protection * to lock the buffer. * * Most action to the buffer is caused by calls from Limbo, so locking at * the top before going into the library is good enough. However, the * garbage collector can call the free routines at other times, so they * need to protect themselves whether called through the Draw module * or not; hence the need for check against recursive locking in lockdisplay(). * This also means that we needn't lock around calls to destroy if it's * extra work to do so. */ typedef struct Cache Cache; typedef struct DRef DRef; typedef struct DDisplay DDisplay; typedef struct DImage DImage; typedef struct DScreen DScreen; typedef struct DFont DFont; struct Cache { int ref; char* name; Display*display; union{ Subfont* sf; Font* f; void* ptr; }u; Cache* next; }; /* not visible to Limbo; used only for internal reference counting */ struct DRef { int ref; Display* display; }; struct DDisplay { Draw_Display drawdisplay; Display* display; DRef* dref; }; struct DImage { Draw_Image drawimage; Image* image; void* refreshptr; DRef* dref; int flush; }; struct DScreen { Draw_Screen drawscreen; Screen* screen; DRef* dref; }; struct DFont { Draw_Font drawfont; Font* font; DRef* dref; }; Cache* sfcache[BIHASH]; Cache* fcache[BIHASH]; void* cacheqlock; static Cache *cachelookup(Cache**, Display*, char*); uchar fontmap[] = Draw_Font_map; uchar imagemap[] = Draw_Image_map; uchar screenmap[] = Draw_Screen_map; uchar displaymap[] = Draw_Display_map; Type* TFont; Type* TImage; Type* TScreen; Type* TDisplay; Draw_Image* allocdrawimage(DDisplay*, Draw_Rect, ulong, Image*, int, int); Draw_Image* color(DDisplay*, ulong); Draw_Screen *mkdrawscreen(Screen*, Draw_Display*); char deffontname[] = "*default*"; void refreshslave(Display*); void subfont_close(Subfont*); void freeallsubfonts(Display*); void drawmodinit(void) { TFont = dtype(freedrawfont, sizeof(DFont), fontmap, sizeof(fontmap)); TImage = dtype(freedrawimage, sizeof(DImage), imagemap, sizeof(imagemap)); TScreen = dtype(freedrawscreen, sizeof(DScreen), screenmap, sizeof(screenmap)); TDisplay = dtype(freedrawdisplay, sizeof(DDisplay), displaymap, sizeof(displaymap)); builtinmod("$Draw", Drawmodtab, Drawmodlen); } static int drawhash(char *s) { int h; h = 0; while(*s){ h += *s++; h <<= 1; if(h & (1<<8)) h |= 1; } return (h&0xFFFF)%BIHASH; } static Cache* cachelookup(Cache *cache[], Display *d, char *name) { Cache *c; libqlock(cacheqlock); c = cache[drawhash(name)]; while(c!=nil && (d!=c->display || strcmp(name, c->name)!=0)) c = c->next; libqunlock(cacheqlock); return c; } Cache* cacheinstall(Cache **cache, Display *d, char *name, void *ptr, char *type) { Cache *c; int hash; USED(type); c = cachelookup(cache, d, name); if(c){ /* print("%s %s already in cache\n", type, name); /**/ return nil; } c = malloc(sizeof(Cache)); if(c == nil) return nil; hash = drawhash(name); c->ref = 0; /* will be incremented by caller */ c->display = d; c->name = strdup(name); c->u.ptr = ptr; libqlock(cacheqlock); c->next = cache[hash]; cache[hash] = c; libqunlock(cacheqlock); return c; } void cacheuninstall(Cache **cache, Display *d, char *name, char *type) { Cache *c, *prev; int hash; hash = drawhash(name); libqlock(cacheqlock); c = cache[hash]; if(c == nil){ Notfound: libqunlock(cacheqlock); print("%s not in %s cache\n", name, type); return; } prev = nil; while(c!=nil && (d!=c->display || strcmp(name, c->name)!=0)){ prev = c; c = c->next; } if(c == nil) goto Notfound; if(prev == 0) cache[hash] = c->next; else prev->next = c->next; libqunlock(cacheqlock); free(c->name); free(c); } Image* lookupimage(Draw_Image *di) { Display *disp; Image *i; int locked; if(di == H || D2H(di)->t != TImage) return nil; i = ((DImage*)di)->image; if(i == nil) return nil; if(!eqrect(IRECT(di->clipr), i->clipr) || di->repl!=i->repl){ disp = i->display; locked = lockdisplay(disp); replclipr(i, di->repl, IRECT(di->clipr)); if(locked) unlockdisplay(disp); } return i; } Screen* lookupscreen(Draw_Screen *ds) { if(ds == H || D2H(ds)->t != TScreen) return nil; return ((DScreen*)ds)->screen; } Font* lookupfont(Draw_Font *df) { if(df == H || D2H(df)->t != TFont) return nil; return ((DFont*)df)->font; } Display* lookupdisplay(Draw_Display *dd) { if(dd == H || D2H(dd)->t != TDisplay) return nil; return ((DDisplay*)dd)->display; } Image* checkimage(Draw_Image *di) { Image *i; if(di == H) error("nil Image"); i = lookupimage(di); if(i == nil) error(exType); return i; } Screen* checkscreen(Draw_Screen *ds) { Screen *s; if(ds == H) error("nil Screen"); s = lookupscreen(ds); if(s == nil) error(exType); return s; } Font* checkfont(Draw_Font *df) { Font *f; if(df == H) error("nil Font"); f = lookupfont(df); if(f == nil) error(exType); return f; } Display* checkdisplay(Draw_Display *dd) { Display *d; if(dd == H) error("nil Display"); d = lookupdisplay(dd); if(d == nil) error(exType); return d; } void Display_allocate(void *fp) { F_Display_allocate *f; char buf[128], *dev; Subfont *df; Display *display; DDisplay *dd; Heap *h; Draw_Rect r; DRef *dr; Cache *c; f = fp; destroy(*f->ret); *f->ret = H; if(cacheqlock == nil){ cacheqlock = libqlalloc(); if(cacheqlock == nil) return; } dev = string2c(f->dev); if(dev[0] == 0) dev = 0; display = initdisplay(dev, dev, nil); /* TO DO: win, error */ if(display == 0) return; dr = malloc(sizeof(DRef)); if(dr == nil) return; h = heap(TDisplay); if(h == H){ closedisplay(display); return; } dd = H2D(DDisplay*, h); dd->display = display; *f->ret = &dd->drawdisplay; dd->dref = dr; display->limbo = dr; dr->display = display; dr->ref = 1; df = getdefont(display); if(df){ display->defaultsubfont = df; sprint(buf, "%d %d\n0 %d\t%s\n", df->height, df->ascent, df->n-1, deffontname); display->defaultfont = buildfont(display, buf, deffontname); if(display->defaultfont){ c = cacheinstall(fcache, display, deffontname, display->defaultfont, "font"); if(c) c->ref++; /* else BUG? */ } } R2R(r, display->image->r); dd->drawdisplay.image = allocdrawimage(dd, r, display->image->chan, display->image, 0, 0); R2R(r, display->white->r); dd->drawdisplay.black = allocdrawimage(dd, r, display->black->chan, display->black, 1, 0); dd->drawdisplay.white = allocdrawimage(dd, r, display->white->chan, display->white, 1, 0); dd->drawdisplay.opaque = allocdrawimage(dd, r, display->opaque->chan, display->opaque, 1, 0); dd->drawdisplay.transparent = allocdrawimage(dd, r, display->transparent->chan, display->transparent, 1, 0); /* don't call unlockdisplay because the qlock was left up by initdisplay */ libqunlock(display->qlock); } void Display_getwindow(void *fp) { F_Display_getwindow *f; Display *disp; int locked; Image *image; Screen *screen; char *wn; void *r; f = fp; r = f->ret->t0; f->ret->t0 = H; destroy(r); r = f->ret->t1; f->ret->t1 = H; destroy(r); disp = checkdisplay(f->d); if(f->winname == H) wn = "/dev/winname"; else wn = string2c(f->winname); if(f->image == H) image = nil; else image = checkimage(f->image); if(f->screen == H) screen = nil; else screen = checkscreen(f->screen); locked = lockdisplay(disp); if(gengetwindow(disp, wn, &image, &screen, f->backup) < 0){ /* TO DO: eliminate f->image and f->screen's references to Image and Screen */ goto Return; } if(screen != nil){ if(f->screen != H){ f->ret->t0 = f->screen; D2H(f->screen)->ref++; }else f->ret->t0 = mkdrawscreen(screen, f->d); } if(image != nil){ if(f->image != H){ f->ret->t1 = f->image; D2H(f->image)->ref++; }else f->ret->t1 = mkdrawimage(image, f->ret->t0, f->d, nil); } Return: if(locked) unlockdisplay(disp); } void Display_startrefresh(void *fp) { F_Display_startrefresh *f; Display *disp; f = fp; disp = checkdisplay(f->d); refreshslave(disp); } void display_dec(void *v) { DRef *dr; Display *d; int locked; dr = v; if(dr->ref-- != 1) return; d = dr->display; locked = lockdisplay(d); font_close(d->defaultfont); subfont_close(d->defaultsubfont); if(locked) unlockdisplay(d); freeallsubfonts(d); closedisplay(d); free(dr); } void freedrawdisplay(Heap *h, int swept) { DDisplay *dd; Display *d; dd = H2D(DDisplay*, h); if(!swept) { destroy(dd->drawdisplay.image); destroy(dd->drawdisplay.black); destroy(dd->drawdisplay.white); destroy(dd->drawdisplay.opaque); destroy(dd->drawdisplay.transparent); } /* we've now released dd->image etc.; make sure they're not freed again */ d = dd->display; d->image = nil; d->white = nil; d->black = nil; d->opaque = nil; d->transparent = nil; display_dec(dd->dref); /* Draw_Display header will be freed by caller */ } void Display_color(void *fp) { F_Display_color *f; Display *d; int locked; f = fp; destroy(*f->ret); *f->ret = H; d = checkdisplay(f->d); locked = lockdisplay(d); *f->ret = color((DDisplay*)f->d, f->color); if(locked) unlockdisplay(d); } void Image_flush(void *fp) { F_Image_flush *f; Image *d; DImage *di; int locked; f = fp; d = checkimage(f->win); di = (DImage*)f->win; switch(f->func){ case 0: /* Draw->Flushoff */ di->flush = 0; break; case 1: /* Draw->Flushon */ di->flush = 1; /* fall through */ case 2: /* Draw->Flushnow */ locked = lockdisplay(d->display); if(d->id==0 || d->screen!=0) flushimage(d->display, 1); if(locked) unlockdisplay(d->display); break; default: error(exInval); } } void checkflush(Draw_Image *dst) { DImage *di; di = (DImage*)dst; if(di->flush && (di->image->id==0 || di->image->screen!=nil)) flushimage(di->image->display, 1); } static void imagedraw(void *fp, int op) { F_Image_draw *f; Image *d, *s, *m; int locked; f = fp; d = checkimage(f->dst); if(f->src == H){ s = d->display->black; }else{ s = checkimage(f->src); } if(f->matte == H) m = d->display->white; /* ones */ else m = checkimage(f->matte); if(d->display!=s->display || d->display!=m->display){ DP("imagedraw d->display!=s->display || d->display!=m->display\n"); return; } locked = lockdisplay(d->display); drawop(d, IRECT(f->r), s, m, IPOINT(f->p), op); checkflush(f->dst); if(locked) unlockdisplay(d->display); } void Image_draw(void *fp) { imagedraw(fp, SoverD); } void Image_drawop(void *fp) { F_Image_drawop *f; f = fp; imagedraw(fp, f->op); } static void imagegendraw(void *fp, int op) { F_Image_gendraw *f; Image *d, *s, *m; int locked; f = fp; d = checkimage(f->dst); if(f->src == H) s = d->display->black; else s = checkimage(f->src); if(f->matte == H) m = d->display->white; /* ones */ else m = checkimage(f->matte); if(d->display!=s->display || d->display!=m->display) return; locked = lockdisplay(d->display); gendrawop(d, IRECT(f->r), s, IPOINT(f->p0), m, IPOINT(f->p1), op); checkflush(f->dst); if(locked) unlockdisplay(d->display); } void Image_gendraw(void *fp) { imagegendraw(fp, SoverD); } void Image_gendrawop(void *fp) { F_Image_gendrawop *f; f = fp; imagegendraw(fp, f->op); } static void drawline(void *fp, int op) { F_Image_line *f; Image *d, *s; int locked; f = fp; d = checkimage(f->dst); s = checkimage(f->src); if(d->display != s->display || f->radius < 0) return; locked = lockdisplay(d->display); lineop(d, IPOINT(f->p0), IPOINT(f->p1), f->end0, f->end1, f->radius, s, IPOINT(f->sp), op); checkflush(f->dst); if(locked) unlockdisplay(d->display); } void Image_line(void *fp) { drawline(fp, SoverD); } void Image_lineop(void *fp) { F_Image_lineop *f; f = fp; drawline(fp, f->op); } static void drawsplinepoly(void *fp, int smooth, int op) { F_Image_poly *f; Image *d, *s; int locked; f = fp; d = checkimage(f->dst); s = checkimage(f->src); if(d->display != s->display|| f->radius < 0) return; locked = lockdisplay(d->display); /* sleazy: we know that Draw_Points have same shape as Points */ if(smooth) bezsplineop(d, (Point*)f->p->data, f->p->len, f->end0, f->end1, f->radius, s, IPOINT(f->sp), op); else polyop(d, (Point*)f->p->data, f->p->len, f->end0, f->end1, f->radius, s, IPOINT(f->sp), op); checkflush(f->dst); if(locked) unlockdisplay(d->display); } void Image_poly(void *fp) { drawsplinepoly(fp, 0, SoverD); } void Image_polyop(void *fp) { F_Image_polyop *f; f = fp; drawsplinepoly(fp, 0, f->op); } void Image_bezspline(void *fp) { drawsplinepoly(fp, 1, SoverD); } void Image_bezsplineop(void *fp) { F_Image_bezsplineop *f; f = fp; drawsplinepoly(fp, 1, f->op); } static void drawbezier(void *fp, int op) { F_Image_bezier *f; Image *d, *s; int locked; f = fp; d = checkimage(f->dst); s = checkimage(f->src); if(d->display != s->display || f->radius < 0) return; locked = lockdisplay(d->display); bezierop(d, IPOINT(f->a), IPOINT(f->b), IPOINT(f->c), IPOINT(f->d), f->end0, f->end1, f->radius, s, IPOINT(f->sp), op); checkflush(f->dst); if(locked) unlockdisplay(d->display); } void Image_bezier(void *fp) { drawbezier(fp, SoverD); } void Image_bezierop(void *fp) { F_Image_bezierop *f; f = fp; drawbezier(fp, f->op); } static void drawfillbezier(void *fp, int op) { F_Image_fillbezier *f; Image *d, *s; int locked; f = fp; d = checkimage(f->dst); s = checkimage(f->src); if(d->display != s->display) return; locked = lockdisplay(d->display); fillbezierop(d, IPOINT(f->a), IPOINT(f->b), IPOINT(f->c), IPOINT(f->d), f->wind, s, IPOINT(f->sp), op); checkflush(f->dst); if(locked) unlockdisplay(d->display); } void Image_fillbezier(void *fp) { drawfillbezier(fp, SoverD); } void Image_fillbezierop(void *fp) { F_Image_fillbezierop *f; f = fp; drawfillbezier(fp, f->op); } static void drawfillsplinepoly(void *fp, int smooth, int op) { F_Image_fillpoly *f; Image *d, *s; int locked; f = fp; d = checkimage(f->dst); s = checkimage(f->src); if(d->display != s->display) return; locked = lockdisplay(d->display); /* sleazy: we know that Draw_Points have same shape as Points */ if(smooth) fillbezsplineop(d, (Point*)f->p->data, f->p->len, f->wind, s, IPOINT(f->sp), op); else fillpolyop(d, (Point*)f->p->data, f->p->len, f->wind, s, IPOINT(f->sp), op); checkflush(f->dst); if(locked) unlockdisplay(d->display); } void Image_fillpoly(void *fp) { drawfillsplinepoly(fp, 0, SoverD); } void Image_fillpolyop(void *fp) { F_Image_fillpolyop *f; f = fp; drawfillsplinepoly(fp, 0, f->op); } void Image_fillbezspline(void *fp) { drawfillsplinepoly(fp, 1, SoverD); } void Image_fillbezsplineop(void *fp) { F_Image_fillbezsplineop *f; f = fp; drawfillsplinepoly(fp, 1, f->op); } static void drawarcellipse(void *fp, int isarc, int alpha, int phi, int op) { F_Image_arc *f; Image *d, *s; int locked; f = fp; d = checkimage(f->dst); s = checkimage(f->src); if(d->display != s->display || f->thick < 0 || f->a<0 || f->b<0) return; locked = lockdisplay(d->display); if(isarc) arcop(d, IPOINT(f->c), f->a, f->b, f->thick, s, IPOINT(f->sp), alpha, phi, op); else ellipseop(d, IPOINT(f->c), f->a, f->b, f->thick, s, IPOINT(f->sp), op); checkflush(f->dst); if(locked) unlockdisplay(d->display); } void Image_ellipse(void *fp) { drawarcellipse(fp, 0, 0, 0, SoverD); } void Image_ellipseop(void *fp) { F_Image_ellipseop *f; f = fp; drawarcellipse(fp, 0, 0, 0, f->op); } void Image_arc(void *fp) { F_Image_arc *f; f = fp; drawarcellipse(fp, 1, f->alpha, f->phi, SoverD); } void Image_arcop(void *fp) { F_Image_arcop *f; f = fp; drawarcellipse(fp, 1, f->alpha, f->phi, f->op); } static void drawfillarcellipse(void *fp, int isarc, int alpha, int phi, int op) { F_Image_fillarc *f; Image *d, *s; int locked; f = fp; d = checkimage(f->dst); s = checkimage(f->src); if(d->display != s->display || f->a<0 || f->b<0) return; locked = lockdisplay(d->display); if(isarc) fillarcop(d, IPOINT(f->c), f->a, f->b, s, IPOINT(f->sp), alpha, phi, op); else fillellipseop(d, IPOINT(f->c), f->a, f->b, s, IPOINT(f->sp), op); checkflush(f->dst); if(locked) unlockdisplay(d->display); } void Image_fillellipse(void *fp) { drawfillarcellipse(fp, 0, 0, 0, SoverD); } void Image_fillellipseop(void *fp) { F_Image_fillellipseop *f; f = fp; drawfillarcellipse(fp, 0, 0, 0, f->op); } void Image_fillarc(void *fp) { F_Image_fillarc *f; f = fp; drawfillarcellipse(fp, 1, f->alpha, f->phi, SoverD); } void Image_fillarcop(void *fp) { F_Image_fillarcop *f; f = fp; drawfillarcellipse(fp, 1, f->alpha, f->phi, f->op); } static void drawtext(void *fp, int op) { F_Image_text *f; Font *font; Point pt; Image *s, *d; String *str; int locked; f = fp; if(f->dst == H || f->src == H) goto Return; if(f->font == H || f->str == H) goto Return; str = f->str; d = checkimage(f->dst); s = checkimage(f->src); font = checkfont(f->font); if(d->display!=s->display || d->display!=font->display) return; locked = lockdisplay(d->display); if(str->len >= 0) pt = stringnop(d, IPOINT(f->p), s, IPOINT(f->sp), font, str->Sascii, str->len, op); else pt = runestringnop(d, IPOINT(f->p), s, IPOINT(f->sp), font, str->Srune, -str->len, op); checkflush(f->dst); if(locked) unlockdisplay(d->display); Return: P2P(*f->ret, pt); } void Image_text(void *fp) { drawtext(fp, SoverD); } void Image_textop(void *fp) { F_Image_textop *f; f = fp; drawtext(fp, f->op); } static void drawtextbg(void *fp, int op) { F_Image_textbg *f; Font *font; Point pt; Image *s, *d, *bg; String *str; int locked; f = fp; if(f->dst == H || f->src == H) goto Return; if(f->font == H || f->str == H) goto Return; str = f->str; d = checkimage(f->dst); s = checkimage(f->src); bg = checkimage(f->bg); font = checkfont(f->font); if(d->display!=s->display || d->display!=font->display) return; locked = lockdisplay(d->display); if(str->len >= 0) pt = stringnbgop(d, IPOINT(f->p), s, IPOINT(f->sp), font, str->Sascii, str->len, bg, IPOINT(f->bgp), op); else pt = runestringnbgop(d, IPOINT(f->p), s, IPOINT(f->sp), font, str->Srune, -str->len, bg, IPOINT(f->bgp), op); checkflush(f->dst); if(locked) unlockdisplay(d->display); Return: P2P(*f->ret, pt); } void Image_textbg(void *fp) { drawtextbg(fp, SoverD); } void Image_textbgop(void *fp) { F_Image_textbgop *f; f = fp; drawtextbg(fp, f->op); } static void drawborder(void *fp, int op) { F_Image_border *f; Image *d, *s; int locked; f = fp; d = checkimage(f->dst); s = checkimage(f->src); if(d->display != s->display) return; locked = lockdisplay(d->display); borderop(d, IRECT(f->r), f->i, s, IPOINT(f->sp), op); checkflush(f->dst); if(locked) unlockdisplay(d->display); } void Image_border(void *fp) { drawborder(fp, SoverD); } void Display_newimage(void *fp) { F_Display_newimage *f; Display *d; int locked; f = fp; d = checkdisplay(f->d); destroy(*f->ret); *f->ret = H; locked = lockdisplay(d); *f->ret = allocdrawimage((DDisplay*)f->d, f->r, f->chans.desc, nil, f->repl, f->color); if(locked) unlockdisplay(d); } void Display_colormix(void *fp) { F_Display_colormix *f; Display *disp; Image *i; int locked; f = fp; destroy(*f->ret); *f->ret = H; disp = checkdisplay(f->d); locked = lockdisplay(disp); i = allocimagemix(disp, f->c1, f->c2); if(locked) unlockdisplay(disp); *f->ret = mkdrawimage(i, H, f->d, nil); } void Image_readpixels(void *fp) { F_Image_readpixels *f; Rectangle r; Image *i; int locked; f = fp; R2R(r, f->r); i = checkimage(f->src); locked = lockdisplay(i->display); *f->ret = unloadimage(i, r, f->data->data, f->data->len); if(locked) unlockdisplay(i->display); } void Image_writepixels(void *fp) { Rectangle r; F_Image_writepixels *f; Image *i; int locked; f = fp; R2R(r, f->r); i = checkimage(f->dst); locked = lockdisplay(i->display); *f->ret = loadimage(i, r, f->data->data, f->data->len); checkflush(f->dst); if(locked) unlockdisplay(i->display); } void Image_arrow(void *fp) { F_Image_arrow *f; f = fp; *f->ret = ARROW(f->a, f->b, f->c); } void Image_name(void *fp) { F_Image_name *f; Image *i; int locked, ok; char *name; f = fp; *f->ret = -1; i = checkimage(f->src); name = string2c(f->name); locked = lockdisplay(i->display); *f->ret = ok = nameimage(i, name, f->in); if(locked) unlockdisplay(i->display); if(ok){ destroy(f->src->iname); if(f->in){ f->src->iname = f->name; D2H(f->name)->ref++; }else f->src->iname = H; } } Image* display_open(Display *disp, char *name) { Image *i; int fd; fd = libopen(name, OREAD); if(fd < 0) return nil; i = readimage(disp, fd, 1); libclose(fd); return i; } void Display_open(void *fp) { Image *i; Display *disp; F_Display_open *f; f = fp; destroy(*f->ret); *f->ret = H; disp = lookupdisplay(f->d); if(disp == nil) return; i = display_open(disp, string2c(f->name)); if(i == nil) return; *f->ret = allocdrawimage((DDisplay*)f->d, DRECT(i->r), i->chan, i, 0, 0); } void Display_namedimage(void *fp) { F_Display_namedimage *f; Display *d; Image *i; Draw_Image *di; int locked; f = fp; destroy(*f->ret); *f->ret = H; d = checkdisplay(f->d); locked = lockdisplay(d); i = namedimage(d, string2c(f->name)); if(locked) unlockdisplay(d); if(i == nil) return; di = allocdrawimage((DDisplay*)f->d, DRECT(i->r), i->chan, i, i->repl, 0); *f->ret = di; if(di == H){ locked = lockdisplay(d); freeimage(i); if(locked) unlockdisplay(d); }else{ di->iname = f->name; D2H(f->name)->ref++; } } void Display_readimage(void *fp) { Image *i; Display *disp; F_Display_readimage *f; Sys_FD *fd; int locked; f = fp; destroy(*f->ret); *f->ret = H; fd = f->fd; if(fd == H) return; disp = checkdisplay(f->d); i = readimage(disp, fd->fd, 1); if(i == nil) return; *f->ret = allocdrawimage((DDisplay*)f->d, DRECT(i->r), i->chan, i, 0, 0); if(*f->ret == H){ locked = lockdisplay(disp); freeimage(i); if(locked) unlockdisplay(disp); } } void Display_writeimage(void *fp) { Image *i; F_Display_writeimage *f; Sys_FD *fd; f = fp; *f->ret = -1; fd = f->fd; if(fd == H) return; i = checkimage(f->i); if(checkdisplay(f->d) != i->display) return; *f->ret = writeimage(fd->fd, i, 1); /* TO DO: dolock? */ } Draw_Screen* mkdrawscreen(Screen *s, Draw_Display *display) { Heap *h; DScreen *ds; Draw_Image *dimage, *dfill; dimage = mkdrawimage(s->image, H, display, nil); dfill = mkdrawimage(s->fill, H, display, nil); h = heap(TScreen); if(h == H) return nil; ds = H2D(DScreen*, h); ds->screen = s; ds->drawscreen.fill = dfill; D2H(dfill)->ref++; ds->drawscreen.image = dimage; D2H(dimage)->ref++; ds->drawscreen.display = dimage->display; D2H(dimage->display)->ref++; ds->drawscreen.id = s->id; ds->dref = s->display->limbo; ds->dref->ref++; return &ds->drawscreen; } static DScreen* allocdrawscreen(Draw_Image *dimage, Draw_Image *dfill, int public) { Heap *h; Screen *s; DScreen *ds; Image *image, *fill; image = ((DImage*)dimage)->image; fill = ((DImage*)dfill)->image; s = allocscreen(image, fill, public); if(s == 0) return nil; h = heap(TScreen); if(h == H) return nil; ds = H2D(DScreen*, h); ds->screen = s; ds->drawscreen.fill = dfill; D2H(dfill)->ref++; ds->drawscreen.image = dimage; D2H(dimage)->ref++; ds->drawscreen.display = dimage->display; D2H(dimage->display)->ref++; ds->drawscreen.id = s->id; ds->dref = image->display->limbo; ds->dref->ref++; return ds; } void Screen_allocate(void *fp) { F_Screen_allocate *f; DScreen *ds; Image *image; int locked; f = fp; destroy(*f->ret); *f->ret = H; image = checkimage(f->image); checkimage(f->fill); locked = lockdisplay(image->display); ds = allocdrawscreen(f->image, f->fill, f->public); if(ds != nil) *f->ret = &ds->drawscreen; if(locked) unlockdisplay(image->display); } void Display_publicscreen(void *fp) { F_Display_publicscreen *f; Heap *h; Screen *s; DScreen *ds; Display *disp; int locked; f = fp; destroy(*f->ret); *f->ret = H; disp = checkdisplay(f->d); locked = lockdisplay(disp); s = publicscreen(disp, f->id, disp->image->chan); if(locked) unlockdisplay(disp); if(s == nil) return; h = heap(TScreen); if(h == H) return; ds = H2D(DScreen*, h); ds->screen = s; ds->drawscreen.fill = H; ds->drawscreen.image =H; ds->drawscreen.id = s->id; ds->drawscreen.display = f->d; D2H(f->d)->ref++; ds->dref = disp->limbo; ds->dref->ref++; *f->ret = &ds->drawscreen; } void freedrawscreen(Heap *h, int swept) { DScreen *ds; Screen *s; Display *disp; int locked; ds = H2D(DScreen*, h); if(!swept) { destroy(ds->drawscreen.image); destroy(ds->drawscreen.fill); destroy(ds->drawscreen.display); } s = lookupscreen(&ds->drawscreen); if(s == nil){ if(!swept) freeptrs(ds, TScreen); return; } disp = s->display; locked = lockdisplay(disp); freescreen(s); if(locked) unlockdisplay(disp); display_dec(ds->dref); /* screen header will be freed by caller */ } void Font_build(void *fp) { F_Font_build *f; Font *font; DFont *dfont; Heap *h; char buf[128]; char *name, *data; Subfont *df; Display *disp; int locked; f = fp; destroy(*f->ret); *f->ret = H; disp = checkdisplay(f->d); name = string2c(f->name); font = font_open(disp, name); if(font == nil) { if(strcmp(name, deffontname) == 0) { df = disp->defaultsubfont; sprint(buf, "%d %d\n0 %d\t%s\n", df->height, df->ascent, df->n-1, name); data = buf; } else if(f->desc == H) return; else data = string2c(f->desc); locked = lockdisplay(disp); font = buildfont(disp, data, name); if(locked) unlockdisplay(disp); if(font == nil) return; } h = heap(TFont); if(h == H) return; dfont = H2D(DFont*, h); dfont->font = font; dfont->drawfont.name = f->name; D2H(f->name)->ref++; dfont->drawfont.height = font->height; dfont->drawfont.ascent = font->ascent; dfont->drawfont.display = f->d; D2H(f->d)->ref++; dfont->dref = disp->limbo; dfont->dref->ref++; *f->ret = &dfont->drawfont; } Font* font_open(Display *display, char *name) { Cache *c; Font *font; int locked; c = cachelookup(fcache, display, name); if(c) font = c->u.f; else { locked = lockdisplay(display); font = openfont(display, name); if(locked) unlockdisplay(display); if(font == nil) return nil; c = cacheinstall(fcache, display, name, font, "font"); } if(c) c->ref++; return font; } void font_close(Font *f) { Cache *c; Display *disp; int locked; disp = f->display; if(f->name == nil) return; /* fonts from Font_build() aren't always in fcache, but we still need to free them */ c = cachelookup(fcache, disp, f->name); if(c != nil && f == c->u.f) { if(c->ref <= 0) return; if(c->ref-- != 1) return; cacheuninstall(fcache, disp, f->name, "font"); } locked = lockdisplay(disp); freefont(f); if(locked) unlockdisplay(disp); } void freecachedsubfont(Subfont *sf) { Cache *c; Display *disp; disp = sf->bits->display; c = cachelookup(sfcache, disp, sf->name); if(c == nil){ fprint(2, "subfont %s not cached\n", sf->name); return; } if(c->ref > 0) c->ref--; /* if ref is zero, we leave it around for later harvesting by freeallsubfonts */ } void freeallsubfonts(Display *d) { int i; Cache *c, *prev, *o; Subfont *sf; int locked; if(cacheqlock == nil) /* may not have allocated anything yet */ return; libqlock(cacheqlock); for(i=0; iref==0 && (d==nil || c->display==d)){ if(prev == 0) sfcache[i] = c->next; else prev->next = c->next; free(c->name); sf = c->u.sf; if(--sf->ref==0){ free(sf->info); locked = lockdisplay(c->display); freeimage(sf->bits); if(locked) unlockdisplay(c->display); free(sf); } o = c; c = c->next; free(o); }else{ prev = c; c = c->next; } } } libqunlock(cacheqlock); } void subfont_close(Subfont *sf) { freecachedsubfont(sf); } void freesubfont(Subfont *sf) { freecachedsubfont(sf); } void Font_open(void *fp) { Heap *h; Font *font; Display *disp; DFont *df; F_Font_open *f; f = fp; destroy(*f->ret); *f->ret = H; disp = checkdisplay(f->d); font = font_open(disp, string2c(f->name)); if(font == 0) return; h = heap(TFont); if(h == H) return; df = H2D(DFont*, h); df->font = font; df->drawfont.name = f->name; D2H(f->name)->ref++; df->drawfont.height = font->height; df->drawfont.ascent = font->ascent; df->drawfont.display = f->d; D2H(f->d)->ref++; df->dref = disp->limbo; df->dref->ref++; *f->ret = &df->drawfont; } void Font_width(void *fp) { F_Font_width *f; Font *font; char *s; int locked; f = fp; s = string2c(f->str); if(f->f == H || s[0]=='\0') *f->ret = 0; else{ font = checkfont(f->f); locked = lockdisplay(font->display); *f->ret = stringwidth(font, s); if(locked) unlockdisplay(font->display); } } void Font_bbox(void *fp) { F_Font_bbox *f; Draw_Rect *ret; /* place holder for the real thing */ f = fp; ret = f->ret; ret->min.x = ret->min.y = 0; ret->max.x = ret->max.y = 0; } /* * BUG: would be nice if this cached the whole font. * Instead only the subfonts are cached and the fonts are * freed when released. */ void freedrawfont(Heap*h, int swept) { Draw_Font *d; Font *f; d = H2D(Draw_Font*, h); f = lookupfont(d); if(!swept) { destroy(d->name); destroy(d->display); } font_close(f); display_dec(((DFont*)d)->dref); } void Chans_text(void *fp) { F_Chans_text *f; char buf[16]; f = fp; destroy(*f->ret); *f->ret = H; if(chantostr(buf, f->c.desc) != nil) retstr(buf, f->ret); } void Chans_depth(void *fp) { F_Chans_depth *f; f = fp; *f->ret = chantodepth(f->c.desc); } void Chans_eq(void *fp) { F_Chans_eq *f; f = fp; *f->ret = f->c.desc == f->d.desc; } void Chans_mk(void *fp) { F_Chans_mk *f; f = fp; f->ret->desc = strtochan(string2c(f->s)); } void Display_rgb(void *fp) { ulong c; Display *disp; F_Display_rgb *f; int locked; void *r; f = fp; r = *f->ret; *f->ret = H; destroy(r); disp = checkdisplay(f->d); c = ((f->r&255)<<24)|((f->g&255)<<16)|((f->b&255)<<8)|0xFF; locked = lockdisplay(disp); *f->ret = color((DDisplay*)f->d, c); if(locked) unlockdisplay(disp); } void Display_rgb2cmap(void *fp) { F_Display_rgb2cmap *f; f = fp; /* f->display is unused, but someday may have color map */ *f->ret = rgb2cmap(f->r, f->g, f->b); } void Display_cmap2rgb(void *fp) { F_Display_cmap2rgb *f; ulong c; f = fp; /* f->display is unused, but someday may have color map */ c = cmap2rgb(f->c); f->ret->t0 = (c>>16)&0xFF; f->ret->t1 = (c>>8)&0xFF; f->ret->t2 = (c>>0)&0xFF; } void Display_cmap2rgba(void *fp) { F_Display_cmap2rgba *f; f = fp; /* f->display is unused, but someday may have color map */ *f->ret = cmap2rgba(f->c); } void Draw_setalpha(void *fp) { F_Draw_setalpha *f; f = fp; *f->ret = setalpha(f->c, f->a); } void Draw_icossin(void *fp) { F_Draw_icossin *f; s32 s, c; f = fp; icossin(f->deg, &s, &c); f->ret->t0 = s; f->ret->t1 = c; } void Draw_icossin2(void *fp) { F_Draw_icossin2 *f; s32 s, c; f = fp; icossin2(f->p.x, f->p.y, &s, &c); f->ret->t0 = s; f->ret->t1 = c; } void Draw_bytesperline(void *fp) { F_Draw_bytesperline *f; f = fp; *f->ret = bytesperline(IRECT(f->r), f->d); } Draw_Image* color(DDisplay *dd, ulong color) { int c; Draw_Rect r; r.min.x = 0; r.min.y = 0; r.max.x = 1; r.max.y = 1; c = (color&0xff) == 0xff ? RGB24: RGBA32; return allocdrawimage(dd, r, c, nil, 1, color); } Draw_Image* mkdrawimage(Image *i, Draw_Screen *screen, Draw_Display *display, void *ref) { Heap *h; DImage *di; h = heap(TImage); if(h == H) return H; di = H2D(DImage*, h); di->image = i; di->drawimage.screen = screen; if(screen != H) D2H(screen)->ref++; di->drawimage.display = display; if(display != H) D2H(display)->ref++; di->refreshptr = ref; R2R(di->drawimage.r, i->r); R2R(di->drawimage.clipr, i->clipr); di->drawimage.chans.desc = i->chan; di->drawimage.depth = i->depth; di->drawimage.repl = i->repl; di->flush = 1; di->dref = i->display->limbo; di->dref->ref++; return &di->drawimage; } void Screen_newwindow(void *fp) { F_Screen_newwindow *f; Image *i; Screen *s; Rectangle r; int locked; void *v; f = fp; s = checkscreen(f->screen); R2R(r, f->r); if(f->backing != Refnone && f->backing != Refbackup) f->backing = Refbackup; v = *f->ret; *f->ret = H; destroy(v); locked = lockdisplay(s->display); i = allocwindow(s, r, f->backing, f->color); if(locked) unlockdisplay(s->display); if(i == nil) return; *f->ret = mkdrawimage(i, f->screen, f->screen->display, 0); } static void screentopbot(Draw_Screen *screen, Array *array, void (*topbot)(Image **, int)) { Screen *s; Draw_Image **di; Image **ip; int i, n, locked; s = checkscreen(screen); di = (Draw_Image**)array->data; ip = malloc(array->len * sizeof(Image*)); if(ip == nil) return; n = 0; for(i=0; ilen; i++) if(di[i] != H){ ip[n] = lookupimage(di[i]); if(ip[n]==nil || ip[n]->screen != s){ free(ip); return; } n++; } if(n == 0){ free(ip); return; } locked = lockdisplay(s->display); (*topbot)(ip, n); free(ip); flushimage(s->display, 1); if(locked) unlockdisplay(s->display); } void Screen_top(void *fp) { F_Screen_top *f; f = fp; screentopbot(f->screen, f->wins, topnwindows); } void Screen_bottom(void *fp) { F_Screen_top *f; f = fp; screentopbot(f->screen, f->wins, bottomnwindows); } void freedrawimage(Heap *h, int swept) { Image *i; int locked; Display *disp; Draw_Image *d; d = H2D(Draw_Image*, h); i = lookupimage(d); if(i == nil) { if(!swept) freeptrs(d, TImage); return; } disp = i->display; locked = lockdisplay(disp); freeimage(i); if(locked) unlockdisplay(disp); display_dec(((DImage*)d)->dref); /* image/layer header will be freed by caller */ } void Image_top(void *fp) { F_Image_top *f; Image *i; int locked; f = fp; i = checkimage(f->win); locked = lockdisplay(i->display); topwindow(i); flushimage(i->display, 1); if(locked) unlockdisplay(i->display); } void Image_origin(void *fp) { F_Image_origin *f; Image *i; int locked; f = fp; i = checkimage(f->win); locked = lockdisplay(i->display); if(originwindow(i, IPOINT(f->log), IPOINT(f->scr)) < 0) *f->ret = -1; else{ f->win->r = DRECT(i->r); f->win->clipr = DRECT(i->clipr); *f->ret = 1; } if(locked) unlockdisplay(i->display); } void Image_bottom(void *fp) { F_Image_top *f; Image *i; int locked; f = fp; i = checkimage(f->win); locked = lockdisplay(i->display); bottomwindow(i); flushimage(i->display, 1); if(locked) unlockdisplay(i->display); } Draw_Image* allocdrawimage(DDisplay *ddisplay, Draw_Rect r, ulong chan, Image *iimage, int repl, int color) { Heap *h; DImage *di; Rectangle rr; Image *image; image = iimage; if(iimage == nil){ R2R(rr, r); image = allocimage(ddisplay->display, rr, chan, repl, color); if(image == nil) return H; } h = heap(TImage); if(h == H){ if(iimage == nil) freeimage(image); return H; } di = H2D(DImage*, h); di->drawimage.r = r; R2R(di->drawimage.clipr, image->clipr); di->drawimage.chans.desc = chan; di->drawimage.depth = chantodepth(chan); di->drawimage.repl = repl; di->drawimage.display = (Draw_Display*)ddisplay; D2H(di->drawimage.display)->ref++; di->drawimage.screen = H; di->dref = ddisplay->display->limbo; di->dref->ref++; di->image = image; di->refreshptr = 0; di->flush = 1; return &di->drawimage; } /* * Entry points called from the draw library */ Subfont* lookupsubfont(Display *d, char *name) { Cache *c; c = cachelookup(sfcache, d, name); if(c == nil) return nil; /*c->u.sf->ref++;*/ /* TO DO: need to revisit the reference counting */ return c->u.sf; } void installsubfont(char *name, Subfont *subfont) { Cache *c; c = cacheinstall(sfcache, subfont->bits->display, name, subfont, "subfont"); if(c) c->ref++; } /* * BUG version */ char* subfontname(char *cfname, char *fname, int maxdepth) { char *t, *u, tmp1[256], tmp2[256]; int i, fd; if(strcmp(cfname, deffontname) == 0) return strdup(cfname); t = cfname; if(t[0] != '/'){ strcpy(tmp2, fname); u = utfrrune(tmp2, '/'); if(u) u[0] = 0; else strcpy(tmp2, "."); snprint(tmp1, sizeof tmp1, "%s/%s", tmp2, t); t = tmp1; } if(maxdepth > 8) maxdepth = 8; for(i=3; i>=0; i--){ if((1< maxdepth) continue; /* try i-bit grey */ snprint(tmp2, sizeof tmp2, "%s.%d", t, i); fd = libopen(tmp2, OREAD); if(fd >= 0){ libclose(fd); return strdup(tmp2); } } return strdup(t); } void refreshslave(Display *d) { int i, n, id; uchar buf[5*(5*4)], *p; Rectangle r; Image *im; int locked; for(;;){ release(); n = kchanio(d->refchan, buf, sizeof buf, OREAD); acquire(); if(n < 0) /* probably caused by closedisplay() closing refchan */ return; /* will fall off end of thread and close down */ locked = lockdisplay(d); p = buf; for(i=0; iwindows; im; im=im->next) if(im->id == id) break; if(im && im->screen && im->reffn) (*im->reffn)(im, r, im->refptr); } flushimage(d, 1); if(locked) unlockdisplay(d); } } void startrefresh(Display *disp) { USED(disp); } static int doflush(Display *d) { int m, n; char err[ERRMAX]; uchar *tp; n = d->bufp-d->buf; if(n <= 0) return 1; if(d->local == 0) release(); if((m = kchanio(d->datachan, d->buf, n, OWRITE)) != n){ if(d->local == 0) acquire(); kgerrstr(err, sizeof err); if(_drawdebug || strcmp(err, "screen id in use") != 0 && strcmp(err, exImage) != 0){ print("flushimage fail: (%d not %d) d=%zx: %s\nbuffer: ", m, n, (uintptr)d, err); for(tp = d->buf; tp < d->bufp; tp++) print("%.2x ", (int)*tp); print("\n"); } d->bufp = d->buf; /* might as well; chance of continuing */ return -1; } /* to debug what is being sent to memdraw() */ DP("doflush sending d->buf[0] = %c\n", d->buf[0]); for(int i = 1; i < n; i+=4){ DP("\td->buf[%d] = ", i); for(int j = 0; j < 4; j++){ DP(" %x", d->buf[i+j]); } DP("\n"); } d->bufp = d->buf; if(d->local == 0) acquire(); return 1; } int flushimage(Display *d, int visible) { int ret; Refreshq *r; for(;;){ if(visible) *d->bufp++ = 'v'; /* one byte always reserved for this */ ret = doflush(d); if(d->refhead == nil) break; while(r = d->refhead){ /* assign = */ d->refhead = r->next; if(d->refhead == nil) d->reftail = nil; r->reffn(nil, r->r, r->refptr); free(r); } } return ret; } /* * Turn off refresh for this window and remove any pending refresh events for it. */ void delrefresh(Image *i) { Refreshq *r, *prev, *next; int locked; Display *d; void *refptr; d = i->display; /* * Any refresh function will do, because the data pointer is nil. * Can't use nil, though, because that turns backing store back on. */ if(d->local) drawlsetrefresh(d->dataqid, i->id, memlnorefresh, nil); refptr = i->refptr; i->refptr = nil; if(d->refhead==nil || refptr==nil) return; locked = lockdisplay(d); prev = nil; for(r=d->refhead; r; r=next){ next = r->next; if(r->refptr == refptr){ if(prev) prev->next = next; else d->refhead = next; if(d->reftail == r) d->reftail = prev; free(r); }else prev = r; } if(locked) unlockdisplay(d); } void queuerefresh(Image *i, Rectangle r, Reffn reffn, void *refptr) { Display *d; Refreshq *rq; d = i->display; rq = malloc(sizeof(Refreshq)); if(rq == nil) return; if(d->reftail) d->reftail->next = rq; else d->refhead = rq; d->reftail = rq; rq->reffn = reffn; rq->refptr = refptr; rq->r = r; } uchar* bufimage(Display *d, int n) { uchar *p; if(n<0 || n>Displaybufsize){ kwerrstr("bad count in bufimage"); return 0; } if(d->bufp+n > d->buf+Displaybufsize){ if(d->local==0 && currun()!=libqlowner(d->qlock)) { print("bufimage: %zx %zx\n", (uintptr)libqlowner(d->qlock), (uintptr)currun()); abort(); } if(doflush(d) < 0) return 0; } p = d->bufp; d->bufp += n; /* return with buffer locked */ return p; } void drawerror(Display *d, char *s) { USED(d); fprint(2, "draw: %s: %r\n", s); }