Beispiel #1
0
char*
chantostr(char *buf, ulong cc)
{
	ulong c, rc;
	char *p;

	if(chantodepth(cc) == 0)
		return nil;

	/* reverse the channel descriptor so we can easily generate the string in the right order */
	rc = 0;
	for(c=cc; c; c>>=8){
		rc <<= 8;
		rc |= c&0xFF;
	}

	p = buf;
	for(c=rc; c; c>>=8) {
		*p++ = channames[TYPE(c)];
		*p++ = '0'+NBITS(c);
	}
	*p = 0;

	return buf;
}
Beispiel #2
0
Memimage*
allocmemimage(Rectangle r, ulong chan)
{
	int d;
	uchar *p;
	ulong l, nw;
	Memdata *md;
	Memimage *i;

	if((d = chantodepth(chan)) == 0) {
		werrstr("bad channel descriptor %.8lux", chan);
		return nil;
	}

	l = wordsperline(r, d);
	nw = l*Dy(r);
	md = malloc(sizeof(Memdata));
	if(md == nil)
		return nil;

	md->ref = 1;
	md->base = poolalloc(imagmem, sizeof(Memdata*)+(1+nw)*sizeof(ulong));
	if(md->base == nil){
		free(md);
		return nil;
	}

	p = (uchar*)md->base;
	*(Memdata**)p = md;
	p += sizeof(Memdata*);

	*(ulong*)p = getcallerpc(&r);
	p += sizeof(ulong);

	/* if this changes, memimagemove must change too */
	md->bdata = p;
	md->allocd = 1;

	i = allocmemimaged(r, chan, md);
	if(i == nil){
		poolfree(imagmem, md->base);
		free(md);
		return nil;
	}
	md->imref = i;
	return i;
}
Beispiel #3
0
int
memsetchan(Memimage *i, ulong chan)
{
	int d;
	int t, j, k;
	ulong cc;
	int bytes;

	if((d = chantodepth(chan)) == 0) {
		werrstr("bad channel descriptor");
		return -1;
	}

	i->depth = d;
	i->chan = chan;
	i->flags &= ~(Fgrey|Falpha|Fcmap|Fbytes);
	bytes = 1;
	for(cc=chan, j=0, k=0; cc; j+=NBITS(cc), cc>>=8, k++){
		t=TYPE(cc);
		if(t < 0 || t >= NChan){
			werrstr("bad channel string");
			return -1;
		}
		if(t == CGrey)
			i->flags |= Fgrey;
		if(t == CAlpha)
			i->flags |= Falpha;
		if(t == CMap && i->cmap == nil){
			i->cmap = memdefcmap;
			i->flags |= Fcmap;
		}

		i->shift[t] = j;
		i->mask[t] = (1<<NBITS(cc))-1;
		i->nbits[t] = NBITS(cc);
		if(NBITS(cc) != 8)
			bytes = 0;
	}
	i->nchan = k;
	if(bytes)
		i->flags |= Fbytes;
	return 0;
}
Beispiel #4
0
Memimage*
allocmemimaged(Rectangle r, ulong chan, Memdata *md, void *X)
{
	int d;
	ulong l;
	Memimage *i;

	if(Dx(r) <= 0 || Dy(r) <= 0){
		werrstr("bad rectangle %R", r);
		return nil;
	}
	if((d = chantodepth(chan)) == 0) {
		werrstr("bad channel descriptor %.8lux", chan);
		return nil;
	}

	l = wordsperline(r, d);

	i = mallocz(sizeof(Memimage), 1);
	if(i == nil)
		return nil;

	i->X = X;
	i->data = md;
	i->zero = sizeof(ulong)*l*r.min.y;
	
	if(r.min.x >= 0)
		i->zero += (r.min.x*d)/8;
	else
		i->zero -= (-r.min.x*d+7)/8;
	i->zero = -i->zero;
	i->width = l;
	i->r = r;
	i->clipr = r;
	i->flags = 0;
	i->layer = nil;
	i->cmap = memdefcmap;
	if(memsetchan(i, chan) < 0){
		free(i);
		return nil;
	}
	return i;
}
Beispiel #5
0
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;
	info[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;
}
Beispiel #6
0
Image*
_allocimage(Image *ai, Display *d, Rectangle r, ulong chan, int repl, ulong val, int screenid, int refresh)
{
	uchar *a;
	char *err;
	Image *i;
	Rectangle clipr;
	int id;
	int depth;

	err = 0;
	i = 0;

	if(chan == 0){
		werrstr("bad channel descriptor");
		return nil;
	}

	depth = chantodepth(chan);
	if(depth == 0){
		err = "bad channel descriptor";
    Error:
		if(err)
			werrstr("allocimage: %s", err);
		else
			werrstr("allocimage: %r");
		free(i);
		return 0;
	}

	/* flush pending data so we don't get error allocating the image */
	flushimage(d, 0);
	a = bufimage(d, 1+4+4+1+4+1+4*4+4*4+4);
	if(a == 0)
		goto Error;
	d->imageid++;
	id = d->imageid;
	a[0] = 'b';
	BPLONG(a+1, id);
	BPLONG(a+5, screenid);
	a[9] = refresh;
	BPLONG(a+10, chan);
	a[14] = repl;
	BPLONG(a+15, r.min.x);
	BPLONG(a+19, r.min.y);
	BPLONG(a+23, r.max.x);
	BPLONG(a+27, r.max.y);
	if(repl)
		/* huge but not infinite, so various offsets will leave it huge, not overflow */
		clipr = Rect(-0x3FFFFFFF, -0x3FFFFFFF, 0x3FFFFFFF, 0x3FFFFFFF);
	else
		clipr = r;
	BPLONG(a+31, clipr.min.x);
	BPLONG(a+35, clipr.min.y);
	BPLONG(a+39, clipr.max.x);
	BPLONG(a+43, clipr.max.y);
	BPLONG(a+47, val);
	if(flushimage(d, 0) < 0)
		goto Error;

	if(ai)
		i = ai;
	else{
		i = malloc(sizeof(Image));
		if(i == nil){
			a = bufimage(d, 1+4);
			if(a){
				a[0] = 'f';
				BPLONG(a+1, id);
				flushimage(d, 0);
			}
			goto Error;
		}
	}
	i->display = d;
	i->id = id;
	i->depth = depth;
	i->chan = chan;
	i->r = r;
	i->clipr = clipr;
	i->repl = repl;
	i->screen = 0;
	i->next = 0;
	return i;
}
Beispiel #7
0
Image*
namedimage(Display *d, char *name)
{
	uchar *a;
	char *err, buf[12*12+1];
	Image *i;
	int id, n;
	ulong chan;

	err = 0;
	i = 0;

	n = strlen(name);
	if(n >= 256){
		err = "name too long";
    Error:
		if(err)
			werrstr("namedimage: %s", err);
		else
			werrstr("namedimage: %r");
		if(i)
			free(i);
		return 0;
	}
	/* flush pending data so we don't get error allocating the image */
	flushimage(d, 0);
	a = bufimage(d, 1+4+1+n);
	if(a == 0)
		goto Error;
	d->imageid++;
	id = d->imageid;
	a[0] = 'n';
	BPLONG(a+1, id);
	a[5] = n;
	memmove(a+6, name, n);
	if(flushimage(d, 0) < 0)
		goto Error;

	if(pread(d->ctlfd, buf, sizeof buf, 0) < 12*12)
		goto Error;
	buf[12*12] = '\0';

	i = malloc(sizeof(Image));
	if(i == nil){
	Error1:
		a = bufimage(d, 1+4);
		if(a){
			a[0] = 'f';
			BPLONG(a+1, id);
			flushimage(d, 0);
		}
		goto Error;
	}
	i->display = d;
	i->id = id;
	if((chan=strtochan(buf+2*12))==0){
		werrstr("bad channel '%.12s' from devdraw", buf+2*12);
		goto Error1;
	}
	i->chan = chan;
	i->depth = chantodepth(chan);
	i->repl = atoi(buf+3*12);
	i->r.min.x = atoi(buf+4*12);
	i->r.min.y = atoi(buf+5*12);
	i->r.max.x = atoi(buf+6*12);
	i->r.max.y = atoi(buf+7*12);
	i->clipr.min.x = atoi(buf+8*12);
	i->clipr.min.y = atoi(buf+9*12);
	i->clipr.max.x = atoi(buf+10*12);
	i->clipr.max.y = atoi(buf+11*12);
	i->screen = 0;
	i->next = 0;
	return i;
}
Beispiel #8
0
static void
vgactl(Cmdbuf *cb)
{
    int align, i, size, x, y, z;
    char *chanstr, *p;
    ulong chan;
    Cmdtab *ct;
    VGAscr *scr;
    extern VGAdev *vgadev[];
    extern VGAcur *vgacur[];

    scr = &vgascreen[0];
    ct = lookupcmd(cb, vgactlmsg, nelem(vgactlmsg));
    switch(ct->index) {
    case CMhwgc:
        if(strcmp(cb->f[1], "off") == 0) {
            lock(&cursor);
            if(scr->cur) {
                if(scr->cur->disable)
                    scr->cur->disable(scr);
                scr->cur = nil;
            }
            unlock(&cursor);
            return;
        }
        if(strcmp(cb->f[1], "soft") == 0) {
            lock(&cursor);
            swcursorinit();
            if(scr->cur && scr->cur->disable)
                scr->cur->disable(scr);
            scr->cur = &swcursor;
            if(scr->cur->enable)
                scr->cur->enable(scr);
            unlock(&cursor);
            return;
        }
        for(i = 0; vgacur[i]; i++) {
            if(strcmp(cb->f[1], vgacur[i]->name))
                continue;
            lock(&cursor);
            if(scr->cur && scr->cur->disable)
                scr->cur->disable(scr);
            scr->cur = vgacur[i];
            if(scr->cur->enable)
                scr->cur->enable(scr);
            unlock(&cursor);
            return;
        }
        break;

    case CMtype:
        for(i = 0; vgadev[i]; i++) {
            if(strcmp(cb->f[1], vgadev[i]->name))
                continue;
            if(scr->dev && scr->dev->disable)
                scr->dev->disable(scr);
            scr->dev = vgadev[i];
            if(scr->dev->enable)
                scr->dev->enable(scr);
            return;
        }
        break;

    case CMtextmode:
        screeninit();
        return;

    case CMsize:

        x = strtoul(cb->f[1], &p, 0);
        if(x == 0 || x > 10240)
            error(Ebadarg);
        if(*p)
            p++;

        y = strtoul(p, &p, 0);
        if(y == 0 || y > 10240)
            error(Ebadarg);
        if(*p)
            p++;

        z = strtoul(p, &p, 0);

        chanstr = cb->f[2];
        if((chan = strtochan(chanstr)) == 0)
            error("bad channel");

        if(chantodepth(chan) != z)
            error("depth, channel do not match");

        cursoroff(1);
        deletescreenimage();
        if(screensize(x, y, z, chan))
            error(Egreg);
        vgascreenwin(scr);
        resetscreenimage();
        cursoron(1);
        return;

    case CMactualsize:
        if(scr->gscreen == nil)
            error("set the screen size first");

        x = strtoul(cb->f[1], &p, 0);
        if(x == 0 || x > 2048)
            error(Ebadarg);
        if(*p)
            p++;

        y = strtoul(p, nil, 0);
        if(y == 0 || y > 2048)
            error(Ebadarg);

        if(x > scr->gscreen->r.max.x || y > scr->gscreen->r.max.y)
            error("physical screen bigger than virtual");

        physgscreenr = Rect(0,0,x,y);
        scr->gscreen->clipr = physgscreenr;
        return;

    case CMpalettedepth:
        x = strtoul(cb->f[1], &p, 0);
        if(x != 8 && x != 6)
            error(Ebadarg);

        scr->palettedepth = x;
        return;

    case CMdrawinit:
        memimagedraw(scr->gscreen, scr->gscreen->r, memblack, ZP, nil, ZP, S);
        if(scr && scr->dev && scr->dev->drawinit)
            scr->dev->drawinit(scr);
        return;

    case CMlinear:
        if(cb->nf!=2 && cb->nf!=3)
            error(Ebadarg);
        size = strtoul(cb->f[1], 0, 0);
        if(cb->nf == 2)
            align = 0;
        else
            align = strtoul(cb->f[2], 0, 0);
        if(screenaperture(size, align) < 0)
            error("not enough free address space");
        return;
    /*
    	case CMmemset:
    		memset((void*)strtoul(cb->f[1], 0, 0), atoi(cb->f[2]), atoi(cb->f[3]));
    		return;
    */

    case CMblank:
        drawblankscreen(1);
        return;

    case CMunblank:
        drawblankscreen(0);
        return;

    case CMblanktime:
        blanktime = strtoul(cb->f[1], 0, 0);
        return;

    case CMpanning:
        if(strcmp(cb->f[1], "on") == 0) {
            if(scr == nil || scr->cur == nil)
                error("set screen first");
            if(!scr->cur->doespanning)
                error("panning not supported");
            scr->gscreen->clipr = scr->gscreen->r;
            panning = 1;
        }
        else if(strcmp(cb->f[1], "off") == 0) {
            scr->gscreen->clipr = physgscreenr;
            panning = 0;
        } else
            break;
        return;

    case CMhwaccel:
        if(strcmp(cb->f[1], "on") == 0)
            hwaccel = 1;
        else if(strcmp(cb->f[1], "off") == 0)
            hwaccel = 0;
        else
            break;
        return;

    case CMhwblank:
        if(strcmp(cb->f[1], "on") == 0)
            hwblank = 1;
        else if(strcmp(cb->f[1], "off") == 0)
            hwblank = 0;
        else
            break;
        return;
    }

    cmderror(cb, "bad VGA control message");
}
Beispiel #9
0
static void
vgactl(char* a)
{
	int align, i, n, size, x, y, z;
	char *chanstr, *field[6], *p;
	ulong chan;
	VGAscr *scr;
	extern VGAdev *vgadev[];
	extern VGAcur *vgacur[];
	Rectangle r;

	n = tokenize(a, field, nelem(field));
	if(n < 1)
		error(Ebadarg);

	scr = &vgascreen[0];
	if(strcmp(field[0], "hwgc") == 0){
		if(n < 2)
			error(Ebadarg);

		if(strcmp(field[1], "off") == 0){
			lock(&cursor);
			if(scr->cur){
				if(scr->cur->disable)
					scr->cur->disable(scr);
				scr->cur = nil;
			}
			unlock(&cursor);
			return;
		}

		for(i = 0; vgacur[i]; i++){
			if(strcmp(field[1], vgacur[i]->name))
				continue;
			lock(&cursor);
			if(scr->cur && scr->cur->disable)
				scr->cur->disable(scr);
			scr->cur = vgacur[i];
			if(scr->cur->enable)
				scr->cur->enable(scr);
			unlock(&cursor);
			return;
		}
	}
	else if(strcmp(field[0], "type") == 0){
		if(n < 2)
			error(Ebadarg);

		for(i = 0; vgadev[i]; i++){
			if(strcmp(field[1], vgadev[i]->name))
				continue;
			if(scr->dev && scr->dev->disable)
				scr->dev->disable(scr);
			scr->dev = vgadev[i];
			if(scr->dev->enable)
				scr->dev->enable(scr);
			return;
		}
	}
	else if(strcmp(field[0], "size") == 0){
		if(n < 3)
			error(Ebadarg);
		if(drawhasclients())
			error(Ebusy);

		x = strtoul(field[1], &p, 0);
		if(x == 0 || x > 2048)
			error(Ebadarg);
		if(*p)
			p++;

		y = strtoul(p, &p, 0);
		if(y == 0 || y > 2048)
			error(Ebadarg);
		if(*p)
			p++;

		z = strtoul(p, &p, 0);

		chanstr = field[2];
		if((chan = strtochan(chanstr)) == 0)
			error("bad channel");

		if(chantodepth(chan) != z)
			error("depth, channel do not match");

		cursoroff(1);
		deletescreenimage();
		if(screensize(x, y, z, chan))
			error(Egreg);
		vgascreenwin(scr);
		cursoron(1);
		return;
	}
	else if(strcmp(field[0], "actualsize") == 0){
		if(scr->gscreen == nil)
			error("set the screen size first");

		if(n < 2)
			error(Ebadarg);
		x = strtoul(field[1], &p, 0);
		if(x == 0 || x > 2048)
			error(Ebadarg);
		if(*p)
			p++;

		y = strtoul(p, nil, 0);
		if(y == 0 || y > 2048)
			error(Ebadarg);

		if(x > scr->gscreen->r.max.x || y > scr->gscreen->r.max.y)
			error("physical screen bigger than virtual");

		r = Rect(0,0,x,y);
		if(!eqrect(r, scr->gscreen->r)){
			if(scr->cur == nil || scr->cur->doespanning == 0)
				error("virtual screen not supported");
		}

		physgscreenr = r;
		return;
	}
	else if(strcmp(field[0], "palettedepth") == 0){
		if(n < 2)
			error(Ebadarg);

		x = strtoul(field[1], &p, 0);
		if(x != 8 && x != 6)
			error(Ebadarg);

		scr->palettedepth = x;
		return;
	}
	else if(strcmp(field[0], "drawinit") == 0){
		if(scr && scr->dev && scr->dev->drawinit)
			scr->dev->drawinit(scr);
		return;
	}
	else if(strcmp(field[0], "linear") == 0){
		if(n < 2)
			error(Ebadarg);

		size = strtoul(field[1], 0, 0);
		if(n < 3)
			align = 0;
		else
			align = strtoul(field[2], 0, 0);
		if(screenaperture(size, align))
			error("not enough free address space");
		return;
	}
/*	else if(strcmp(field[0], "memset") == 0){
		if(n < 4)
			error(Ebadarg);
		memset((void*)strtoul(field[1], 0, 0), atoi(field[2]), atoi(field[3]));
		return;
	}
*/
	else if(strcmp(field[0], "blank") == 0){
		if(n < 1)
			error(Ebadarg);
		drawblankscreen(1);
		return;
	}
	else if(strcmp(field[0], "blanktime") == 0){
		if(n < 2)
			error(Ebadarg);
		blanktime = strtoul(field[1], 0, 0);
		return;
	}
	else if(strcmp(field[0], "hwaccel") == 0){
		if(n < 2)
			error(Ebadarg);
		if(strcmp(field[1], "on") == 0)
			hwaccel = 1;
		else if(strcmp(field[1], "off") == 0)
			hwaccel = 0;
		return;
	}
	else if(strcmp(field[0], "hwblank") == 0){
		if(n < 2)
			error(Ebadarg);
		if(strcmp(field[1], "on") == 0)
			hwblank = 1;
		else if(strcmp(field[1], "off") == 0)
			hwblank = 0;
		return;
	}

	error(Ebadarg);
}
Beispiel #10
0
static void
xread(Req *r)
{
	int i, size, height, ascent;
	vlong path;
	Fmt fmt;
	XFont *f;
	char *data;
	Memsubfont *sf;
	Memimage *m;
	
	path = r->fid->qid.path;
	switch(QTYPE(path)) {
	case Qroot:
		dirread9p(r, rootgen, nil);
		break;
	case Qfontdir:
		dirread9p(r, fontgen, r->fid);
		break;
	case Qsizedir:
		dirread9p(r, sizegen, r->fid);
		break;
	case Qfontfile:
		fmtstrinit(&fmt);
		f = &xfont[QFONT(path)];
		load(f);
		if(f->unit == 0 && f->loadheight == nil) {
			readstr(r, "font missing\n");
			break;
		}
		height = 0;
		ascent = 0;
		if(f->unit > 0) {
			height = f->height * (int)QSIZE(path)/f->unit + 0.99999999;
			ascent = height - (int)(-f->originy * (int)QSIZE(path)/f->unit + 0.99999999);
		}
		if(f->loadheight != nil)
			f->loadheight(f, QSIZE(path), &height, &ascent);
		fmtprint(&fmt, "%11d %11d\n", height, ascent);
		for(i=0; i<nelem(f->range); i++) {
			if(f->range[i] == 0)
				continue;
			fmtprint(&fmt, "0x%04x 0x%04x x%04x.bit\n", i*SubfontSize, ((i+1)*SubfontSize) - 1, i*SubfontSize);
		}
		data = fmtstrflush(&fmt);
		readstr(r, data);
		free(data);
		break;
	case Qsubfontfile:
		f = &xfont[QFONT(path)];
		load(f);
		if(r->fid->aux == nil) {
			r->fid->aux = mksubfont(f, f->name, QRANGE(path)*SubfontSize, ((QRANGE(path)+1)*SubfontSize)-1, QSIZE(path), QANTIALIAS(path));
			if(r->fid->aux == nil) {
				responderrstr(r);
				return;
			}
		}
		sf = r->fid->aux;
		m = sf->bits;
		if(r->ifcall.offset < 5*12) {
			char *chan;
			if(QANTIALIAS(path))
				chan = "k8";
			else
				chan = "k1";
			data = smprint("%11s %11d %11d %11d %11d ", chan, m->r.min.x, m->r.min.y, m->r.max.x, m->r.max.y);
			readstr(r, data);
			free(data);
			break;
		}
		r->ifcall.offset -= 5*12;
		size = bytesperline(m->r, chantodepth(m->chan)) * Dy(m->r);
		if(r->ifcall.offset < size) {
			readbuf(r, byteaddr(m, m->r.min), size);
			break;
		}
		r->ifcall.offset -= size;
		data = emalloc9p(3*12+6*(sf->n+1));
		sprint(data, "%11d %11d %11d ", sf->n, sf->height, sf->ascent);
		packinfo(sf->info, (uchar*)data+3*12, sf->n);
		readbuf(r, data, 3*12+6*(sf->n+1));
		free(data);
		break;
	}
	respond(r, nil);
}