#include <9pm/u.h>
#include <9pm/libc.h>
#include <9pm/draw.h>
#include <9pm/ns.h>
#undef error
#undef devdir
Display *display;
Font *font;
Image *screen;
int _drawdebug;
static char deffontname[] = "*default*";
Screen *_screen;
int debuglockdisplay = 0;
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)
{
int fd, n;
Subfont *df;
char buf[128];
adddev(&devmouse);
adddev(&devdraw);
bind("#U/srv", "/srv", MREPL|MCREATE);
bind("#i", "/dev", MBEFORE);
bind("#m", "/dev", MBEFORE);
close(0);
close(1);
open("/dev/cons", OREAD);
open("/dev/cons", OWRITE);
display = initdisplay(devdir, windir, error);
if(display == nil)
return -1;
/*
* Set up default font
*/
df = getdefont(display);
display->defaultsubfont = df;
if(df == nil){
_drawprint(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){
_drawprint(2, "imageinit: can't open default font: %r\n");
goto Error;
}
}else{
font = openfont(display, fontname); /* BUG: grey fonts */
if(font == nil){
_drawprint(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);
}
}
}
if(getwindow(display, 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){
_drawprint(2, "imageinit: can't bind /dev/draw: %r");
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){
*winp = d->image;
assert(*winp && (*winp)->chan != 0);
return 1;
}
close(fd);
buf[n] = '\0';
if(*winp != nil){
_freeimage1(*winp);
freeimage((*scrp)->image);
freescreen(*scrp);
*scrp = nil;
}
image = namedimage(d, buf);
if(image == 0){
*winp = nil;
return -1;
}
assert(image->chan != 0);
*scrp = allocscreen(image, d->white, 0);
if(*scrp == nil){
*winp = nil;
return -1;
}
r = image->r;
if(strncmp(buf, "kernel", 6) != 0)
r = insetrect(image->r, Borderwidth);
*winp = _allocwindow(*winp, *scrp, r, ref, DWhite);
if(*winp == nil)
return -1;
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;
int datafd, ctlfd, reffd;
Display *disp;
Image *image;
Dir *dir;
ulong chan;
fmtinstall('P', Pconv);
fmtinstall('R', Rconv);
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(read(ctlfd, info, sizeof info) < NINFO){
Error2:
close(ctlfd);
goto Error1;
}
if((chan=strtochan(info+2*12)) == 0){
werrstr("bad channel '%.*s' in %s", utfnlen(info+2*12, 12), info+2*12, buf);
goto Error2;
}
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 = malloc(sizeof(Display));
if(disp == 0){
Error4:
close(reffd);
goto Error3;
}
image = malloc(sizeof(Image));
if(image == 0){
Error5:
free(disp);
goto Error4;
}
memset(image, 0, sizeof(Image));
memset(disp, 0, sizeof(Display));
disp->bufsize = iounit(datafd);
if(disp->bufsize <= 0)
disp->bufsize = 8000;
if(disp->bufsize < 512){
free(image);
werrstr("iounit %d too small", disp->bufsize);
goto Error5;
}
disp->buf = malloc(disp->bufsize+1); /* +1 for flush message */
if(disp->buf == nil){
free(image);
goto Error5;
}
image->idisplay = disp;
image->id = 0;
image->chan = chan;
image->depth = chantodepth(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->dirno = atoi(info+0*12);
disp->fd = datafd;
disp->ctlfd = ctlfd;
disp->reffd = reffd;
disp->image = image;
disp->bufp = disp->buf;
disp->error = error;
disp->chan = image->chan;
disp->depth = image->depth;
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(image);
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;
}
free(dir);
assert(disp->chan != 0 && image->chan != 0);
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);
freeimage(disp->white);
freeimage(disp->black);
free(disp->image);
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)){
_drawprint(1, "proc %d waiting for display lock...\n", getpid());
sleep(1000);
}
}else
qlock(&disp->qlock);
}
void
unlockdisplay(Display *disp)
{
qunlock(&disp->qlock);
}
/* use static buffer to avoid stack bloat */
int
_drawprint(int fd, char *fmt, ...)
{
int n;
va_list arg;
static char buf[1024];
static QLock l;
qlock(&l);
va_start(arg, fmt);
doprint(buf, buf+sizeof buf, fmt, arg);
va_end(arg);
n = write(fd, buf, strlen(buf));
qunlock(&l);
return n;
}
void
drawerror(Display *d, char *s)
{
char err[ERRMAX];
if(d->error)
d->error(d, s);
else{
errstr(err, sizeof err);
_drawprint(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(write(d->fd, d->buf, n) != n){
if(_drawdebug)
_drawprint(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)
*d->bufp++ = 'v'; /* one byte always reserved for this */
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;
}
syntax highlighted by Code2HTML, v. 0.9.1