diff options
author | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
---|---|---|
committer | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
commit | e5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch) | |
tree | d8d51eac403f07814b9e936eed0c9a79195e2450 /sys/src/libdraw/init.c |
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/libdraw/init.c')
-rwxr-xr-x | sys/src/libdraw/init.c | 468 |
1 files changed, 468 insertions, 0 deletions
diff --git a/sys/src/libdraw/init.c b/sys/src/libdraw/init.c new file mode 100755 index 000000000..c0ed2edbf --- /dev/null +++ b/sys/src/libdraw/init.c @@ -0,0 +1,468 @@ +#include <u.h> +#include <libc.h> +#include <draw.h> + +Display *display; +Font *font; +Image *screen; +int _drawdebug = 0; + +static char deffontname[] = "*default*"; +Screen *_screen; + +int debuglockdisplay = 0; + +static void _closedisplay(Display*, int); + +/* note handler */ +static void +drawshutdown(void) +{ + Display *d; + + d = display; + if(d){ + display = nil; + _closedisplay(d, 1); + } +} + +int +geninitdraw(char *devdir, void(*error)(Display*, char*), char *fontname, char *label, char *windir, int ref) +{ + int fd, n; + Subfont *df; + char buf[128]; + + display = initdisplay(devdir, windir, error); + if(display == nil) + return -1; + + /* + * Set up default font + */ + df = getdefont(display); + display->defaultsubfont = df; + if(df == nil){ + fprint(2, "imageinit: can't open default subfont: %r\n"); + Error: + closedisplay(display); + display = nil; + return -1; + } + if(fontname == nil){ + fd = open("/env/font", OREAD); + if(fd >= 0){ + n = read(fd, buf, sizeof(buf)); + if(n>0 && n<sizeof buf-1){ + buf[n] = 0; + fontname = buf; + } + close(fd); + } + } + /* + * Build fonts with caches==depth of screen, for speed. + * If conversion were faster, we'd use 0 and save memory. + */ + if(fontname == nil){ + snprint(buf, sizeof buf, "%d %d\n0 %d\t%s\n", df->height, df->ascent, + df->n-1, deffontname); +//BUG: Need something better for this installsubfont("*default*", df); + font = buildfont(display, buf, deffontname); + if(font == nil){ + fprint(2, "imageinit: can't open default font: %r\n"); + goto Error; + } + }else{ + font = openfont(display, fontname); /* BUG: grey fonts */ + if(font == nil){ + fprint(2, "imageinit: can't open font %s: %r\n", fontname); + goto Error; + } + } + display->defaultfont = font; + + /* + * Write label; ignore errors (we might not be running under rio) + */ + if(label){ + snprint(buf, sizeof buf, "%s/label", display->windir); + fd = open(buf, OREAD); + if(fd >= 0){ + read(fd, display->oldlabel, (sizeof display->oldlabel)-1); + close(fd); + fd = create(buf, OWRITE, 0666); + if(fd >= 0){ + write(fd, label, strlen(label)); + close(fd); + } + } + } + + snprint(buf, sizeof buf, "%s/winname", display->windir); + if(gengetwindow(display, buf, &screen, &_screen, ref) < 0) + goto Error; + + atexit(drawshutdown); + + return 1; +} + +int +initdraw(void(*error)(Display*, char*), char *fontname , char *label) +{ + char *dev = "/dev"; + + if(access("/dev/draw/new", AEXIST)<0 && bind("#i", "/dev", MAFTER)<0){ + fprint(2, "imageinit: can't bind /dev/draw: %r\n"); + return -1; + } + return geninitdraw(dev, error, fontname, label, dev, Refnone); +} + +/* + * Attach, or possibly reattach, to window. + * If reattaching, maintain value of screen pointer. + */ +int +gengetwindow(Display *d, char *winname, Image **winp, Screen **scrp, int ref) +{ + int n, fd; + char buf[64+1]; + Image *image; + Rectangle r; + + fd = open(winname, OREAD); + if(fd<0 || (n=read(fd, buf, sizeof buf-1))<=0){ + if((image=d->image) == nil){ + fprint(2, "gengetwindow: %r\n"); + *winp = nil; + d->screenimage = nil; + return -1; + } + strcpy(buf, "noborder"); + }else{ + close(fd); + buf[n] = '\0'; + if(*winp != nil){ + _freeimage1(*winp); + freeimage((*scrp)->image); + freescreen(*scrp); + *scrp = nil; + } + image = namedimage(d, buf); + if(image == 0){ + fprint(2, "namedimage %s failed: %r\n", buf); + *winp = nil; + d->screenimage = nil; + return -1; + } + assert(image->chan != 0); + } + + d->screenimage = image; + *scrp = allocscreen(image, d->white, 0); + if(*scrp == nil){ + freeimage(d->screenimage); + *winp = nil; + d->screenimage = nil; + return -1; + } + + r = image->r; + if(strncmp(buf, "noborder", 8) != 0) + r = insetrect(image->r, Borderwidth); + *winp = _allocwindow(*winp, *scrp, r, ref, DWhite); + if(*winp == nil){ + freescreen(*scrp); + *scrp = nil; + freeimage(image); + d->screenimage = nil; + return -1; + } + d->screenimage = *winp; + assert((*winp)->chan != 0); + return 1; +} + +int +getwindow(Display *d, int ref) +{ + char winname[128]; + + snprint(winname, sizeof winname, "%s/winname", d->windir); + return gengetwindow(d, winname, &screen, &_screen, ref); +} + +#define NINFO 12*12 + +Display* +initdisplay(char *dev, char *win, void(*error)(Display*, char*)) +{ + char buf[128], info[NINFO+1], *t, isnew; + int n, datafd, ctlfd, reffd; + Display *disp; + Dir *dir; + Image *image; + + fmtinstall('P', Pfmt); + fmtinstall('R', Rfmt); + if(dev == 0) + dev = "/dev"; + if(win == 0) + win = "/dev"; + if(strlen(dev)>sizeof buf-25 || strlen(win)>sizeof buf-25){ + werrstr("initdisplay: directory name too long"); + return nil; + } + t = strdup(win); + if(t == nil) + return nil; + + sprint(buf, "%s/draw/new", dev); + ctlfd = open(buf, ORDWR|OCEXEC); + if(ctlfd < 0){ + if(bind("#i", dev, MAFTER) < 0){ + Error1: + free(t); + werrstr("initdisplay: %s: %r", buf); + return 0; + } + ctlfd = open(buf, ORDWR|OCEXEC); + } + if(ctlfd < 0) + goto Error1; + if((n=read(ctlfd, info, sizeof info)) < 12){ + Error2: + close(ctlfd); + goto Error1; + } + if(n==NINFO+1) + n = NINFO; + buf[n] = '\0'; + isnew = 0; + if(n < NINFO) /* this will do for now, we need something better here */ + isnew = 1; + sprint(buf, "%s/draw/%d/data", dev, atoi(info+0*12)); + datafd = open(buf, ORDWR|OCEXEC); + if(datafd < 0) + goto Error2; + sprint(buf, "%s/draw/%d/refresh", dev, atoi(info+0*12)); + reffd = open(buf, OREAD|OCEXEC); + if(reffd < 0){ + Error3: + close(datafd); + goto Error2; + } + disp = mallocz(sizeof(Display), 1); + if(disp == 0){ + Error4: + close(reffd); + goto Error3; + } + image = nil; + if(0){ + Error5: + free(image); + free(disp); + goto Error4; + } + if(n >= NINFO){ + image = mallocz(sizeof(Image), 1); + if(image == nil) + goto Error5; + image->display = disp; + image->id = 0; + image->chan = strtochan(info+2*12); + image->depth = chantodepth(image->chan); + image->repl = atoi(info+3*12); + image->r.min.x = atoi(info+4*12); + image->r.min.y = atoi(info+5*12); + image->r.max.x = atoi(info+6*12); + image->r.max.y = atoi(info+7*12); + image->clipr.min.x = atoi(info+8*12); + image->clipr.min.y = atoi(info+9*12); + image->clipr.max.x = atoi(info+10*12); + image->clipr.max.y = atoi(info+11*12); + } + + disp->_isnewdisplay = isnew; + disp->bufsize = iounit(datafd); + if(disp->bufsize <= 0) + disp->bufsize = 8000; + if(disp->bufsize < 512){ + werrstr("iounit %d too small", disp->bufsize); + goto Error5; + } + disp->buf = malloc(disp->bufsize+5); /* +5 for flush message */ + if(disp->buf == nil) + goto Error5; + + disp->image = image; + disp->dirno = atoi(info+0*12); + disp->fd = datafd; + disp->ctlfd = ctlfd; + disp->reffd = reffd; + disp->bufp = disp->buf; + disp->error = error; + disp->windir = t; + disp->devdir = strdup(dev); + qlock(&disp->qlock); + disp->white = allocimage(disp, Rect(0, 0, 1, 1), GREY1, 1, DWhite); + disp->black = allocimage(disp, Rect(0, 0, 1, 1), GREY1, 1, DBlack); + if(disp->white == nil || disp->black == nil){ + free(disp->devdir); + free(disp->white); + free(disp->black); + goto Error5; + } + disp->opaque = disp->white; + disp->transparent = disp->black; + dir = dirfstat(ctlfd); + if(dir!=nil && dir->type=='i'){ + disp->local = 1; + disp->dataqid = dir->qid.path; + } + if(dir!=nil && dir->qid.vers==1) /* other way to tell */ + disp->_isnewdisplay = 1; + free(dir); + + return disp; +} + +/* + * Call with d unlocked. + * Note that disp->defaultfont and defaultsubfont are not freed here. + */ +void +closedisplay(Display *disp) +{ + _closedisplay(disp, 0); +} + +static void +_closedisplay(Display *disp, int isshutdown) +{ + int fd; + char buf[128]; + + if(disp == nil) + return; + if(disp == display) + display = nil; + if(disp->oldlabel[0]){ + snprint(buf, sizeof buf, "%s/label", disp->windir); + fd = open(buf, OWRITE); + if(fd >= 0){ + write(fd, disp->oldlabel, strlen(disp->oldlabel)); + close(fd); + } + } + + /* + * if we're shutting down, don't free all the resources. + * if other procs are getting shot down by notes too, + * one might get shot down while holding the malloc lock. + * just let the kernel clean things up when we exit. + */ + if(isshutdown) + return; + + free(disp->devdir); + free(disp->windir); + freeimage(disp->white); + freeimage(disp->black); + close(disp->fd); + close(disp->ctlfd); + /* should cause refresh slave to shut down */ + close(disp->reffd); + qunlock(&disp->qlock); + free(disp); +} + +void +lockdisplay(Display *disp) +{ + if(debuglockdisplay){ + /* avoid busy looping; it's rare we collide anyway */ + while(!canqlock(&disp->qlock)){ + fprint(1, "proc %d waiting for display lock...\n", getpid()); + sleep(1000); + } + }else + qlock(&disp->qlock); +} + +void +unlockdisplay(Display *disp) +{ + qunlock(&disp->qlock); +} + +void +drawerror(Display *d, char *s) +{ + char err[ERRMAX]; + + if(d && d->error) + d->error(d, s); + else{ + errstr(err, sizeof err); + fprint(2, "draw: %s: %s\n", s, err); + exits(s); + } +} + +static +int +doflush(Display *d) +{ + int n, nn; + + n = d->bufp-d->buf; + if(n <= 0) + return 1; + + if((nn=write(d->fd, d->buf, n)) != n){ + if(_drawdebug) + fprint(2, "flushimage fail: d=%p: n=%d nn=%d %r\n", d, n, nn); /**/ + d->bufp = d->buf; /* might as well; chance of continuing */ + return -1; + } + d->bufp = d->buf; + return 1; +} + +int +flushimage(Display *d, int visible) +{ + if(d == nil) + return 0; + if(visible){ + *d->bufp++ = 'v'; /* five bytes always reserved for this */ + if(d->_isnewdisplay){ + BPLONG(d->bufp, d->screenimage->id); + d->bufp += 4; + } + } + return doflush(d); +} + +uchar* +bufimage(Display *d, int n) +{ + uchar *p; + + if(n<0 || n>d->bufsize){ + werrstr("bad count in bufimage"); + return 0; + } + if(d->bufp+n > d->buf+d->bufsize) + if(doflush(d) < 0) + return 0; + p = d->bufp; + d->bufp += n; + return p; +} + |