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/cmd/rio/xfid.c |
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/cmd/rio/xfid.c')
-rwxr-xr-x | sys/src/cmd/rio/xfid.c | 846 |
1 files changed, 846 insertions, 0 deletions
diff --git a/sys/src/cmd/rio/xfid.c b/sys/src/cmd/rio/xfid.c new file mode 100755 index 000000000..c67f783fb --- /dev/null +++ b/sys/src/cmd/rio/xfid.c @@ -0,0 +1,846 @@ +#include <u.h> +#include <libc.h> +#include <draw.h> +#include <thread.h> +#include <cursor.h> +#include <mouse.h> +#include <keyboard.h> +#include <frame.h> +#include <fcall.h> +#include <plumb.h> +#include "dat.h" +#include "fns.h" + +#define MAXSNARF 100*1024 + +char Einuse[] = "file in use"; +char Edeleted[] = "window deleted"; +char Ebadreq[] = "bad graphics request"; +char Etooshort[] = "buffer too small"; +char Ebadtile[] = "unknown tile"; +char Eshort[] = "short i/o request"; +char Elong[] = "snarf buffer too long"; +char Eunkid[] = "unknown id in attach"; +char Ebadrect[] = "bad rectangle in attach"; +char Ewindow[] = "cannot make window"; +char Enowindow[] = "window has no image"; +char Ebadmouse[] = "bad format on /dev/mouse"; +char Ebadwrect[] = "rectangle outside screen"; +char Ebadoffset[] = "window read not on scan line boundary"; +extern char Eperm[]; + +static Xfid *xfidfree; +static Xfid *xfid; +static Channel *cxfidalloc; /* chan(Xfid*) */ +static Channel *cxfidfree; /* chan(Xfid*) */ + +static char *tsnarf; +static int ntsnarf; + +void +xfidallocthread(void*) +{ + Xfid *x; + enum { Alloc, Free, N }; + static Alt alts[N+1]; + + alts[Alloc].c = cxfidalloc; + alts[Alloc].v = nil; + alts[Alloc].op = CHANRCV; + alts[Free].c = cxfidfree; + alts[Free].v = &x; + alts[Free].op = CHANRCV; + alts[N].op = CHANEND; + for(;;){ + switch(alt(alts)){ + case Alloc: + x = xfidfree; + if(x) + xfidfree = x->free; + else{ + x = emalloc(sizeof(Xfid)); + x->c = chancreate(sizeof(void(*)(Xfid*)), 0); + x->flushc = chancreate(sizeof(int), 0); /* notification only; no data */ + x->flushtag = -1; + x->next = xfid; + xfid = x; + threadcreate(xfidctl, x, 16384); + } + if(x->ref != 0){ + fprint(2, "%p incref %ld\n", x, x->ref); + error("incref"); + } + if(x->flushtag != -1) + error("flushtag in allocate"); + incref(x); + sendp(cxfidalloc, x); + break; + case Free: + if(x->ref != 0){ + fprint(2, "%p decref %ld\n", x, x->ref); + error("decref"); + } + if(x->flushtag != -1) + error("flushtag in free"); + x->free = xfidfree; + xfidfree = x; + break; + } + } +} + +Channel* +xfidinit(void) +{ + cxfidalloc = chancreate(sizeof(Xfid*), 0); + cxfidfree = chancreate(sizeof(Xfid*), 0); + threadcreate(xfidallocthread, nil, STACK); + return cxfidalloc; +} + +void +xfidctl(void *arg) +{ + Xfid *x; + void (*f)(Xfid*); + char buf[64]; + + x = arg; + snprint(buf, sizeof buf, "xfid.%p", x); + threadsetname(buf); + for(;;){ + f = recvp(x->c); + (*f)(x); + if(decref(x) == 0) + sendp(cxfidfree, x); + } +} + +void +xfidflush(Xfid *x) +{ + Fcall t; + Xfid *xf; + + for(xf=xfid; xf; xf=xf->next) + if(xf->flushtag == x->oldtag){ + xf->flushtag = -1; + xf->flushing = TRUE; + incref(xf); /* to hold data structures up at tail of synchronization */ + if(xf->ref == 1) + error("ref 1 in flush"); + if(canqlock(&xf->active)){ + qunlock(&xf->active); + sendul(xf->flushc, 0); + }else{ + qlock(&xf->active); /* wait for him to finish */ + qunlock(&xf->active); + } + xf->flushing = FALSE; + if(decref(xf) == 0) + sendp(cxfidfree, xf); + break; + } + filsysrespond(x->fs, x, &t, nil); +} + +void +xfidattach(Xfid *x) +{ + Fcall t; + int id, hideit, scrollit; + Window *w; + char *err, *n, *dir, errbuf[ERRMAX]; + int pid, newlymade; + Rectangle r; + Image *i; + + t.qid = x->f->qid; + qlock(&all); + w = nil; + err = Eunkid; + newlymade = FALSE; + hideit = 0; + + if(x->aname[0] == 'N'){ /* N 100,100, 200, 200 - old syntax */ + n = x->aname+1; + pid = strtoul(n, &n, 0); + if(*n == ',') + n++; + r.min.x = strtoul(n, &n, 0); + if(*n == ',') + n++; + r.min.y = strtoul(n, &n, 0); + if(*n == ',') + n++; + r.max.x = strtoul(n, &n, 0); + if(*n == ',') + n++; + r.max.y = strtoul(n, &n, 0); + Allocate: + if(!goodrect(r)) + err = Ebadrect; + else{ + if(hideit) + i = allocimage(display, r, screen->chan, 0, DWhite); + else + i = allocwindow(wscreen, r, Refbackup, DWhite); + if(i){ + border(i, r, Selborder, display->black, ZP); + if(pid == 0) + pid = -1; /* make sure we don't pop a shell! - UGH */ + w = new(i, hideit, scrolling, pid, nil, nil, nil); + flushimage(display, 1); + newlymade = TRUE; + }else + err = Ewindow; + } + }else if(strncmp(x->aname, "new", 3) == 0){ /* new -dx -dy - new syntax, as in wctl */ + pid = 0; + if(parsewctl(nil, ZR, &r, &pid, nil, &hideit, &scrollit, &dir, x->aname, errbuf) < 0) + err = errbuf; + else + goto Allocate; + }else{ + id = atoi(x->aname); + w = wlookid(id); + } + x->f->w = w; + if(w == nil){ + qunlock(&all); + x->f->busy = FALSE; + filsysrespond(x->fs, x, &t, err); + return; + } + if(!newlymade) /* counteract dec() in winshell() */ + incref(w); + qunlock(&all); + filsysrespond(x->fs, x, &t, nil); +} + +void +xfidopen(Xfid *x) +{ + Fcall t; + Window *w; + + w = x->f->w; + if(w->deleted){ + filsysrespond(x->fs, x, &t, Edeleted); + return; + } + switch(FILE(x->f->qid)){ + case Qconsctl: + if(w->ctlopen){ + filsysrespond(x->fs, x, &t, Einuse); + return; + } + w->ctlopen = TRUE; + break; + case Qkbdin: + if(w != wkeyboard){ + filsysrespond(x->fs, x, &t, Eperm); + return; + } + break; + case Qmouse: + if(w->mouseopen){ + filsysrespond(x->fs, x, &t, Einuse); + return; + } + /* + * Reshaped: there's a race if the appl. opens the + * window, is resized, and then opens the mouse, + * but that's rare. The alternative is to generate + * a resized event every time a new program starts + * up in a window that has been resized since the + * dawn of time. We choose the lesser evil. + */ + w->resized = FALSE; + w->mouseopen = TRUE; + break; + case Qsnarf: + if(x->mode==ORDWR || x->mode==OWRITE){ + if(tsnarf) + free(tsnarf); /* collision, but OK */ + ntsnarf = 0; + tsnarf = malloc(1); + } + break; + case Qwctl: + if(x->mode==OREAD || x->mode==ORDWR){ + /* + * It would be much nicer to implement fan-out for wctl reads, + * so multiple people can see the resizings, but rio just isn't + * structured for that. It's structured for /dev/cons, which gives + * alternate data to alternate readers. So to keep things sane for + * wctl, we compromise and give an error if two people try to + * open it. Apologies. + */ + if(w->wctlopen){ + filsysrespond(x->fs, x, &t, Einuse); + return; + } + w->wctlopen = TRUE; + w->wctlready = 1; + wsendctlmesg(w, Wakeup, ZR, nil); + } + break; + } + t.qid = x->f->qid; + t.iounit = messagesize-IOHDRSZ; + x->f->open = TRUE; + x->f->mode = x->mode; + filsysrespond(x->fs, x, &t, nil); +} + +void +xfidclose(Xfid *x) +{ + Fcall t; + Window *w; + int nb, nulls; + + w = x->f->w; + switch(FILE(x->f->qid)){ + case Qconsctl: + if(w->rawing){ + w->rawing = FALSE; + wsendctlmesg(w, Rawoff, ZR, nil); + } + if(w->holding){ + w->holding = FALSE; + wsendctlmesg(w, Holdoff, ZR, nil); + } + w->ctlopen = FALSE; + break; + case Qcursor: + w->cursorp = nil; + wsetcursor(w, FALSE); + break; + case Qmouse: + w->resized = FALSE; + w->mouseopen = FALSE; + if(w->i != nil) + wsendctlmesg(w, Refresh, w->i->r, nil); + break; + /* odd behavior but really ok: replace snarf buffer when /dev/snarf is closed */ + case Qsnarf: + if(x->f->mode==ORDWR || x->f->mode==OWRITE){ + snarf = runerealloc(snarf, ntsnarf+1); + cvttorunes(tsnarf, ntsnarf, snarf, &nb, &nsnarf, &nulls); + free(tsnarf); + tsnarf = nil; + ntsnarf = 0; + } + break; + case Qwctl: + if(x->f->mode==OREAD || x->f->mode==ORDWR) + w->wctlopen = FALSE; + break; + } + wclose(w); + filsysrespond(x->fs, x, &t, nil); +} + +void +xfidwrite(Xfid *x) +{ + Fcall fc; + int c, cnt, qid, nb, off, nr; + char buf[256], *p; + Point pt; + Window *w; + Rune *r; + Conswritemesg cwm; + Stringpair pair; + enum { CWdata, CWflush, NCW }; + Alt alts[NCW+1]; + + w = x->f->w; + if(w->deleted){ + filsysrespond(x->fs, x, &fc, Edeleted); + return; + } + qid = FILE(x->f->qid); + cnt = x->count; + off = x->offset; + x->data[cnt] = 0; + switch(qid){ + case Qcons: + nr = x->f->nrpart; + if(nr > 0){ + memmove(x->data+nr, x->data, cnt); /* there's room: see malloc in filsysproc */ + memmove(x->data, x->f->rpart, nr); + cnt += nr; + x->f->nrpart = 0; + } + r = runemalloc(cnt); + cvttorunes(x->data, cnt-UTFmax, r, &nb, &nr, nil); + /* approach end of buffer */ + while(fullrune(x->data+nb, cnt-nb)){ + c = nb; + nb += chartorune(&r[nr], x->data+c); + if(r[nr]) + nr++; + } + if(nb < cnt){ + memmove(x->f->rpart, x->data+nb, cnt-nb); + x->f->nrpart = cnt-nb; + } + x->flushtag = x->tag; + + alts[CWdata].c = w->conswrite; + alts[CWdata].v = &cwm; + alts[CWdata].op = CHANRCV; + alts[CWflush].c = x->flushc; + alts[CWflush].v = nil; + alts[CWflush].op = CHANRCV; + alts[NCW].op = CHANEND; + + switch(alt(alts)){ + case CWdata: + break; + case CWflush: + filsyscancel(x); + return; + } + + /* received data */ + x->flushtag = -1; + if(x->flushing){ + recv(x->flushc, nil); /* wake up flushing xfid */ + pair.s = runemalloc(1); + pair.ns = 0; + send(cwm.cw, &pair); /* wake up window with empty data */ + filsyscancel(x); + return; + } + qlock(&x->active); + pair.s = r; + pair.ns = nr; + send(cwm.cw, &pair); + fc.count = x->count; + filsysrespond(x->fs, x, &fc, nil); + qunlock(&x->active); + return; + + case Qconsctl: + if(strncmp(x->data, "holdon", 6)==0){ + if(w->holding++ == 0) + wsendctlmesg(w, Holdon, ZR, nil); + break; + } + if(strncmp(x->data, "holdoff", 7)==0 && w->holding){ + if(--w->holding == FALSE) + wsendctlmesg(w, Holdoff, ZR, nil); + break; + } + if(strncmp(x->data, "rawon", 5)==0){ + if(w->holding){ + w->holding = FALSE; + wsendctlmesg(w, Holdoff, ZR, nil); + } + if(w->rawing++ == 0) + wsendctlmesg(w, Rawon, ZR, nil); + break; + } + if(strncmp(x->data, "rawoff", 6)==0 && w->rawing){ + if(--w->rawing == 0) + wsendctlmesg(w, Rawoff, ZR, nil); + break; + } + filsysrespond(x->fs, x, &fc, "unknown control message"); + return; + + case Qcursor: + if(cnt < 2*4+2*2*16) + w->cursorp = nil; + else{ + w->cursor.offset.x = BGLONG(x->data+0*4); + w->cursor.offset.y = BGLONG(x->data+1*4); + memmove(w->cursor.clr, x->data+2*4, 2*2*16); + w->cursorp = &w->cursor; + } + wsetcursor(w, !sweeping); + break; + + case Qlabel: + if(off != 0){ + filsysrespond(x->fs, x, &fc, "non-zero offset writing label"); + return; + } + free(w->label); + w->label = emalloc(cnt+1); + memmove(w->label, x->data, cnt); + w->label[cnt] = 0; + break; + + case Qmouse: + if(w!=input || Dx(w->screenr)<=0) + break; + if(x->data[0] != 'm'){ + filsysrespond(x->fs, x, &fc, Ebadmouse); + return; + } + p = nil; + pt.x = strtoul(x->data+1, &p, 0); + if(p == nil){ + filsysrespond(x->fs, x, &fc, Eshort); + return; + } + pt.y = strtoul(p, nil, 0); + if(w==input && wpointto(mouse->xy)==w) + wsendctlmesg(w, Movemouse, Rpt(pt, pt), nil); + break; + + case Qsnarf: + /* always append only */ + if(ntsnarf > MAXSNARF){ /* avoid thrashing when people cut huge text */ + filsysrespond(x->fs, x, &fc, Elong); + return; + } + tsnarf = erealloc(tsnarf, ntsnarf+cnt+1); /* room for NUL */ + memmove(tsnarf+ntsnarf, x->data, cnt); + ntsnarf += cnt; + snarfversion++; + break; + + case Qwdir: + if(cnt == 0) + break; + if(x->data[cnt-1] == '\n'){ + if(cnt == 1) + break; + x->data[cnt-1] = '\0'; + } + /* assume data comes in a single write */ + /* + * Problem: programs like dossrv, ftp produce illegal UTF; + * we must cope by converting it first. + */ + snprint(buf, sizeof buf, "%.*s", cnt, x->data); + if(buf[0] == '/'){ + free(w->dir); + w->dir = estrdup(buf); + }else{ + p = emalloc(strlen(w->dir) + 1 + strlen(buf) + 1); + sprint(p, "%s/%s", w->dir, buf); + free(w->dir); + w->dir = cleanname(p); + } + break; + + case Qkbdin: + keyboardsend(x->data, cnt); + break; + + case Qwctl: + if(writewctl(x, buf) < 0){ + filsysrespond(x->fs, x, &fc, buf); + return; + } + flushimage(display, 1); + break; + + default: + fprint(2, buf, "unknown qid %d in write\n", qid); + sprint(buf, "unknown qid in write"); + filsysrespond(x->fs, x, &fc, buf); + return; + } + fc.count = cnt; + filsysrespond(x->fs, x, &fc, nil); +} + +int +readwindow(Image *i, char *t, Rectangle r, int offset, int n) +{ + int ww, y; + + offset -= 5*12; + ww = bytesperline(r, screen->depth); + r.min.y += offset/ww; + if(r.min.y >= r.max.y) + return 0; + y = r.min.y + n/ww; + if(y < r.max.y) + r.max.y = y; + if(r.max.y <= r.min.y) + return 0; + return unloadimage(i, r, (uchar*)t, n); +} + +void +xfidread(Xfid *x) +{ + Fcall fc; + int n, off, cnt, c; + uint qid; + char buf[128], *t; + char cbuf[30]; + Window *w; + Mouse ms; + Rectangle r; + Image *i; + Channel *c1, *c2; /* chan (tuple(char*, int)) */ + Consreadmesg crm; + Mousereadmesg mrm; + Consreadmesg cwrm; + Stringpair pair; + enum { CRdata, CRflush, NCR }; + enum { MRdata, MRflush, NMR }; + enum { WCRdata, WCRflush, NWCR }; + Alt alts[NCR+1]; + + w = x->f->w; + if(w->deleted){ + filsysrespond(x->fs, x, &fc, Edeleted); + return; + } + qid = FILE(x->f->qid); + off = x->offset; + cnt = x->count; + switch(qid){ + case Qcons: + x->flushtag = x->tag; + + alts[CRdata].c = w->consread; + alts[CRdata].v = &crm; + alts[CRdata].op = CHANRCV; + alts[CRflush].c = x->flushc; + alts[CRflush].v = nil; + alts[CRflush].op = CHANRCV; + alts[NMR].op = CHANEND; + + switch(alt(alts)){ + case CRdata: + break; + case CRflush: + filsyscancel(x); + return; + } + + /* received data */ + x->flushtag = -1; + c1 = crm.c1; + c2 = crm.c2; + t = malloc(cnt+UTFmax+1); /* room to unpack partial rune plus */ + pair.s = t; + pair.ns = cnt; + send(c1, &pair); + if(x->flushing){ + recv(x->flushc, nil); /* wake up flushing xfid */ + recv(c2, nil); /* wake up window and toss data */ + free(t); + filsyscancel(x); + return; + } + qlock(&x->active); + recv(c2, &pair); + fc.data = pair.s; + fc.count = pair.ns; + filsysrespond(x->fs, x, &fc, nil); + free(t); + qunlock(&x->active); + break; + + case Qlabel: + n = strlen(w->label); + if(off > n) + off = n; + if(off+cnt > n) + cnt = n-off; + fc.data = w->label+off; + fc.count = cnt; + filsysrespond(x->fs, x, &fc, nil); + break; + + case Qmouse: + x->flushtag = x->tag; + + alts[MRdata].c = w->mouseread; + alts[MRdata].v = &mrm; + alts[MRdata].op = CHANRCV; + alts[MRflush].c = x->flushc; + alts[MRflush].v = nil; + alts[MRflush].op = CHANRCV; + alts[NMR].op = CHANEND; + + switch(alt(alts)){ + case MRdata: + break; + case MRflush: + filsyscancel(x); + return; + } + + /* received data */ + x->flushtag = -1; + if(x->flushing){ + recv(x->flushc, nil); /* wake up flushing xfid */ + recv(mrm.cm, nil); /* wake up window and toss data */ + filsyscancel(x); + return; + } + qlock(&x->active); + recv(mrm.cm, &ms); + c = 'm'; + if(w->resized) + c = 'r'; + n = sprint(buf, "%c%11d %11d %11d %11ld ", c, ms.xy.x, ms.xy.y, ms.buttons, ms.msec); + w->resized = 0; + fc.data = buf; + fc.count = min(n, cnt); + filsysrespond(x->fs, x, &fc, nil); + qunlock(&x->active); + break; + + case Qcursor: + filsysrespond(x->fs, x, &fc, "cursor read not implemented"); + break; + + /* The algorithm for snarf and text is expensive but easy and rarely used */ + case Qsnarf: + getsnarf(); + if(nsnarf) + t = runetobyte(snarf, nsnarf, &n); + else { + t = nil; + n = 0; + } + goto Text; + + case Qtext: + t = wcontents(w, &n); + goto Text; + + Text: + if(off > n){ + off = n; + cnt = 0; + } + if(off+cnt > n) + cnt = n-off; + fc.data = t+off; + fc.count = cnt; + filsysrespond(x->fs, x, &fc, nil); + free(t); + break; + + case Qwdir: + t = estrdup(w->dir); + n = strlen(t); + goto Text; + + case Qwinid: + n = sprint(buf, "%11d ", w->id); + t = estrdup(buf); + goto Text; + + + case Qwinname: + n = strlen(w->name); + if(n == 0){ + filsysrespond(x->fs, x, &fc, "window has no name"); + break; + } + t = estrdup(w->name); + goto Text; + + case Qwindow: + i = w->i; + if(i == nil || Dx(w->screenr)<=0){ + filsysrespond(x->fs, x, &fc, Enowindow); + return; + } + r = w->screenr; + goto caseImage; + + case Qscreen: + i = display->image; + if(i == nil){ + filsysrespond(x->fs, x, &fc, "no top-level screen"); + break; + } + r = i->r; + /* fall through */ + + caseImage: + if(off < 5*12){ + n = sprint(buf, "%11s %11d %11d %11d %11d ", + chantostr(cbuf, screen->chan), + i->r.min.x, i->r.min.y, i->r.max.x, i->r.max.y); + t = estrdup(buf); + goto Text; + } + t = malloc(cnt); + fc.data = t; + n = readwindow(i, t, r, off, cnt); /* careful; fc.count is unsigned */ + if(n < 0){ + buf[0] = 0; + errstr(buf, sizeof buf); + filsysrespond(x->fs, x, &fc, buf); + }else{ + fc.count = n; + filsysrespond(x->fs, x, &fc, nil); + } + free(t); + return; + + case Qwctl: /* read returns rectangle, hangs if not resized */ + if(cnt < 4*12){ + filsysrespond(x->fs, x, &fc, Etooshort); + break; + } + x->flushtag = x->tag; + + alts[WCRdata].c = w->wctlread; + alts[WCRdata].v = &cwrm; + alts[WCRdata].op = CHANRCV; + alts[WCRflush].c = x->flushc; + alts[WCRflush].v = nil; + alts[WCRflush].op = CHANRCV; + alts[NMR].op = CHANEND; + + switch(alt(alts)){ + case WCRdata: + break; + case WCRflush: + filsyscancel(x); + return; + } + + /* received data */ + x->flushtag = -1; + c1 = cwrm.c1; + c2 = cwrm.c2; + t = malloc(cnt+1); /* be sure to have room for NUL */ + pair.s = t; + pair.ns = cnt+1; + send(c1, &pair); + if(x->flushing){ + recv(x->flushc, nil); /* wake up flushing xfid */ + recv(c2, nil); /* wake up window and toss data */ + free(t); + filsyscancel(x); + return; + } + qlock(&x->active); + recv(c2, &pair); + fc.data = pair.s; + if(pair.ns > cnt) + pair.ns = cnt; + fc.count = pair.ns; + filsysrespond(x->fs, x, &fc, nil); + free(t); + qunlock(&x->active); + break; + + default: + fprint(2, "unknown qid %d in read\n", qid); + sprint(buf, "unknown qid in read"); + filsysrespond(x->fs, x, &fc, buf); + break; + } +} |