|  | #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); | 
|  |  | 
|  | a = bufimage(d, 3); | 
|  | a[0] = 'q'; | 
|  | a[1] = 1; | 
|  | a[2] = 'd'; | 
|  | d->dpi = 100; | 
|  | if(flushimage(d, 0) >= 0 && _displayrddraw(d, info, 12) == 12) | 
|  | d->dpi = atoi(info); | 
|  |  | 
|  | 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; | 
|  | } | 
|  |  | 
|  | int | 
|  | scalesize(Display *d, int n) | 
|  | { | 
|  | if(d == nil || d->dpi <= DefaultDPI) | 
|  | return n; | 
|  | return (n*d->dpi+DefaultDPI/2)/DefaultDPI; | 
|  | } |