ref: 424638912db3db1dc88d7b91b8f9d38c11e920b7
parent: 031d7dabfe30a8bf74e5ca5db7f7093fddf07bd1
author: Sigrid Solveig Haflínudóttir <[email protected]>
date: Wed Aug 24 16:47:30 EDT 2022
rewrite riow (in C this time) to use /dev/kbdtap
--- a/9front.diff
+++ /dev/null
@@ -1,117 +1,0 @@
-diff -r ab6ef2653d12 sys/src/cmd/rio/dat.h
---- a/sys/src/cmd/rio/dat.h Mon Dec 07 15:15:02 2020 +0100
-+++ b/sys/src/cmd/rio/dat.h Mon Dec 07 15:56:46 2020 +0100
-@@ -346,11 +346,15 @@
- char *startdir;
- int sweeping;
- int wctlfd;
-+int gkbdfd;
-+Channel* gkbdc;
- char srvpipe[];
- char srvwctl[];
-+char srvgkbd[];
- int errorshouldabort;
- int menuing; /* menu action is pending; waiting for window to be indicated */
- int snarfversion; /* updated each time it is written */
- int messagesize; /* negotiated in 9P version setup */
- int shiftdown;
-+int mod4down;
- int debug;
-diff -r ab6ef2653d12 sys/src/cmd/rio/fsys.c
---- a/sys/src/cmd/rio/fsys.c Mon Dec 07 15:15:02 2020 +0100
-+++ b/sys/src/cmd/rio/fsys.c Mon Dec 07 15:56:46 2020 +0100
-@@ -50,6 +50,7 @@
-
- char srvpipe[64];
- char srvwctl[64];
-+char srvgkbd[64];
-
- static Xfid* filsysflush(Filsys*, Xfid*, Fid*);
- static Xfid* filsysversion(Filsys*, Xfid*, Fid*);
-@@ -115,6 +116,34 @@
- return 0;
- }
-
-+void
-+gkbdproc(void *v)
-+{
-+ char *s;
-+ int n, eofs;
-+ Channel *c;
-+
-+ threadsetname("GKBDPROC");
-+ c = v;
-+
-+ eofs = 0;
-+ for(;;){
-+ if((s = recvp(c)) == nil)
-+ break;
-+ n = write(gkbdfd, s, strlen(s)); /* room for \0 */
-+ free(s);
-+ if(n < 0)
-+ break;
-+ if(n == 0){
-+ if(++eofs > 20)
-+ break;
-+ continue;
-+ }
-+ eofs = 0;
-+ }
-+ close(gkbdfd);
-+}
-+
- Filsys*
- filsysinit(Channel *cxfidalloc)
- {
-@@ -142,6 +171,12 @@
- post(srvwctl, "wctl", p0);
- close(p0);
-
-+ if(cexecpipe(&p0, &gkbdfd) < 0)
-+ goto Rescue;
-+ snprint(srvgkbd, sizeof(srvgkbd), "/srv/riogkbd.%s.%lud", fs->user, (ulong)getpid());
-+ post(srvgkbd, "gkbd", p0);
-+ close(p0);
-+
- /*
- * Start server processes
- */
-@@ -150,6 +185,12 @@
- error("wctl channel");
- proccreate(wctlproc, c, 4096);
- threadcreate(wctlthread, c, 4096);
-+
-+ gkbdc = chancreate(sizeof(char*), 0);
-+ if(gkbdc == nil)
-+ error("gkbd channel");
-+ proccreate(gkbdproc, gkbdc, 4096);
-+
- proccreate(filsysproc, fs, 10000);
-
- /*
-diff -r ab6ef2653d12 sys/src/cmd/rio/rio.c
---- a/sys/src/cmd/rio/rio.c Mon Dec 07 15:15:02 2020 +0100
-+++ b/sys/src/cmd/rio/rio.c Mon Dec 07 15:56:46 2020 +0100
-@@ -340,13 +340,20 @@
- keyboardthread(void*)
- {
- char *s;
-+ int mod4downnew;
-
- threadsetname("keyboardthread");
-
-+ mod4downnew = 0;
- while(s = recvp(kbdchan)){
-- if(*s == 'k' || *s == 'K')
-+ if(*s == 'k' || *s == 'K'){
- shiftdown = utfrune(s+1, Kshift) != nil;
-- if(input == nil || sendp(input->ck, s) <= 0)
-+ mod4downnew = utfrune(s+1, Kmod4) != nil;
-+ }
-+ if(mod4down || mod4downnew){
-+ mod4down = mod4downnew;
-+ sendp(gkbdc, s);
-+ }else if(input == nil || sendp(input->ck, s) <= 0)
- free(s);
- }
- }
--- a/README.md
+++ b/README.md
@@ -2,9 +2,8 @@
Some kind of window management experiments with rio. What does it do?
It gives you virtual desktops to move window around to, by pressing
-keys, like i3. The window management code is written in `rc` and is done
-by reading and writing `/dev/wsys` for the most part. There are minimal
-changes to `rio` (to provide an additional `/srv/riogkbd.*` file).
+keys, like i3. The window management is done by reading and writing
+`/dev/wsys` for the most part.
*No guarantees, use at your own risk and blah. This isn't supposed to
work with drawterm.*
@@ -23,23 +22,37 @@
## Installation and usage
-Run `mk install` in this repo. Apply `9front.diff` and rebuild/reinstall `rio`:
+Run `mk install` in this repo.
- cat 9front.diff | @{cd /sys/src/cmd/rio && ape/patch -p5 && mk install}
+`riow` uses `/dev/kbdtap` of rio and should be placed as *last* in the chain of
+`kbdtap` users (after `ktrans` and/or `reform/shortcuts`).
-As a matter of fact, you can copy the original `rio` directory
-somewhere, apply the patch, and install under your user's `bin`
-instead.
+Running it alone:
-To start `riow`, either through `riostart`, or by hand:
+ riow </dev/kbdtap >/dev/kbdtap >[3]/dev/null
- window -scroll riow
+Note that the current desktop number is printed to fd 3.
-If you're *NOT* using [bar](https://git.sr.ht/~ft/bar), run with
-`-hide` as well.
+Running with [bar](https://git.sr.ht/~ft/bar) is recommended. For
+example, with additional `zuke(1)` controls and `reform/shortcuts`:
-Modify `riow` to your own needs.
+ ; cat $home/rc/bin/Bar
+ #!/bin/rc
+ rfork ne
+
+ fn bar {
+ sed -u 's/$/ │ ⏮ │ ⏯ │ ⏭/g' \
+ | /bin/bar \
+ | awk -v 'c=plumb -d audio ''key ' '
+ /⏮/{system(c"<''")}
+ /⏯/{system(c"p''")}
+ /⏭/{system(c">''")}
+ ' >[2]/dev/null
+ }
+ </dev/kbdtap reform/shortcuts | riow >/dev/kbdtap |[3] bar
+This can be running on rio startup by adding `window Bar` to your `riostart` script.
+
## Keys
```
@@ -49,14 +62,11 @@
Mod4-[0..9] switch to a specific virtual desktop
Mod4-shift-[0..9] move the current window to a specific virtual desktop
-Mod4-[↑↓←→] move the window (big steps)
-Mod4-shift-[↑↓←→] move the window (small steps)
+Mod4-[↑↓←→] move the window (small steps)
+Mod4-ctrl-[↑↓←→] move the window (big steps)
-Mod4-ctrl-[↑↓←→] drag bottom-right of the window (big steps)
-Mod4-ctrl-shift-[↑↓←→] drag bottom-right of the window (small steps)
-
-Mod4-alt-[↑↓←→] drag top-left of the window (big steps)
-Mod4-alt-shift-[↑↓←→] drag top-left of the window (small steps)
+Mod4-shift-[↑↓←→] resize the window (small steps)
+Mod4-shift-ctrl-[↑↓←→] resize the window (big steps)
```
## Extras
--- a/gkbd.c
+++ /dev/null
@@ -1,72 +1,0 @@
-#include <u.h>
-#include <libc.h>
-#include <keyboard.h>
-#include <ctype.h>
-
-static struct {
- Rune r;
- char *k;
-}keys[] = {
- {'\t', "tab"},
- {0x0a, "enter"},
- {0x20, "space"},
- {Kalt, "alt"},
- {Kctl, "ctl"},
- {Kdel, "del"},
- {Kdown, "down"},
- {Kesc, "esc"},
- {Kmod4, "mod4"},
- {Kleft, "left"},
- {Kright, "right"},
- {Kshift, "shift"},
- {Kup, "up"},
-};
-
-void
-main(int argc, char **argv)
-{
- char k[32], *s, out[128];
- Rune r;
- int n, i;
-
- USED(argc); USED(argv);
-
- for(;;){
- s = k;
- if((n = read(0, k, sizeof(k)-1)) <= 0)
- break;
- k[n] = 0;
- n = 0;
- out[n++] = *s++;
- out[n++] = ' ';
- while(*s){
- s += chartorune(&r, s);
- if(r == Runeerror){
- n = -1;
- break;
- }
-
- for(i = 0; i < nelem(keys); i++){
- if(keys[i].r == r){
- n += sprint(out+n, "%s ", keys[i].k);
- break;
- }
- }
-
- if(i >= nelem(keys)){
- if(isprint(r))
- n += sprint(out+n, "%C ", r);
- else
- n += sprint(out+n, "0x%x ", r);
- }
- }
-
- if(n > 0){
- out[n-1] = '\n';
- if(write(1, out, n) != n)
- break;
- }
- }
-
- exits(nil);
-}
--- a/mkfile
+++ b/mkfile
@@ -1,16 +1,11 @@
</$objtype/mkfile
-TARG=gkbd
+TARG=riow
BIN=/$objtype/bin
OFILES=\
- gkbd.$O\
+ riow.$O\
-default:V: all
+default:V: all
</sys/src/cmd/mkone
-
-install:V: $BIN/$TARG /rc/bin/riow
-
-/rc/bin/riow: riow
- cp $prereq $target
--- a/riow
+++ /dev/null
@@ -1,213 +1,0 @@
-#!/bin/rc
-rfork ne
-
-# these are shown on every desktop
-sticky=(bar 'cat clock' clock desktop faces kbmap stats winwatch)
-
-# window move/resize step (in pixels)
-step=96
-stepsmall=8
-
-alt=0
-ctl=0
-shift=0
-curdesktop=1
-
-fn scrsize {
- s=`{dd -quiet 1 -if /dev/screen -bs 60 -count 1 >[2]/dev/null}
- screenw=$s(4)
- screenh=$s(5)
-}
-
-fn winread {
- dd -quiet 1 -if $1/wctl -bs 120 -count 1 >[2]/dev/null
-}
-
-fn winlabel {
- cat $1/label
-}
-
-fn curwindow {
- cur=''
- for(f in /dev/wsys/*){
- a=`{winread $f}
- if(~ $a(5) current)
- cur=`{basename $f}
- }
- echo -n $cur
-}
-
-fn togglefullscreen {
- winid=`{curwindow}
- if(! ~ $#winid 0){
- f=/dev/wsys/^$winid
- a=`{winread $f}
- scrsize
- if(~ $a(1) 0 && ~ $a(2) 0 && ~ $a(3) $screenw && ~ $a(4) $screenh){
- if(test -f /env/winsize_^$winid)
- echo resize -r `{cat /env/winsize_^$winid} >$f/wctl >[2]/dev/null
- }
- if not {
- echo -n $a(1 2 3 4) >/env/winsize_^$winid
- echo resize -r 0 0 9999 9999 >$f/wctl >[2]/dev/null
- }
- }
-}
-
-fn desktop {
- # find and remember the current window
- if(~ $shift 0){
- winid=`{curwindow}
- if(! ~ $#winid 0)
- echo -n $winid >/env/windeskcur_^$curdesktop
- if not
- rm -f /env/windeskcur_^$curdesktop
- }
-
- unhide=()
- hide=()
- for(f in /dev/wsys/*){
- winid=`{basename $f}
- if(! ~ `{winlabel $f} $sticky && ! test -f /env/winsticky_^$winid){
- a=`{winread $f}
-
- if(~ $shift 1){ # moving the current window elsewhere
- if(~ $a(5) current){
- echo -n $1 >/env/windesk_^$winid
- hide=($hide $f/wctl)
- }
- }
- if not {
- # go through all visible windows
- if(~ $a(6) visible){
- # assign to the current one
- echo -n $curdesktop >/env/windesk_^$winid
- # it was visible, make sure riow doesn't think otherwise later on
- rm -f /env/winhidden_^$winid
- # and hide later
- hide=($hide $f/wctl)
- }
- if not { # hidden
- windesk=`{test -f /env/windesk_^$winid && cat /env/windesk_^$winid}
- if(~ $windesk $1){ # unhide if should be shown now
- # unless it's supposed to stay hidden, of course
- if(! test -f /env/winhidden_^$winid)
- unhide=($unhide $f/wctl)
- }
- # and remember it was hidden on the desktop we're still on
- if(~ $windesk $curdesktop && ! test -f /env/winhidden_^$winid)
- touch /env/winhidden_^$winid
- }
- }
- }
- }
- if(~ $shift 0){
- winid=()
- if(test -f /env/windeskcur_^$1){
- winid=`{cat /env/windeskcur_^$1}
- if(test -d /dev/wsys/^$winid && test -f /env/windesk_^$winid && ~ `{cat /env/windesk_^$winid} $1)
- ;
- if not
- winid=()
- }
- for(w in $unhide)
- echo unhide >$w
- if(! ~ $#winid 0){
- echo top >/dev/wsys/^$winid^/wctl >[2]/dev/null
- echo current >/dev/wsys/^$winid^/wctl >[2]/dev/null
- }
- curdesktop=$1
- }
- for(w in $hide)
- echo hide >$w &
-}
-
-fn togglesticky {
- winid=`{curwindow}
- if(! ~ $#winid 0){
- if(test -f /env/winsticky_^$winid)
- rm -f /env/winsticky_^$winid
- if not
- touch /env/winsticky_^$winid
- }
-}
-
-fn arrows {
- winid=`{curwindow}
- if(! ~ $#winid 0){
- f=/dev/wsys/^$winid
- x=+0
- y=+0
- if(~ $2 0)
- s=$step
- if not
- s=$stepsmall
- ~ $1 up && y=-$s
- ~ $1 right && x=+$s
- ~ $1 down && y=+$s
- ~ $1 left && x=-$s
-
- if(~ $3 0 && ~ $4 0)
- echo move -minx $x -miny $y >> $f/wctl
- if not {
- if(~ $4 0)
- echo resize -maxx $x -maxy $y >> $f/wctl
- if not
- echo resize -minx $x -miny $y >> $f/wctl
- }
- }
-}
-
-fn handle {
- # K is key down
- # k is key up
- # c is key (and repeat) with the shift applied
-
- key='' # depending on the shift state (1 → !)
- ukey='' # stays the same regardless of the shift state
- m=$1
- state=0
- if(~ $1 k)
- state=1
- shift
- if(~ $m K && ~ $#* 0){
- alt=0
- ctl=0
- shift=0
- }
- if not while(! ~ $#* 0){
- if(~ $1 alt || ~ $1 ctl || ~ $1 shift)
- eval '$1=$state'
- if not if(~ $m c) key=$1
- if not if(~ $m k) ukey=$1
- shift
- }
- if(~ $ctl 0 && ~ $alt 0){
- if(~ $shift 0){
- if(~ $key enter) window >[2]/dev/null
- if not if(~ $key f) togglefullscreen
- if not if(~ $key s) togglesticky
- }
- if(~ $ukey [0-9] && ! ~ $ukey $curdesktop)
- desktop $ukey
- }
- arrows $key $shift $ctl $alt
-}
-
-scrsize
-
-fn work {
- echo $curdesktop
- while(s=`{read}){
- olddesktop=$curdesktop
- handle $s
- if(! ~ $olddesktop $curdesktop)
- echo $curdesktop
- }
-}
-
-fn display {
- bar || aux/statusmsg -k -w `{echo $screenw-100|bc}^,`{echo $screenh-60|bc}^,$screenw^,$screenh desktop
-}
-
-gkbd < `{echo $wsys | sed 's/rio\./riogkbd./'} | work | display
--- /dev/null
+++ b/riow.c
@@ -1,0 +1,346 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <keyboard.h>
+
+typedef struct W W;
+
+enum {
+ Mmod4 = 1<<0,
+ Mctl = 1<<1,
+ Mshift = 1<<2,
+
+ Step = 16,
+ Stepbig = 64,
+
+ Fvisible = 1<<0,
+ Fcurrent = 1<<1,
+ Fsticky = 1<<2,
+ Ffullscreen = 1<<3,
+};
+
+struct W {
+ int id;
+ Rectangle r;
+ int vd;
+ int flags;
+};
+
+static int vd = 1; /* current virtual desktop */
+static int wsys; /* rios /dev/wsys fd */
+static int mod;
+static W *ws, *wcur;
+static int wsn;
+
+static char *sticky[] = {
+ "bar", "cat clock", "clock", "faces", "kbmap", "stats", "winwatch",
+};
+
+static int
+wwctl(int id, int mode)
+{
+ char s[64];
+
+ snprint(s, sizeof(s), "/dev/wsys/%d/wctl", id);
+
+ return open(s, mode);
+}
+
+static void
+wsupdate(void)
+{
+ int i, k, n, f, seen, tn, dsn;
+ char s[256], *t[8];
+ W *newws, *w;
+ Dir *ds, *d;
+
+ seek(wsys, 0, 0);
+ if((dsn = dirreadall(wsys, &ds)) < 0)
+ sysfatal("/dev/wsys: %r");
+
+ newws = malloc(sizeof(W)*dsn);
+ wcur = nil;
+ for(i = 0, d = ds, w = newws; i < dsn; i++, d++){
+ if((f = wwctl(atoi(d->name), OREAD)) < 0)
+ continue;
+ n = read(f, s, sizeof(s)-1);
+ close(f);
+ if(n < 12)
+ continue;
+ s[n] = 0;
+ if((tn = tokenize(s, t, nelem(t))) < 6)
+ continue;
+
+ w->id = atoi(d->name);
+ w->r.min.x = atoi(t[0]);
+ w->r.min.y = atoi(t[1]);
+ w->r.max.x = atoi(t[2]);
+ w->r.max.y = atoi(t[3]);
+ w->vd = -1;
+ w->flags = 0;
+
+ /* move over the current state of the window */
+ for(k = 0, seen = 0; k < wsn; k++){
+ if(ws[k].id == w->id){
+ w->vd = ws[k].vd;
+ w->flags = ws[k].flags & ~Fvisible;
+ if(w->flags & Ffullscreen)
+ w->r = ws[k].r;
+ seen = 1;
+ break;
+ }
+ }
+
+ /* update current state */
+ for(k = 4; k < tn; k++){
+ if(strcmp(t[k], "current") == 0){
+ w->flags |= Fcurrent;
+ wcur = w;
+ }else if(strcmp(t[k], "visible") == 0){
+ w->flags |= Fvisible;
+ }
+ }
+
+ if(!seen){
+ /* not seen previously - set the new state for it */
+ w->vd = vd;
+ snprint(s, sizeof(s), "/dev/wsys/%d/label", w->id);
+ if((f = open(s, OREAD)) >= 0){
+ n = read(f, s, sizeof(s)-1);
+ close(f);
+ if(n > 0){
+ s[n] = 0;
+ for(k = 0; k < nelem(sticky); k++){
+ if(strcmp(sticky[k], s) == 0){
+ w->flags |= Fsticky;
+ break;
+ }
+ }
+ }
+ }
+ }
+ w++;
+ }
+
+ free(ds);
+ free(ws);
+ ws = newws;
+ wsn = w - newws;
+}
+
+static void
+spawn(char *s)
+{
+ if(rfork(RFPROC|RFNOWAIT|RFNAMEG|RFENVG|RFCFDG|RFREND) == 0)
+ execl("/bin/rc", "rc", s, nil);
+}
+
+static void
+togglefullscreen(void)
+{
+ int f;
+
+ if(wcur == nil || (f = wwctl(wcur->id, OWRITE)) < 0)
+ return;
+ wcur->flags ^= Ffullscreen;
+ if(wcur->flags & Ffullscreen)
+ fprint(f, "resize -r 0 0 9999 9999");
+ else
+ fprint(f, "resize -r %d %d %d %d", wcur->r.min.x, wcur->r.min.y, wcur->r.max.x, wcur->r.max.y);
+ close(f);
+}
+
+static void
+togglesticky(void)
+{
+ if(wcur != nil)
+ wcur->flags ^= Fsticky;
+}
+
+static void
+vdaction(int nvd)
+{
+ int f, wcurf;
+ W *w;
+
+ if(mod == Mmod4){
+ wcur = nil;
+ wcurf = -1;
+ for(w = ws; w < ws+wsn; w++){
+ if((f = wwctl(w->id, OWRITE)) < 0)
+ continue;
+ if(w->flags & Fvisible)
+ w->vd = vd;
+ if(w->vd != nvd && (w->flags & Fsticky) == 0)
+ fprint(f, "hide");
+ else{
+ fprint(f, "unhide");
+ if((w->flags & Fcurrent) && wcurf < 0){
+ wcur = w;
+ wcurf = f;
+ f = -1;
+ }
+ }
+ if(f >= 0)
+ close(f);
+ }
+ if(wcur != nil){
+ fprint(wcurf, "top");
+ fprint(wcurf, "current");
+ close(wcurf);
+ }
+ vd = nvd;
+ fprint(3, "%d\n", vd);
+ }else if(mod == (Mmod4 | Mshift) && wcur != nil){
+ if((f = wwctl(wcur->id, OWRITE)) >= 0){
+ fprint(f, "hide");
+ wcur->vd = nvd;
+ wcur = nil;
+ close(f);
+ }
+ }
+}
+
+static void
+arrowaction(int x, int y)
+{
+ int f;
+
+ if(wcur == nil || (f = wwctl(wcur->id, OWRITE)) < 0)
+ return;
+
+ x *= (mod & Mctl) ? Stepbig : Step;
+ y *= (mod & Mctl) ? Stepbig : Step;
+ if((mod & Mshift) == 0)
+ fprint(f, "move -minx %+d -miny %+d", x, y);
+ else
+ fprint(f, "resize -maxx %+d -maxy %+d -minx %+d -miny %+d", x, y, -x, -y);
+ close(f);
+}
+
+static void
+keyevent(Rune r)
+{
+ wsupdate();
+
+ if(r == '\n')
+ spawn("window");
+ else if(r == 'f')
+ togglefullscreen();
+ else if(r == 's')
+ togglesticky();
+ else if(r >= '0' && r <= '9')
+ vdaction(r - '0');
+ else if(r == Kup)
+ arrowaction(0, -1);
+ else if(r == Kdown)
+ arrowaction(0, 1);
+ else if(r == Kleft)
+ arrowaction(-1, 0);
+ else if(r == Kright)
+ arrowaction(1, 0);
+}
+
+static void
+process(char *s)
+{
+ char b[128], *p;
+ int n, o;
+ Rune r;
+
+ if(*s == 'K' && s[1] == 0)
+ mod = 0;
+
+ o = 0;
+ b[o++] = *s;
+ for(p = s+1; *p != 0; p += n){
+ if((n = chartorune(&r, p)) == 1 && r == Runeerror){
+ /* bail out */
+ n = strlen(p);
+ memmove(b+o, p, n);
+ o += n;
+ p += n;
+ break;
+ }
+
+ if(*s == 'c' && (mod & Mmod4) != 0){
+ keyevent(r);
+ continue;
+ }
+
+ if(*s == 'k'){
+ if(r == Kmod4)
+ mod |= Mmod4;
+ else if(r == Kctl)
+ mod |= Mctl;
+ else if(r == Kshift)
+ mod |= Mshift;
+ else if(r >= '0' && r <= '9' && (mod & (Mshift|Mmod4)) == (Mshift|Mmod4)){
+ keyevent(r);
+ continue;
+ }
+ }else if(*s == 'K'){
+ if(r == Kmod4)
+ mod &= ~Mmod4;
+ else if(r == Kctl)
+ mod &= ~Mctl;
+ else if(r == Kshift)
+ mod &= ~Mshift;
+ }
+
+ memmove(b+o, p, n);
+ o += n;
+ }
+
+ /* all runes filtered out - ignore completely */
+ if(o == 1 && p-s > 1)
+ return;
+
+ b[o++] = 0;
+ if(write(1, b, o) != o)
+ exits(nil);
+}
+
+static void
+usage(void)
+{
+ fprint(2, "usage: [-l light_step] [-v vol_step] %s\n", argv0);
+ exits("usage");
+}
+
+void
+main(int argc, char **argv)
+{
+ char b[128];
+ int i, j, n;
+
+ ARGBEGIN{
+ default:
+ usage();
+ }ARGEND
+
+ if((wsys = open("/dev/wsys", OREAD)) < 0)
+ sysfatal("%r");
+
+ /* initial state */
+ wsupdate();
+ for(i = 0; i < wsn; i++)
+ ws[i].vd = vd;
+ fprint(3, "%d\n", vd);
+
+ for(i = 0;;){
+ if((n = read(0, b+i, sizeof(b)-i)) <= 0)
+ break;
+ n += i;
+ for(j = 0; j < n; j++){
+ if(b[j] == 0){
+ process(b+i);
+ i = j+1;
+ }
+ }
+ memmove(b, b+i, j-i);
+ i -= j;
+ }
+
+ exits(nil);
+}