| #include <u.h> |
| #include <libc.h> |
| #include <draw.h> |
| #include <mouse.h> |
| |
| Display *display; |
| Font *font; |
| Image *screen; |
| int _drawdebug; |
| |
| static char deffontname[] = "*default*"; |
| Screen *_screen; |
| |
| int debuglockdisplay = 1; |
| char *winsize; |
| |
| int visibleclicks = 0; |
| Image *mousebuttons; |
| Image *mousesave; |
| Mouse _drawmouse; |
| |
| /* |
| static void |
| drawshutdown(void) |
| { |
| Display *d; |
| |
| d = display; |
| if(d){ |
| display = nil; |
| closedisplay(d); |
| } |
| } |
| */ |
| |
| int |
| geninitdraw(char *devdir, void(*error)(Display*, char*), char *fontname, char *label, char *windir, int ref) |
| { |
| Subfont *df; |
| char buf[128], *p; |
| |
| if(label == nil) |
| label = argv0; |
| display = _initdisplay(error, label); |
| 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) |
| fontname = getenv("font"); |
| |
| /* |
| * 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; |
| |
| _screen = allocscreen(display->image, display->white, 0); |
| display->screenimage = display->image; /* _allocwindow wants screenimage->chan */ |
| screen = _allocwindow(nil, _screen, display->image->r, Refnone, DWhite); |
| if(screen == nil){ |
| fprint(2, "_allocwindow: %r\n"); |
| goto Error; |
| } |
| display->screenimage = screen; |
| draw(screen, screen->r, display->white, nil, ZP); |
| flushimage(display, 1); |
| |
| p = getenv("visibleclicks"); |
| visibleclicks = p != nil && *p == '1'; |
| if(visibleclicks) { |
| Font *f; |
| |
| f = display->defaultfont; |
| mousebuttons = allocimage(display, Rect(0,0,64,22), screen->chan, 0, DWhite); |
| border(mousebuttons, mousebuttons->r, 1, display->black, ZP); |
| border(mousebuttons, Rect(0, 0, 22, 22), 1, display->black, ZP); |
| border(mousebuttons, Rect(42, 0, 64, 22), 1, display->black, ZP); |
| string(mousebuttons, Pt(10-stringwidth(display->defaultfont, "1")/2, 11-f->height/2), display->black, ZP, display->defaultfont, "1"); |
| string(mousebuttons, Pt(21+10-stringwidth(display->defaultfont, "2")/2, 11-f->height/2), display->black, ZP, display->defaultfont, "2"); |
| string(mousebuttons, Pt(42+10-stringwidth(display->defaultfont, "3")/2, 11-f->height/2), display->black, ZP, display->defaultfont, "3"); |
| mousesave = allocimage(display, Rect(0,0,64,22), screen->chan, 0, 0); |
| } |
| |
| /* |
| * I don't see any reason to go away gracefully, |
| * and if some other proc exits holding the display |
| * lock, this atexit call never finishes. |
| * |
| * atexit(drawshutdown); |
| */ |
| return 1; |
| } |
| |
| int |
| initdraw(void (*error)(Display*, char*), char *fontname, char *label) |
| { |
| return geninitdraw("/dev", error, fontname, label, "/dev", Refnone); |
| } |
| |
| extern int _freeimage1(Image*); |
| |
| static Image* |
| getimage0(Display *d, Image *image) |
| { |
| char info[12*12+1]; |
| uchar *a; |
| int n; |
| |
| /* |
| * If there's an old screen, it has id 0. The 'J' request below |
| * will try to install the new screen as id 0, so the old one |
| * must be freed first. |
| */ |
| if(image){ |
| _freeimage1(image); |
| memset(image, 0, sizeof(Image)); |
| } |
| |
| a = bufimage(d, 2); |
| a[0] = 'J'; |
| a[1] = 'I'; |
| if(flushimage(d, 0) < 0){ |
| fprint(2, "cannot read screen info: %r\n"); |
| return nil; |
| } |
| |
| n = _displayrddraw(d, info, sizeof info); |
| if(n != 12*12){ |
| fprint(2, "short screen info\n"); |
| return nil; |
| } |
| |
| if(image == nil){ |
| image = mallocz(sizeof(Image), 1); |
| if(image == nil){ |
| fprint(2, "cannot allocate image: %r\n"); |
| return nil; |
| } |
| } |
| |
| image->display = d; |
| 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); |
| return image; |
| } |
| |
| /* |
| * Attach, or possibly reattach, to window. |
| * If reattaching, maintain value of screen pointer. |
| */ |
| int |
| getwindow(Display *d, int ref) |
| { |
| Image *i, *oi; |
| |
| /* XXX check for destroyed? */ |
| |
| /* |
| * Libdraw promises not to change the value of "screen", |
| * so we have to reuse the image structure |
| * memory we already have. |
| */ |
| oi = d->image; |
| i = getimage0(d, oi); |
| if(i == nil) |
| sysfatal("getwindow failed"); |
| d->image = i; |
| /* fprint(2, "getwindow %p -> %p\n", oi, i); */ |
| |
| freescreen(_screen); |
| _screen = allocscreen(i, d->white, 0); |
| _freeimage1(screen); |
| screen = _allocwindow(screen, _screen, i->r, ref, DWhite); |
| d->screenimage = screen; |
| return 0; |
| } |
| |
| Display* |
| _initdisplay(void (*error)(Display*, char*), char *label) |
| { |
| Display *disp; |
| Image *image; |
| |
| fmtinstall('P', Pfmt); |
| fmtinstall('R', Rfmt); |
| |
| disp = mallocz(sizeof(Display), 1); |
| if(disp == nil){ |
| Error1: |
| return nil; |
| } |
| disp->srvfd = -1; |
| image = nil; |
| if(0){ |
| Error2: |
| free(image); |
| free(disp); |
| goto Error1; |
| } |
| disp->bufsize = 65500; |
| disp->buf = malloc(disp->bufsize+5); /* +5 for flush message */ |
| disp->bufp = disp->buf; |
| disp->error = error; |
| qlock(&disp->qlock); |
| |
| if(disp->buf == nil) |
| goto Error2; |
| if(0){ |
| Error3: |
| free(disp->buf); |
| goto Error2; |
| } |
| |
| if(_displaymux(disp) < 0 |
| || _displayconnect(disp) < 0 |
| || _displayinit(disp, label, winsize) < 0) |
| goto Error3; |
| if(0){ |
| Error4: |
| close(disp->srvfd); |
| goto Error3; |
| } |
| |
| image = getimage0(disp, nil); |
| if(image == nil) |
| goto Error4; |
| |
| disp->image = image; |
| 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->white); |
| free(disp->black); |
| goto Error4; |
| } |
| |
| disp->opaque = disp->white; |
| disp->transparent = disp->black; |
| |
| return disp; |
| } |
| |
| /* |
| * Call with d unlocked. |
| * Note that disp->defaultfont and defaultsubfont are not freed here. |
| */ |
| void |
| closedisplay(Display *disp) |
| { |
| 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); |
| } |
| } |
| |
| free(disp->devdir); |
| free(disp->windir); |
| if(disp->white) |
| freeimage(disp->white); |
| if(disp->black) |
| freeimage(disp->black); |
| if(disp->srvfd >= 0) |
| close(disp->srvfd); |
| 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->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; |
| |
| n = d->bufp-d->buf; |
| if(n <= 0) |
| return 1; |
| |
| if(_displaywrdraw(d, d->buf, n) != n){ |
| if(_drawdebug) |
| fprint(2, "flushimage fail: d=%p: %r\n", d); /**/ |
| 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(visible == 1 && visibleclicks && mousebuttons && _drawmouse.buttons) { |
| Rectangle r, r1; |
| int ret; |
| |
| r = mousebuttons->r; |
| r = rectaddpt(r, _drawmouse.xy); |
| r = rectaddpt(r, Pt(-Dx(mousebuttons->r)/2, -Dy(mousebuttons->r)-3)); |
| drawop(mousesave, mousesave->r, screen, nil, r.min, S); |
| |
| r1 = rectaddpt(Rect(0, 0, 22, 22), r.min); |
| if(_drawmouse.buttons & 1) |
| drawop(screen, r1, mousebuttons, nil, ZP, S); |
| r1 = rectaddpt(r1, Pt(21, 0)); |
| if(_drawmouse.buttons & 2) |
| drawop(screen, r1, mousebuttons, nil, Pt(21, 0), S); |
| r1 = rectaddpt(r1, Pt(21, 0)); |
| if(_drawmouse.buttons & 4) |
| drawop(screen, r1, mousebuttons, nil, Pt(42, 0), S); |
| ret = flushimage(d, 2); |
| drawop(screen, r, mousesave, nil, ZP, S); |
| return ret; |
| } |
| |
| 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 || d == nil || n>d->bufsize){ |
| abort(); |
| 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; |
| } |
| |