Two attached files: - 9p-srv.c is a devdraw(1) version which uses 9pclient(3) to talk with Plan 9-like devices and use them for its windows.
- p9p.patch is a patch that has to be applied to the p9p tree to use this devdraw version. The drawing device expects to find draw(3), cons(3) and mouse(3) files in the $WSYS directory, as can be found in /dev in a Plan 9 terminal (including 9vx or drawterm) or /mnt/wsys when using rio. Then, those devices are used to run p9p programs. I wrote 9p-srv as an experiment to use wsys(4) [1] with p9p applications, but have found it quite useful to open p9p programs - like 9term or sam - in rio windows (for example, from 9vx). Although it could be argued that this is not the most efficient thing to do writing it was quite funny, and much easier than expected. Installation (once the patch has been applied and p9p built): cp 9p-srv.c $PLAN9/src/cmd/devdraw cd $PLAN9/src/cmd/devdraw 9c 9p-srv.c && 9l -o devdraw.9p 9p-srv.o cp devdraw.9p $PLAN9/bin Now, to run 9term in a rio window: # From 9vx's rio: ; mount $wsys /n/w new ; bind -a '#i' /n/w ; aux/listen1 -t 'tcp!*!12345' /bin/exportfs -r /n/w # From unix: $ WSYS='tcp!127.0.0.1!12345' $ DEVDRAW=devdraw.9p $ 9term These files are also included with wsys(4), in the util directory (instructions are in the README). To use the drawing device with wsys(4) just set WSYS to "unix!/tmp/ns.$USERNAME.$DISPLAY/wsys". However, it is not working so well as with rio. The reason is probably some bug in wsys, and not in 9p-srv. I hope to fix it at some point, but is not an easy bug to track. If (after some more testing) the 9p drawing device could be included with p9p that would be great. If it has to stay in the wsys repository that is fine too but, could the p9p patch be applied at least? The two small changes shouldn't be a problem for the other versions of devdraw. Comments are welcomed. [1] http://bitbucket.org/yiyus/devwsys-prev/ (there is also a blog about the development of wsys(4) at http://summerofdevdraw.blogspot.com/ and a WIP short paper is included in the proceedings of the last iwp9). -- - yiyus || JGL .
/* * Window system protocol server. * Use thread(3) and 9pclient(3) to connect * to a wsys service as described in rio(4). */ #include <u.h> #include <libc.h> #include <thread.h> #include <9pclient.h> #include <draw.h> #include <memdraw.h> #include <memlayer.h> #include <keyboard.h> #include <mouse.h> #include <cursor.h> #include <drawfcall.h> #define STACK (32*1024) void runmsg(Wsysmsg*); void replymsg(Wsysmsg*); void kbdthread(void*); void mousethread(void*); int chatty = 0; int drawsleep; int fd; Channel *kbdchan; Channel *mousechan; void usage(void) { fprint(2, "usage: devdraw (don't run directly)\n"); threadexitsall(0); } void bell(void *v, char *msg) { if(strcmp(msg, "alarm") == 0) drawsleep = drawsleep ? 0 : 1000; noted(NCONT); } void threadmain(int argc, char **argv) { char *addr, *defaddr, *ns; uchar buf[4], *mbuf; int nmbuf, n, nn; Ioproc *io; Wsysmsg m; /* * Move the protocol off stdin/stdout so that * any inadvertent prints don't screw things up. */ dup(0, 3); dup(1, 4); close(0); close(1); open("/dev/null", OREAD); open("/dev/null", OWRITE); fmtinstall('H', encodefmt); fmtinstall('W', drawfcallfmt); ARGBEGIN{ case 'd': chatty9pclient++; break; case 'D': chatty++; break; default: usage(); }ARGEND /* * Ignore arguments. They're only for good ps -a listings. */ notify(bell); /* * Connect to 9P server defined by $WSYS * or unix!$NAMESPACE/wsys. */ addr = getenv("WSYS"); defaddr = nil; if(addr == 0){ ns = getns(); if(ns == nil) sysfatal("no name space"); defaddr = smprint("unix!%s/wsys", ns); addr = defaddr; } fd = dial(addr, 0, 0, 0); if(fd < 0) sysfatal("dial %s: %r", addr); if(addr == defaddr) free(addr); mbuf = nil; nmbuf = 0; io = ioproc(); while((n = ioread(io, 3, buf, 4)) == 4){ GET(buf, n); if(n > nmbuf){ free(mbuf); mbuf = malloc(4+n); if(mbuf == nil) sysfatal("malloc: %r"); nmbuf = n; } memmove(mbuf, buf, 4); nn = readn(3, mbuf+4, n-4); if(nn != n-4) sysfatal("eof during message"); /* pick off messages one by one */ if(convM2W(mbuf, nn+4, &m) <= 0) sysfatal("cannot convert message"); if(chatty) fprint(2, "<- %W\n", &m); runmsg(&m); } threadexitsall(0); } void replyerror(Wsysmsg *m) { char err[256]; rerrstr(err, sizeof err); m->type = Rerror; m->error = err; replymsg(m); } /* * Handle a single wsysmsg. * Might queue for later (kbd, mouse read) */ void runmsg(Wsysmsg *m) { uchar buf[65536]; int n; CFid *f; Rectangle r; static int border; static int id = -1; static CFid *fcons, *fmouse, *fctl, *fdata, *frddraw; static CFsys *fsys; switch(m->type){ case Tinit: sprint((char*)buf, "new %s", m->winsize); if((fsys = fsmount(fd, (char*)buf)) == nil) sysfatal("fsmount: %r"); fcons = fsopen(fsys, "cons", OREAD); fmouse = fsopen(fsys, "mouse", ORDWR); f = fsopen(fsys, "label", OWRITE); fsprint(f, m->label); fsclose(f); /* * Read keyboard and mouse events * from different threads. * Use a buffered chan as tag queue. */ kbdchan = chancreate(sizeof(uchar), 12); mousechan = chancreate(sizeof(uchar), 12); f= fsopen(fsys, "consctl", OWRITE); fsprint(f, "rawon"); threadcreate(kbdthread, fcons, STACK); threadcreate(mousethread, fmouse, STACK); /* * Open draw(3) files and register * image named winname */ fctl = fsopen(fsys, "draw/new", ORDWR); fsread(fctl, buf, 12*12); n = atoi((char*)buf); sprint((char*)buf, "draw/%d/data", n); fdata = fsopen(fsys, (char*)buf, ORDWR); replymsg(m); break; case Trdmouse: if(nbsend(mousechan, &m->tag) == 0) sysfatal("too many queued mouse reads"); break; case Trdkbd: if(nbsend(kbdchan, &m->tag) == 0) sysfatal("too many queued mouse reads"); break; case Tmoveto: fsprint(fmouse, "m %11d %11d %11d %11d", m->mouse.xy.x, m->mouse.xy.y, m->mouse.buttons, m->mouse.msec); replymsg(m); break; case Tcursor: f = fsopen(fsys, "cursor", OWRITE); if(m->arrowcursor) fswrite(f, buf, 0); else{ PUT(&buf[0], m->cursor.offset.x); PUT(&buf[4], m->cursor.offset.y); for(n = 0; n < 32; n++){ buf[2*4+n] = m->cursor.clr[n]; buf[2*4+32+n] = m->cursor.set[n]; } fswrite(f, buf, 72); } fsclose(f); replymsg(m); break; case Tbouncemouse: /* Ignore */ replymsg(m); break; case Tlabel: f = fsopen(fsys, "label", OWRITE); fsprint(f, m->label); fsclose(f); replymsg(m); break; case Trdsnarf: f = fsopen(fsys, "snarf", OREAD); fsread(f, buf, sizeof buf); fsclose(f); m->snarf = strdup((char*)buf); replymsg(m); break; case Twrsnarf: f = fsopen(fsys, "snarf", OWRITE); fsprint(f, m->snarf); fsclose(f); replymsg(m); break; case Trddraw: if((n = fsread(frddraw, buf, sizeof buf)) < 0){ replyerror(m); break; } /* * If it is not a "noborder" image lie to p9p's libdraw saying * that the image is smaller, to respect the window border. */ if(frddraw == fctl){ if(border){ r = Rect( atoi((char*)&buf[4*12]), atoi((char*)&buf[5*12]), atoi((char*)&buf[6*12]), atoi((char*)&buf[7*12]) ); r = insetrect(r, Borderwidth); sprint((char*)&buf[4*12], "%11d %11d %11d %11d", r.min.x, r.min.y, r.max.x, r.max.y); } } m->count = n; m->data = buf; frddraw = fdata; replymsg(m); break; case Twrdraw: /* * In the drawfcall protocol: * J: Install screen image as image 0 * I: get "screen" (image 0) information * * Instead, we read the screen image name * from winname and associate it to a devdraw * image with the command 'n', which we * will free after ctl is read. */ SET(n); if(m->count == 2 && m->data[0] == 'J' && m->data[1] == 'I'){ id = 1; buf[0] = 'f'; BPLONG(&buf[1], id); fswrite(fdata, buf, 1+4); f = fsopen(fsys, "winname", OREAD); buf[0] = 'n'; n = fsread(f, &buf[1+4+1], 64); border = (strncmp((char*)&buf[1+4+1], "noborder", 8) != 0); buf[1+4] = n; fsclose(f); BPLONG(&buf[1], id); n = fswrite(fdata, buf, 1+4+1+n); frddraw = fctl; } else n = fswrite(fdata, m->data, m->count); if(n < 0) replyerror(m); else replymsg(m); break; case Ttop: f = fsopen(fsys, "wctl", OWRITE); fsprint(f, "top"); fsclose(f); replymsg(m); break; case Tresize: f = fsopen(fsys, "wctl", OWRITE); fsprint(f, "resize %d %d", Dx(m->rect), Dy(m->rect)); fsclose(f); replymsg(m); break; } } /* * Reply to m. */ QLock replylock; void replymsg(Wsysmsg *m) { int n; static uchar *mbuf; static int nmbuf; /* T -> R msg */ if(m->type%2 == 0) m->type++; if(chatty) fprint(2, "-> %W\n", m); /* copy to output buffer */ n = sizeW2M(m); qlock(&replylock); if(n > nmbuf){ free(mbuf); mbuf = malloc(n); if(mbuf == nil) sysfatal("out of memory"); nmbuf = n; } convW2M(m, mbuf, n); if(write(4, mbuf, n) != n) sysfatal("write: %r"); qunlock(&replylock); } void kbdthread(void *arg) { CFid *f; Rune r; Wsysmsg m; f = arg; m.type = Rrdkbd; while(1){ recv(kbdchan, &m.tag); r = 0; if(fsread(f, &r, 2) < 1) sysfatal("short keyboard read"); m.rune = r; replymsg(&m); } } void mousethread(void *arg) { char buf[49]; CFid *f; Wsysmsg m; f = arg; m.type = Rrdmouse; while(1){ recv(mousechan, &m.tag); if(fsread(f, buf, 49) < 49) sysfatal("short mouse read"); m.resized = 0; if(buf[0] == 'r') m.resized = 1; m.mouse.xy.x = atoi(buf+1+0*12); m.mouse.xy.y = atoi(buf+1+1*12); m.mouse.buttons = atoi(buf+1+2*12); m.mouse.msec = atoi(buf+1+3*12); replymsg(&m); } }
diff -r 16d4081236af src/libdraw/init.c --- a/src/libdraw/init.c Wed Apr 27 13:18:07 2011 -0400 +++ b/src/libdraw/init.c Wed Nov 02 18:07:13 2011 +0100 @@ -145,7 +145,7 @@ } image->display = d; - image->id = 0; + image->id = atoi(info+1*12); image->chan = strtochan(info+2*12); image->depth = chantodepth(image->chan); image->repl = atoi(info+3*12); @@ -206,6 +206,12 @@ return nil; } disp->srvfd = -1; + /* + * The image id 1 is used by devdraw.9p to identify + * the image associated to winname. Since the counter + * is preincremented before use, 1 is a safe value. + */ + disp->imageid = 1; image = nil; if(0){ Error2: