#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)<<QSHIFT)|(t))
static struct {
char *name;
int type;
int perm;
} wintab[] = {
"cons", Qcons, 0600,
"consctl", Qconsctl, 0200,
"cursor", Qcursor, 0600,
"label", Qlabel, 0600,
"mouse", Qmouse, 0600,
"snarf", Qsnarf, 0600,
"text", Qtext, 0600,
"wctl", Qwctl, 0600,
"wdir", Qwdir, 0600,
"window", Qwindow, 0400,
"winid", Qwinid, 0400,
"winname", Qinname, 0400,
};
static int
wsysgen(Chan *c, char *name, Dirtab *tab, int ntab, int s, Dir *dp)
{
int i, n, t;
Qid q;
Window *w;
USED(tab);
USED(ntab);
USED(name);
q.path = 0;
if(s == DEVDOTDOT){
switch(QID(c->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,
};
syntax highlighted by Code2HTML, v. 0.9.1