#include <9pm/u.h> #include <9pm/libc.h> #include <9pm/draw.h> #include <9pm/memdraw.h> #include <9pm/memlayer.h> #include <9pm/cursor.h> #include "dat.h" #include "fns.h" #include "screen.h" /* * So that the device can be mounted on /mnt to yield /mnt/wsys/wsys/n/files, * we present all the files in #w/wsys/wsys. Rio has the luxury of having a * /mnt/wsys on which to mount, so it only presents a single wsys directory. * * Also unlike rio, we don't present any /mnt/wsys/cons etc. files. Instead, * we expect that the code to create a new window will bind the appropriate * /mnt/wsys/wsys/n directory before /mnt/wsys. * * I'd like to be able to access /mnt/wsys/wsys/n/text from other windows. * One way to do so would be to have the device manage the window text like rio, * but that would require porting almost all the draw and frame libraries to memdraw, * which is likely too much pain. Instead, I think we'll make the device text file * writable and have a user-level program that presents a single rio window. * (Perhaps just modify rio.) */ enum { Qmnt = 0, Qwsys, Qwsys1, Qn, Qnew, Qcons, Qconsctl, Qcursor, Qlabel, Qmouse, Qsnarf, Qtext, Qwctl, Qwdir, Qwindow, Qwinid, Qwinname, }; /* * Qid path is: * 8 bits of file type (qids above), * 16 bits of window index */ #define QSHIFT 8 #define QID(q) ((((ulong)(q).path)&0x000000FF)>>0) #define WSYS(q) ((((ulong)(q).path)&0x00FFFF00)>>QSHIFT) #define PATH(t, n) (((n)<qid)){ case Qtopdir: case Qwsys: mkqid(&q, Qtopdir, 0, QTDIR); devdir(c, q, "#w", 0, eve, 0500, dp); break; case Qwsys1: mkqid(&q, Qwsys, 0, QTDIR); devdir(c, q, "wsys", 0, eve, 0500, dp); break; case Qn: mkqid(&q, Qwsys1, 0, QTDIR); devdir(c, q, "wsys", 0, eve, 0500, dp); break; default: panic("wsyswalk %llux", c->qid.path); } return 1; } /* * Top level directory contains wsys */ t = QID(c->qid); if(t == Qtopdir){ switch(s){ case 0: mkqid(&q, Qwsys, 0, QTDIR); devdir(c, q, "wsys", 0, eve, 0500, dp); break; default: return -1; } return 1; } /* * Second level contains wsys */ if(t == Qwsys){ switch(s){ case 0: mkqid(&q, Qwsys1, 0, QTDIR); devdir(c, q, "wsys", 0, eve, 0500, dp); break; default: return -1; } return 1; } /* * Third level contains new and `n' directories. */ if(t == Qwsys1){ if(s == 0){ mkqid(&q, Qnew, 0, QTFILE); devdir(c, q, "new", 0, eve, 0444, dp); }else if(s-1 < nwin){ s--; w = win[s]; if(w == nil) return 0; snprint(up->genbuf, "%d", w->id); mkqid(&q, PATH(Qn, s), 0, QTDIR); devdir(c, q, up->genbuf, 0, eve, 0555, dp); }else return -1; return 1; } /* * Fourth level contains window files. */ n = WSYS(c->qid); if(n >= nwin || (w = win[n]) == nil) return 0; if(s > nelem(wintab)) return -1; mkqid(&q, PATH(wintab[s].type, n), 0, QTFILE); devdir(c, q, wintab[s].name, 0, eve, wintab[s].perm, dp); return 1; } static Chan* wsysattach(char *spec) { return devattach('i', spec); } static Walkqid* wsyswalk(Chan *c, Chan *nc, char **name, int nname) { return devwalk(c, nc, name, nname, 0, 0, wsysgen); } static int wsysstat(Chan *c, uchar *db, int n) { return devstat(c, db, n, 0, 0, wsysgen); } static Chan* wsysopen(Chan *c, int omode) { int n; Window *w; c = devopen(c, omode, 0, 0, wsysgen); if(waserror()){ c->flag &= ~COPEN; nexterror(); } switch(QID(c->qid)){ case Qnew: n = mkwsys(); if(n < 0) error(Enodev); c->qid.path = PATH(Qwinid, n); break; } w = windowbyqid(c->qid); switch(QID(c->qid)){ case Qconsctl: if(!canlock(&w->consctlopen)) error(Einuse); break; case Qmouse: if(!canlock(&w->mouseopen)) error(Einuse); break; case Qwctl: if((omode&3) != OWRITE){ if(!canlock(&w->wctlopenread)) error(Einuse); } break; } incref(&w->ref); return c; } static void wsysclose(Chan *c) { Window *w; if(c->qid.type&QTDIR) return; if(!(c->flag & COPEN)) return; /* might error, that's ok */ w = windowbyqid(c->qid); switch(QID(c->qid)){ case Qconsctl: if(w->holding && --w->holding==0) wnohold(w); if(w->rawing && --w->rawing==0) wnoraw(w); unlock(&w->consctlopen); break; case Qcursor: qlock(&w->lk); w->cursor = arrow; wsetcursor(w); qunlock(&w->lk); break; case Qmouse: unlock(&w->mouseopen); break; case Qsnarf: qlock(&w->lk); wsetsnarf(w); qunlock(&w->lk); break; case Qwctl: if(c->omode != OWRITE) unlock(&w->wctlopen); break; } closewsys(w, WSYS(c->qid)); } static long wsysread(Chan *c, void *a, long n, vlong off) { char *ca, buf[256]; int n, nresize; Kmouse m; Window *w; if(c->qid.type & QTDIR) return devdirread(c, a, n, 0, 0, wsysgen); w = windowbyqid(c->qid); switch(QID(c->qid)){ default: error(Egreg); case Qcons: case Qcursor: if(off != 0) return 0; if(n < 2*4+2*2*16) error(Eshort); n = 2*4+2*2*16; qlock(&w->lk); BPLONG(p+0, w->cursor.offset.x); BPLONG(p+4, w->cursor.offset.y); memmove(p+8, w->cursor.clr, 2*16); memmove(p+40, w->cursor.set, 2*16); qunlock(&w->lk); return n; case Qlabel: qlock(&w->lk); if(w->label == nil) n = 0; else n = readstr(off, a, n, w->label); qunlock(&w->lk); return n; case Qmouse: qlock(&w->mouse.read); while(mousechanged(w) == 0) rendsleep(&w->mouse.r, mousechanged, w); w->mouse.qfull = 0; if(w->mouse.ri != w->mouse.wi) { m = w->mouse.queue[w->mouse.ri]; if(++w->mouse.ri == nelem(w->mouse.queue)) w->mouse.ri = 0; } else { lock(&w->mouse.lk); m = w->mouse.m; unlock(&w->mouse.lk); } sprint(buf, "m%11d %11d %11d %11lud", m.xy.x, m.xy.y, m.buttons, m.msec); nresize = w->mouse.nresize; if(w->mouse.mresize < nresize){ w->mouse.mresize = nresize; buf[0] = 'r'; } w->mouse.lastcounter = m.counter; if(n > 1+4*12) n = 1+4*12; memmove(va, buf, n); qunlock(&w->mouse.read); return n; case Qsnarf: return snarfread(c, a, n, off); case Qtext: error(Egreg); case Qwctl: error(Egreg); case Qwdir: qlock(&w->lk); if(w->wdir == nil) n = 0; else n = readstr(off, a, n, w->wdir); qunlock(&w->lk); return n; case Qwindow: error(Egreg); case Qwinid: snprint(up->genbuf, sizeof up->genbuf, "%d", w->id); return readstr(off, a, n, up->genbuf); case Qwinname: return readstr(off, a, n, w->name); } } static long wsyswrite(Chan *c, void *a, long n, vlong off) { char buf[256], *p; int n, nresize; Kmouse m; Point pt; Window *w; if(c->qid.type & QTDIR) return devdirread(c, a, n, 0, 0, wsysgen); w = windowbyqid(c->qid); switch(QID(c->qid)){ default: error(Egreg); case Qcons: case Qcursor: qlock(&w->lk); if(n < 2*4+2*2*16) w->cursor = arrow; n = 0; else{ n = 2*4+2*2*16; w->cursor.offset.x = BGLONG(p+0); w->cursor.offset.y = BGLONG(p+4); memmove(w->cursor.clr, p+8, 2*16); memmove(w->cursor.set, p+40, 2*16); } winsetcursor(w); qunlock(&w->lk); return n; case Qlabel: if(off != 0) error("non-zero offset writing label"); qlock(&w->lk); free(w->label); w->label = smalloc(n+1); memmove(w->label, a, n); w->label[n] = 0; winsetlabel(w); qunlock(&w->lk); return n; case Qmouse: if(n > sizeof buf - 1) n = sizeof buf - 1; memmove(buf, va, n); buf[n] = 0; p = 0; pt.x = strtoul(buf+1, &p, 0); if(p == 0) error(Eshort); pt.y = strtoul(p, &p, 0); if(p == 0) error(Eshort); if(ptinrect(pt, w->image->r)) movecursor(w, pt); return n; case Qsnarf: return snarfwrite(c, a, n, off); case Qtext: error(Egreg); case Qwctl: error(Egreg); case Qwdir: qlock(&w->lk); p = a; if(n>0 && p[n-1] == '\n') n--; if(n == 0) return 0; /* * Problem: programs like dossrv, ftp produce illegal UTF; * we must cope by converting it first. */ snprint(buf, sizeof buf, "%.*s", n, p); if(buf[0] == '/'){ free(w->dir); w->dir = smalloc(strlen(buf) + 1); strcpy(w->dir, buf); }else{ p = smalloc(strlen(w->dir) + 1 + strlen(buf) + 1); sprint(p, "%s/%s", w->dir, buf); free(w->dir); w->dir = cleanname(p); } qunlock(&w->lk); return n; } } Dev devwsys = { 'w', "wsys", devreset, wsysattach, wsyswalk, wsysstat, wsysopen, devcreate, wsysclose, wsysread, devbread, wsyswrite, devbwrite, devremove, devwstat, };