Change 9wm to look like rio.
diff --git a/src/cmd/rio/9wm.man b/src/cmd/rio/9wm.man
new file mode 100644
index 0000000..23aa06a
--- /dev/null
+++ b/src/cmd/rio/9wm.man
@@ -0,0 +1,118 @@
+.if t .ds 85 8\(12
+.if n .ds 85 8-1/2
+.TH 9wm 1
+.SH NAME
+9wm \- \*(85-like Window Manager for X
+.SH SYNOPSIS
+.B 9wm
+[
+.B \-grey
+] [
+.B \-version
+] [
+.B \-font
+.I fname
+] [
+.B \-term
+.I termprog
+] [
+.BR exit | restart
+]
+.SH DESCRIPTION
+.I 9wm
+is a window manager for X which attempts to emulate the window management
+policies of Plan 9's 
+.I \*(85
+window manager.
+.PP
+The
+.B \-grey
+option makes the background light grey, as does \*(85.
+Use this option for maximum authenticity.
+.B \-font
+.I fname
+sets the font in
+.IR 9wm 's
+menu to
+.IR fname ,
+overriding the default.
+.B \-term
+.I termprog
+specifies an alternative program to run when the
+.I New
+menu item is selected.
+.B \-version
+prints the current version on standard error, then exits.
+.PP
+To make 
+.I 9wm
+exit, you have to run
+.B "9wm exit"
+on the command line.  There is no ``exit'' menu item.
+.PP
+.I 9wm
+is click-to-type: it has a notion of the current window,
+which is usually on top, and always has its border darkened.
+Characters typed at the keyboard go to the current window,
+and mouse clicks outside the current window are swallowed up
+by
+.IR 9wm .
+To make another window the current one, click on it with button 1.
+Unlike other X window managers, 9wm implements `mouse focus': mouse events
+are sent only to the current window.
+.PP
+A menu of window operations is available by pressing button 3
+outside the current window.
+The first of these,
+.IR New ,
+attempts to spawn a
+.I 9term
+process (or
+.I xterm
+if
+.I 9term
+is not available).
+The new
+.I 9term
+will request that its outline be swept using button 3
+of the mouse, by changing the cursor.
+.RI ( xterm
+defaults to a fixed size, and thus wants to be dragged; pressing
+button 3 places it.)
+.PP
+The next four menu items are
+.IR Reshape ,
+.IR Move ,
+.IR Delete ,
+and
+.IR Hide .
+All of the operations change the cursor into a target, prompting the user
+to click button 3 on one of the windows to select it for the operation.
+At this stage, clicking button 1 or 2 will abort the operation.
+Otherwise, if the operation was
+.IR Resize ,
+the user is prompted to sweep out the new outline with button 3.
+If it was
+.IR Move ,
+the user should keep the button held down after the initial click that selected
+the window, and drag the window to the right place before releasing.
+In either case, button 1 or 2 will abort the operation.
+.PP
+If the
+.I Delete
+operation is selected, the window will be deleted when the button is released.
+This typically kills the client that owns the window.
+The
+.I Hide
+operation just makes the window invisible.  While hidden, the window's
+name appears on the bottom of the button 3 menu.  Selecting that item
+brings the window back (unhides it).
+This operation replaces the iconification feature provided by other
+window managers.
+.SH BUGS
+Is not completely compatible with \*(85.
+.PP
+There is a currently a compiled-in limit of 32 hidden windows.
+.SH "SEE ALSO"
+.IR 9term (1),
+.IR xterm (1).
diff --git a/src/cmd/rio/README b/src/cmd/rio/README
new file mode 100644
index 0000000..bece6c3
--- /dev/null
+++ b/src/cmd/rio/README
@@ -0,0 +1,203 @@
+This is David Hogan's 9wm updated to behave more like 
+Plan 9's rio.  Since I cannot get approval for the changes
+and I'd prefer not to resort to patches, I have renamed it "rio".
+
+Current incompatibilities that would be nice to fix:
+
+- Rio uses X11 fonts for the menu, and there aren't any good ones!
+I'm tempted to hard-code the Plan 9 default font bitmap.
+
+- The command-line options should be made more like Plan 9.
+
+- Should work out a protocol between 9term and rio so that:
+	* 9term can tell rio to blue its border during hold mode
+	* rio can tell 9term to fade its text when it loses focus
+	* rio can tell 9term to unfade its text when it regains focus
+
+- Should change window focus on b2/b3 clicks and then
+  pass along the click event to the now-focused window.
+
+- Should change 9term to redirect b3 clicks to rio so that rio
+  can put up the usual b3 menu.
+
+The original README is below.
+
+- russ cox
+rsc@swtch.com
+20 march 2004
+
+
+                           9wm Version 1.2
+                   Copyright 1994-1996 David Hogan.
+
+What is 9wm?
+============
+
+9wm is an X window manager which attempts to emulate the Plan 9 window
+manager 8-1/2 as far as possible within the constraints imposed by X.
+It provides a simple yet comfortable user interface, without garish
+decorations or title-bars. Or icons.  And it's click-to-type.  This
+will not appeal to everybody, but if you're not put off yet then read
+on.  (And don't knock it until you've tried it.)
+
+One major difference between 9wm and 8-1/2 is that the latter provides
+windows of text with a typescript interface, and doesn't need to run a
+separate program to emulate a terminal.  9wm, as an X window manager,
+does require a separate program.  For better 8-1/2 emulation, you should
+obtain Matthew Farrow's "9term" program (ftp://ftp.cs.su.oz.au/matty/unicode),
+version 1.6 or later (earlier versions don't cooperate with 9wm in
+implementing "hold mode").  Of course, you can run xterm under 9wm as well.
+
+What is 9wm not?
+================
+
+9wm is not a virtual window manager.  It is not customisable to any
+great extent.  It is not large and unwieldy, and doesn't use the X
+toolkit.  Requests to make it any of these things will be silently
+ignored (or flamed if I have had a bad day :-)  If you want tvtwm
+or mwm, you know where to get them...
+
+Where do I get it?
+==================
+
+The latest version of 9wm is held at ftp://ftp.cs.su.oz.au/dhog/9wm
+
+Author
+======
+
+9wm was written by David Hogan (dhog@cs.su.oz.au), a postgraduate
+student at the Basser Department of Computer Science, University
+of Sydney (http://www.cs.su.oz.au/~dhog/).
+
+Licence
+=======
+
+  9wm is free software, and is Copyright (c) 1994-1996 by David Hogan.
+  Permission is granted to all sentient beings to use this software,
+  to make copies of it, and to distribute those copies, provided
+  that:
+
+      (1) the copyright and licence notices are left intact
+      (2) the recipients are aware that it is free software
+      (3) any unapproved changes in functionality are either
+            (i) only distributed as patches
+        or (ii) distributed as a new program which is not called 9wm
+                and whose documentation gives credit where it is due
+      (4) the author is not held responsible for any defects
+          or shortcomings in the software, or damages caused by it.
+
+  There is no warranty for this software.  Have a nice day.
+
+How do I compile/install it?
+============================
+
+Assuming your system is correctly configured, you should only need to
+run xmkmf to generate the Makefile, and then run make or make install.
+make install.man should copy the manpage (9wm.man) to the appropriate
+directory.
+
+If the make fails, complaining that the function _XShapeQueryExtension
+does not exist, try removing the "-DSHAPE" from the Imakefile, and
+run xmkmf and make again.
+
+If you don't have imake, or it is misconfigured, or you would prefer
+not to use it, try copying the file "Makefile.no-imake" to "Makefile",
+then edit the definitions in this Makefile to suit your system.  This
+may require defining suitable compilation flags for your system
+(normally imake does this for you).  For instance, on AIX you must
+include "-DBSD_INCLUDES" in CFLAGS.
+
+How do I use it?
+================
+
+See the manual page for details.  You should probably read the
+man page for 9term as well.
+
+What if I find a bug?
+=====================
+
+Please mail all bug reports to 9wm-bugs@plan9.cs.su.oz.au, so
+that I can incorporate fixes into the next release.  If you can
+tell me how to fix it, all the better.
+
+Known Problems/Bugs
+===================
+
+9wm tries hard to emulate 8-1/2, but isn't 100% compatible.  If
+you are an experienced 8-1/2 user, please be patient with it.
+
+One intentional difference between 9wm and 8-1/2 is in the behaviour
+of the menu when the last hidden item is unhidden.  Under 8-1/2, when
+the menu is next used, it pops up with "New" selected.  Under 9wm,
+the (new) last menu item will be selected.  This is a feature.  It
+may be confusing if you frequently switch between 9wm and 8-1/2.
+If you don't like this feature, email me for the one line fix.
+
+There have been some problems encountered when resizing 9term on
+some platforms.  This turns out to be a problem in 9term (actually
+in libXg, to be precise).  Newer versions of 9term should be
+immune to this, see matty@cs.su.oz.au if your 9term needs fixing.
+
+Some client programs do weird things.  One of these is Frame Maker.
+It appears that if it has a modal dialog on the screen, then if any
+of its windows are current, all keypresses are redirected to the
+modal dialog.  This is not 9wm's fault -- Frame Maker is doing this.
+
+Programs like Netscape Navigator like to put riddiculously long
+icon name properties on their windows, of the form "Netscape: blah blah".
+There is no way that I know of to stop netscape from doing this.  For this
+reason, 9wm truncates labels at the first colon it finds.  This keeps the
+button 3 menu from becoming excessively wide.  Note that with same
+applications, you can use an iconName resource to set the label; this
+works well for "xman", whose default icon name of "Manual Browser"
+is a tad too long.
+
+See Also
+========
+
+http://www.cs.su.oz.au/~dhog/
+    The 9wm Home Page
+
+ftp://ftp.cs.su.oz.au/matty/unicode/
+    for source to 9term (get README first)
+
+ftp://plan9.att.com/plan9/unixsrc/sam/
+    for source && info on Rob Pike's editor "sam"
+
+ftp://rtfm.mit.edu/pub/usenet/news.answers/unix-faq/shell/rc
+    for information on a publically available implementation
+    of the Plan 9 shell "rc" for unix (or look in comp.unix.shell).
+
+ftp://viz.tamu.edu/pub/rc
+    for source to the abovementioned implementation of rc.
+
+http://plan9.att.com/plan9/
+http://plan9.att.com/magic/man2html/1/8%c2%bd
+    for information on Plan 9 (including the 8-1/2 manual entry)
+
+Acknowledgements
+================
+
+Thanks to Rob Pike for writing the original 8-1/2 program (and
+before that, mux) which inspired the writing of 9wm.
+
+Thanks to John Mackin, whose gwm "wool code" for emulating mux
+was also an inspiration: I used it (and hacked it) until I got
+too frustrated with gwm's large memory requirements and lack of
+speed (sorry Colas!), and decided to write a dedicated program.
+
+Thanks to Matthew Farrow for writing 9term.
+
+A big thanks to Dave Edmondson for adding support for
+multi-screen displays.
+
+The following people helped beta test 9wm:
+
+	John Mackin
+	Noel Hunt
+	Fred Curtis
+	James Matthew Farrow
+	Danny Yee
+	Arnold Robbins
+	Byron Rakitzis
+	micro@cooper.edu
diff --git a/src/cmd/rio/client.c b/src/cmd/rio/client.c
new file mode 100644
index 0000000..9e34655
--- /dev/null
+++ b/src/cmd/rio/client.c
@@ -0,0 +1,242 @@
+/* Copyright (c) 1994-1996 David Hogan, see README for licence details */
+#include <stdio.h>
+#include <X11/X.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include "dat.h"
+#include "fns.h"
+
+Client	*clients;
+Client	*current;
+
+void
+setactive(Client *c, int on)
+{
+	if (c->parent == c->screen->root) {
+		fprintf(stderr, "9wm: bad parent in setactive; dumping core\n");
+		abort();
+	}
+	if (on) {
+		XUngrabButton(dpy, AnyButton, AnyModifier, c->parent);
+		XSetInputFocus(dpy, c->window, RevertToPointerRoot, timestamp());
+		if (c->proto & Ptakefocus)
+			sendcmessage(c->window, wm_protocols, wm_take_focus, 0);
+		cmapfocus(c);
+	}
+	else
+		XGrabButton(dpy, AnyButton, AnyModifier, c->parent, False,
+			ButtonMask, GrabModeAsync, GrabModeSync, None, None);
+	draw_border(c, on);
+}
+
+void
+draw_border(Client *c, int active)
+{
+	unsigned long pixel;
+
+	if(active){
+		if(c->hold)
+			pixel = c->screen->activeholdborder;
+		else
+			pixel = c->screen->activeborder;
+	}else{
+		if(c->hold)
+			pixel = c->screen->inactiveholdborder;
+		else
+			pixel = c->screen->inactiveborder;
+	}
+
+	XSetWindowBackground(dpy, c->parent, pixel);
+	XClearWindow(dpy, c->parent);
+}
+
+void
+active(Client *c)
+{
+	Client *cc;
+
+	if (c == 0) {
+		fprintf(stderr, "9wm: active(c==0)\n");
+		return;
+	}
+	if (c == current)
+		return;
+	if (current) {
+		setactive(current, 0);
+		if (current->screen != c->screen)
+			cmapnofocus(current->screen);
+	}
+	setactive(c, 1);
+	for (cc = clients; cc; cc = cc->next)
+		if (cc->revert == c)
+			cc->revert = c->revert;
+	c->revert = current;
+	while (c->revert && !normal(c->revert))
+		c->revert = c->revert->revert;
+	current = c;
+#ifdef	DEBUG
+	if (debug)
+		dump_revert();
+#endif
+}
+
+void
+nofocus(void)
+{
+	static Window w = 0;
+	int mask;
+	XSetWindowAttributes attr;
+	Client *c;
+
+	if (current) {
+		setactive(current, 0);
+		for (c = current->revert; c; c = c->revert)
+			if (normal(c)) {
+				active(c);
+				return;
+			}
+		cmapnofocus(current->screen);
+		/* if no candidates to revert to, fall through */
+	}
+	current = 0;
+	if (w == 0) {
+		mask = CWOverrideRedirect;
+		attr.override_redirect = 1;
+		w = XCreateWindow(dpy, screens[0].root, 0, 0, 1, 1, 0,
+			CopyFromParent, InputOnly, CopyFromParent, mask, &attr);
+		XMapWindow(dpy, w);
+	}
+	XSetInputFocus(dpy, w, RevertToPointerRoot, timestamp());
+}
+
+void
+top(Client *c)
+{
+	Client **l, *cc;
+
+	l = &clients;
+	for (cc = *l; cc; cc = *l) {
+		if (cc == c) {
+			*l = c->next;
+			c->next = clients;
+			clients = c;
+			return;
+		}
+		l = &cc->next;
+	}
+	fprintf(stderr, "9wm: %x not on client list in top()\n", c);
+}
+
+Client *
+getclient(Window w, int create)
+{
+	Client *c;
+
+	if (w == 0 || getscreen(w))
+		return 0;
+
+	for (c = clients; c; c = c->next)
+		if (c->window == w || c->parent == w)
+			return c;
+
+	if (!create)
+		return 0;
+
+	c = (Client *)malloc(sizeof(Client));
+	memset(c, 0, sizeof(Client));
+	c->window = w;
+	/* c->parent will be set by the caller */
+	c->parent = None;
+	c->reparenting = 0;
+	c->state = WithdrawnState;
+	c->init = 0;
+	c->cmap = None;
+	c->label = c->class = 0;
+	c->revert = 0;
+	c->is9term = 0;
+	c->hold = 0;
+	c->ncmapwins = 0;
+	c->cmapwins = 0;
+	c->wmcmaps = 0;
+	c->next = clients;
+	clients = c;
+	return c;
+}
+
+void
+rmclient(Client *c)
+{
+	Client *cc;
+
+	for (cc = current; cc && cc->revert; cc = cc->revert)
+		if (cc->revert == c)
+			cc->revert = cc->revert->revert;
+
+	if (c == clients)
+		clients = c->next;
+	for (cc = clients; cc && cc->next; cc = cc->next)
+		if (cc->next == c)
+			cc->next = cc->next->next;
+
+	if (hidden(c))
+		unhidec(c, 0);
+
+	if (c->parent != c->screen->root)
+		XDestroyWindow(dpy, c->parent);
+
+	c->parent = c->window = None;		/* paranoia */
+	if (current == c) {
+		current = c->revert;
+		if (current == 0)
+			nofocus();
+		else {
+			if (current->screen != c->screen)
+				cmapnofocus(c->screen);
+			setactive(current, 1);
+		}
+	}
+	if (c->ncmapwins != 0) {
+		XFree((char *)c->cmapwins);
+		free((char *)c->wmcmaps);
+	}
+	if (c->iconname != 0)
+		XFree((char*) c->iconname);
+	if (c->name != 0)
+		XFree((char*) c->name);
+	if (c->instance != 0)
+		XFree((char*) c->instance);
+	if (c->class != 0)
+		XFree((char*) c->class);
+	memset(c, 0, sizeof(Client));		/* paranoia */
+	free(c);
+}
+
+#ifdef	DEBUG
+void
+dump_revert(void)
+{
+	Client *c;
+	int i;
+
+	i = 0;
+	for (c = current; c; c = c->revert) {
+		fprintf(stderr, "%s(%x:%d)", c->label ? c->label : "?", c->window, c->state);
+		if (i++ > 100)
+			break;
+		if (c->revert)
+			fprintf(stderr, " -> ");
+	}
+	if (current == 0)
+		fprintf(stderr, "empty");
+	fprintf(stderr, "\n");
+}
+
+void
+dump_clients(void)
+{
+	Client *c;
+
+	for (c = clients; c; c = c->next)
+		fprintf(stderr, "w 0x%x parent 0x%x @ (%d, %d)\n", c->window, c->parent, c->x, c->y);
+}
+#endif
diff --git a/src/cmd/rio/color.c b/src/cmd/rio/color.c
new file mode 100644
index 0000000..8c53466
--- /dev/null
+++ b/src/cmd/rio/color.c
@@ -0,0 +1,44 @@
+/* Copyright (c) 2004 Russ Cox, see README for licence details */
+#include <stdio.h>
+#include <signal.h>
+#include <X11/X.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include "dat.h"
+#include "fns.h"
+
+unsigned long
+colorpixel(Display *dpy, int depth, ulong rgb)
+{
+	int r, g, b;
+
+	r = rgb>>16;
+	g = (rgb>>8)&0xFF;
+	b = rgb&0xFF;
+
+	switch(depth){
+	case 1:
+	case 2:
+	case 4:
+	case 8:
+	default:
+		/* not going to waste color map entries */
+		if(rgb == 0xFFFFFF)
+			return WhitePixel(dpy, DefaultScreen(dpy));
+		return BlackPixel(dpy, DefaultScreen(dpy));
+	case 15:
+		r >>= 3;
+		g >>= 3;
+		b >>= 3;
+		return (r<<10) | (g<<5) | b;
+	case 16:
+		r >>= 3;
+		g >>= 2;
+		b >>= 3;
+		return (r<<11) | (g<<5) | b;
+	case 24:
+	case 32:
+		return rgb;
+	}
+}
+
diff --git a/src/cmd/rio/cursor.c b/src/cmd/rio/cursor.c
new file mode 100644
index 0000000..f28906f
--- /dev/null
+++ b/src/cmd/rio/cursor.c
@@ -0,0 +1,368 @@
+/* Copyright (c) 1994-1996 David Hogan, see README for licence details */
+#include <stdio.h>
+#include <X11/X.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include "dat.h"
+#include "fns.h"
+
+typedef struct {
+	int		width;
+	int		hot[2];
+	unsigned char	mask[64];
+	unsigned char	fore[64];
+} Cursordata;
+
+Cursordata bigarrow = {
+	16,
+	{0, 0},
+	{ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0xFF, 0x3F, 
+	  0xFF, 0x0F, 0xFF, 0x0F, 0xFF, 0x1F, 0xFF, 0x3F, 
+	  0xFF, 0x7F, 0xFF, 0xFF, 0xFF, 0x7F, 0xFF, 0x3F, 
+	  0xCF, 0x1F, 0x8F, 0x0F, 0x07, 0x07, 0x03, 0x02, 
+	},
+	{ 0x00, 0x00, 0xFE, 0x7F, 0xFE, 0x3F, 0xFE, 0x0F, 
+	  0xFE, 0x07, 0xFE, 0x07, 0xFE, 0x0F, 0xFE, 0x1F, 
+	  0xFE, 0x3F, 0xFE, 0x7F, 0xFE, 0x3F, 0xCE, 0x1F, 
+	  0x86, 0x0F, 0x06, 0x07, 0x02, 0x02, 0x00, 0x00, 
+	},
+};
+
+Cursordata sweep0data = {
+	16,
+	{7, 7},
+	{0xC0, 0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0, 0x03,
+	 0xC0, 0x03, 0xC0, 0x03, 0xFF, 0xFF, 0xFF, 0xFF,
+	 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x03, 0xC0, 0x03,
+	 0xC0, 0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0, 0x03},
+	{0x00, 0x00, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01,
+	 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0xFE, 0x7F,
+	 0xFE, 0x7F, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01,
+	 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x00, 0x00}
+};
+
+Cursordata boxcursdata = {
+	16,
+	{7, 7},
+	{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	 0xFF, 0xFF, 0x1F, 0xF8, 0x1F, 0xF8, 0x1F, 0xF8,
+	 0x1F, 0xF8, 0x1F, 0xF8, 0x1F, 0xF8, 0xFF, 0xFF,
+	 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},
+	{0x00, 0x00, 0xFE, 0x7F, 0xFE, 0x7F, 0xFE, 0x7F,
+	 0x0E, 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E, 0x70,
+	 0x0E, 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E, 0x70,
+	 0xFE, 0x7F, 0xFE, 0x7F, 0xFE, 0x7F, 0x00, 0x00}
+};
+
+Cursordata sightdata = {
+	16,
+	{7, 7},
+	{0xF8, 0x1F, 0xFC, 0x3F, 0xFE, 0x7F, 0xDF, 0xFB,
+	 0xCF, 0xF3, 0xC7, 0xE3, 0xFF, 0xFF, 0xFF, 0xFF,
+	 0xFF, 0xFF, 0xFF, 0xFF, 0xC7, 0xE3, 0xCF, 0xF3,
+	 0xDF, 0x7B, 0xFE, 0x7F, 0xFC, 0x3F, 0xF8, 0x1F,},
+	{0x00, 0x00, 0xF0, 0x0F, 0x8C, 0x31, 0x84, 0x21,
+	 0x82, 0x41, 0x82, 0x41, 0x82, 0x41, 0xFE, 0x7F,
+	 0xFE, 0x7F, 0x82, 0x41, 0x82, 0x41, 0x82, 0x41,
+	 0x84, 0x21, 0x8C, 0x31, 0xF0, 0x0F, 0x00, 0x00,}
+};
+
+Cursordata arrowdata = {
+	16,
+	{1, 1},
+	{0xFF, 0x07, 0xFF, 0x07, 0xFF, 0x03, 0xFF, 0x00,
+	 0xFF, 0x00, 0xFF, 0x01, 0xFF, 0x03, 0xFF, 0x07,
+	 0xE7, 0x0F, 0xC7, 0x1F, 0x83, 0x3F, 0x00, 0x7F,
+	 0x00, 0xFE, 0x00, 0x7C, 0x00, 0x38, 0x00, 0x10,},
+	{0x00, 0x00, 0xFE, 0x03, 0xFE, 0x00, 0x3E, 0x00,
+	 0x7E, 0x00, 0xFE, 0x00, 0xF6, 0x01, 0xE6, 0x03,
+	 0xC2, 0x07, 0x82, 0x0F, 0x00, 0x1F, 0x00, 0x3E,
+	 0x00, 0x7C, 0x00, 0x38, 0x00, 0x10, 0x00, 0x00,}
+};
+
+Cursordata whitearrow = {
+	16,
+	{0, 0},
+	{0xFF, 0x07, 0xFF, 0x07, 0xFF, 0x03, 0xFF, 0x00,
+	 0xFF, 0x00, 0xFF, 0x01, 0xFF, 0x03, 0xFF, 0x07,
+	 0xE7, 0x0F, 0xC7, 0x1F, 0x83, 0x3F, 0x00, 0x7F,
+	 0x00, 0xFE, 0x00, 0x7C, 0x00, 0x38, 0x00, 0x10,},
+	{0xFF, 0x07, 0xFF, 0x07, 0x83, 0x03, 0xC3, 0x00,
+	 0xC3, 0x00, 0x83, 0x01, 0x1B, 0x03, 0x3F, 0x06,
+	 0x67, 0x0C, 0xC7, 0x18, 0x83, 0x31, 0x00, 0x63,
+	 0x00, 0xC6, 0x00, 0x6C, 0x00, 0x38, 0x00, 0x10,}
+};
+
+Cursordata blittarget = {
+	18,
+	{8, 8},
+	{0xe0, 0x1f, 0x00, 0xf0, 0x3f, 0x00, 0xf8, 0x7f, 0x00,
+	 0xfc, 0xff, 0x00, 0xfe, 0xff, 0x01, 0xff, 0xff, 0x03,
+	 0xff, 0xff, 0x03, 0xff, 0xff, 0x03, 0xff, 0xff, 0x03,
+	 0xff, 0xff, 0x03, 0xff, 0xff, 0x03, 0xff, 0xff, 0x03,
+	 0xff, 0xff, 0x03, 0xfe, 0xff, 0x01, 0xfc, 0xff, 0x00,
+	 0xf8, 0x7f, 0x00, 0xf0, 0x3f, 0x00, 0xe0, 0x1f, 0x00},
+	{0x00, 0x00, 0x00, 0xc0, 0x0f, 0x00, 0xf0, 0x3f, 0x00,
+	 0x38, 0x73, 0x00, 0x8c, 0xc7, 0x00, 0xec, 0xdf, 0x00,
+	 0x66, 0x9b, 0x01, 0x36, 0xb3, 0x01, 0xfe, 0xff, 0x01,
+	 0xfe, 0xff, 0x01, 0x36, 0xb3, 0x01, 0x66, 0x9b, 0x01,
+	 0xec, 0xdf, 0x00, 0x8c, 0xc7, 0x00, 0x38, 0x73, 0x00,
+	 0xf0, 0x3f, 0x00, 0xc0, 0x0f, 0x00, 0x00, 0x00, 0x00}
+};
+
+Cursordata blitarrow = {
+	18,
+	{1, 1},
+	{0xff, 0x0f, 0x00, 0xff, 0x07, 0x00, 0xff, 0x03, 0x00,
+	 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x01, 0x00,
+	 0xff, 0x03, 0x00, 0xff, 0x07, 0x00, 0xe7, 0x0f, 0x00,
+	 0xc7, 0x1f, 0x00, 0x87, 0x3f, 0x00, 0x03, 0x7f, 0x00,
+	 0x01, 0xfe, 0x00, 0x00, 0xfc, 0x01, 0x00, 0xf8, 0x03,
+	 0x00, 0xf0, 0x01, 0x00, 0xe0, 0x00, 0x00, 0x40, 0x00},
+	{0x00, 0x00, 0x00, 0xfe, 0x03, 0x00, 0xfe, 0x00, 0x00,
+	 0x3e, 0x00, 0x00, 0x7e, 0x00, 0x00, 0xfe, 0x00, 0x00,
+	 0xf6, 0x01, 0x00, 0xe6, 0x03, 0x00, 0xc2, 0x07, 0x00,
+	 0x82, 0x0f, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x3e, 0x00,
+	 0x00, 0x7c, 0x00, 0x00, 0xf8, 0x00, 0x00, 0xf0, 0x01,
+	 0x00, 0xe0, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00}
+};
+
+Cursordata blitsweep = {
+	18,
+	{8, 8},
+	{0xc4, 0xff, 0x03, 0xce, 0xff, 0x03, 0xdf, 0xff, 0x03,
+	 0x3e, 0x80, 0x03, 0x7c, 0x83, 0x03, 0xf8, 0x83, 0x03,
+	 0xf7, 0x83, 0x03, 0xe7, 0x83, 0x03, 0xf7, 0x83, 0x03,
+	 0xf7, 0x83, 0x03, 0x07, 0x80, 0x03, 0x07, 0x80, 0x03,
+	 0x07, 0x80, 0x03, 0x07, 0x80, 0x03, 0x07, 0x80, 0x03,
+	 0xff, 0xff, 0x03, 0xff, 0xff, 0x03, 0xff, 0xff, 0x03},
+	{0x00, 0x00, 0x00, 0x84, 0xff, 0x01, 0x0e, 0x00, 0x01,
+	 0x1c, 0x00, 0x01, 0x38, 0x00, 0x01, 0x70, 0x01, 0x01,
+	 0xe0, 0x01, 0x01, 0xc2, 0x01, 0x01, 0xe2, 0x01, 0x01,
+	 0x02, 0x00, 0x01, 0x02, 0x00, 0x01, 0x02, 0x00, 0x01,
+	 0x02, 0x00, 0x01, 0x02, 0x00, 0x01, 0x02, 0x00, 0x01,
+	 0x02, 0x00, 0x01, 0xfe, 0xff, 0x01, 0x00, 0x00, 0x00}
+};
+
+/*
+ *	Grey tile pattern for root background
+ */
+
+#define grey_width 4
+#define grey_height 2
+static char grey_bits[] = {
+	0x01, 0x04,
+};
+
+static XColor	bl, wh;
+
+Cursor
+getcursor(c, s)
+Cursordata *c;
+ScreenInfo *s;
+{
+	Pixmap f, m;
+
+	f = XCreatePixmapFromBitmapData(dpy, s->root, (char *)c->fore,
+		c->width, c->width, 1, 0, 1);
+	m = XCreatePixmapFromBitmapData(dpy, s->root, (char *)c->mask,
+		c->width, c->width, 1, 0, 1);
+	return XCreatePixmapCursor(dpy, f, m, &bl, &wh,
+			c->hot[0], c->hot[1]);
+}
+
+void
+initcurs(s)
+ScreenInfo *s;
+{
+	XColor dummy;
+
+	XAllocNamedColor(dpy, DefaultColormap(dpy, s->num),
+			"black", &bl, &dummy);
+	XAllocNamedColor(dpy, DefaultColormap(dpy, s->num),
+			"white", &wh, &dummy);
+
+	if (nostalgia) {
+		s->arrow = getcursor(&blitarrow, s);
+		s->target = getcursor(&blittarget, s);
+		s->sweep0 = getcursor(&blitsweep, s);
+		s->boxcurs = getcursor(&blitsweep, s);
+	}
+	else {
+		s->arrow = getcursor(&bigarrow, s);
+		s->target = getcursor(&sightdata, s);
+		s->sweep0 = getcursor(&sweep0data, s);
+		s->boxcurs = getcursor(&boxcursdata, s);
+	}
+
+	s->root_pixmap = XCreatePixmapFromBitmapData(dpy,
+		s->root, grey_bits, grey_width, grey_height,
+		s->black, s->white, DefaultDepth(dpy, s->num));
+}
+
+
+/* RIO
+
+Cursor crosscursor = {
+	{-7, -7},
+	{0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0,
+	 0x03, 0xC0, 0x03, 0xC0, 0xFF, 0xFF, 0xFF, 0xFF,
+	 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0xC0, 0x03, 0xC0,
+	 0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0, },
+	{0x00, 0x00, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80,
+	 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x7F, 0xFE,
+	 0x7F, 0xFE, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80,
+	 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x00, 0x00, }
+};
+
+Cursor boxcursor = {
+	{-7, -7},
+	{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	 0xFF, 0xFF, 0xF8, 0x1F, 0xF8, 0x1F, 0xF8, 0x1F,
+	 0xF8, 0x1F, 0xF8, 0x1F, 0xF8, 0x1F, 0xFF, 0xFF,
+	 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, },
+	{0x00, 0x00, 0x7F, 0xFE, 0x7F, 0xFE, 0x7F, 0xFE,
+	 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E,
+	 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E,
+	 0x7F, 0xFE, 0x7F, 0xFE, 0x7F, 0xFE, 0x00, 0x00, }
+};
+
+Cursor sightcursor = {
+	{-7, -7},
+	{0x1F, 0xF8, 0x3F, 0xFC, 0x7F, 0xFE, 0xFB, 0xDF,
+	 0xF3, 0xCF, 0xE3, 0xC7, 0xFF, 0xFF, 0xFF, 0xFF,
+	 0xFF, 0xFF, 0xFF, 0xFF, 0xE3, 0xC7, 0xF3, 0xCF,
+	 0x7B, 0xDF, 0x7F, 0xFE, 0x3F, 0xFC, 0x1F, 0xF8, },
+	{0x00, 0x00, 0x0F, 0xF0, 0x31, 0x8C, 0x21, 0x84,
+	 0x41, 0x82, 0x41, 0x82, 0x41, 0x82, 0x7F, 0xFE,
+	 0x7F, 0xFE, 0x41, 0x82, 0x41, 0x82, 0x41, 0x82,
+	 0x21, 0x84, 0x31, 0x8C, 0x0F, 0xF0, 0x00, 0x00, }
+};
+
+Cursor whitearrow = {
+	{0, 0},
+	{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFC, 
+	 0xFF, 0xF0, 0xFF, 0xF0, 0xFF, 0xF8, 0xFF, 0xFC, 
+	 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFC, 
+	 0xF3, 0xF8, 0xF1, 0xF0, 0xE0, 0xE0, 0xC0, 0x40, },
+	{0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x06, 0xC0, 0x1C, 
+	 0xC0, 0x30, 0xC0, 0x30, 0xC0, 0x38, 0xC0, 0x1C, 
+	 0xC0, 0x0E, 0xC0, 0x07, 0xCE, 0x0E, 0xDF, 0x1C, 
+	 0xD3, 0xB8, 0xF1, 0xF0, 0xE0, 0xE0, 0xC0, 0x40, }
+};
+
+Cursor query = {
+	{-7,-7},
+	{0x0f, 0xf0, 0x1f, 0xf8, 0x3f, 0xfc, 0x7f, 0xfe, 
+	 0x7c, 0x7e, 0x78, 0x7e, 0x00, 0xfc, 0x01, 0xf8, 
+	 0x03, 0xf0, 0x07, 0xe0, 0x07, 0xc0, 0x07, 0xc0, 
+	 0x07, 0xc0, 0x07, 0xc0, 0x07, 0xc0, 0x07, 0xc0, },
+	{0x00, 0x00, 0x0f, 0xf0, 0x1f, 0xf8, 0x3c, 0x3c, 
+	 0x38, 0x1c, 0x00, 0x3c, 0x00, 0x78, 0x00, 0xf0, 
+	 0x01, 0xe0, 0x03, 0xc0, 0x03, 0x80, 0x03, 0x80, 
+	 0x00, 0x00, 0x03, 0x80, 0x03, 0x80, 0x00, 0x00, }
+};
+
+Cursor tl = {
+	{-4, -4},
+	{0xfe, 0x00, 0x82, 0x00, 0x8c, 0x00, 0x87, 0xff, 
+	 0xa0, 0x01, 0xb0, 0x01, 0xd0, 0x01, 0x11, 0xff, 
+	 0x11, 0x00, 0x11, 0x00, 0x11, 0x00, 0x11, 0x00, 
+	 0x11, 0x00, 0x11, 0x00, 0x11, 0x00, 0x1f, 0x00, },
+	{0x00, 0x00, 0x7c, 0x00, 0x70, 0x00, 0x78, 0x00, 
+	 0x5f, 0xfe, 0x4f, 0xfe, 0x0f, 0xfe, 0x0e, 0x00, 
+	 0x0e, 0x00, 0x0e, 0x00, 0x0e, 0x00, 0x0e, 0x00, 
+	 0x0e, 0x00, 0x0e, 0x00, 0x0e, 0x00, 0x00, 0x00, }
+};
+
+Cursor t = {
+	{-7, -8},
+	{0x00, 0x00, 0x00, 0x00, 0x03, 0x80, 0x06, 0xc0, 
+	 0x1c, 0x70, 0x10, 0x10, 0x0c, 0x60, 0xfc, 0x7f, 
+	 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0xff, 0xff, 
+	 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, },
+	{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 
+	 0x03, 0x80, 0x0f, 0xe0, 0x03, 0x80, 0x03, 0x80, 
+	 0x7f, 0xfe, 0x7f, 0xfe, 0x7f, 0xfe, 0x00, 0x00, 
+	 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }
+};
+
+Cursor tr = {
+	{-11, -4},
+	{0x00, 0x7f, 0x00, 0x41, 0x00, 0x31, 0xff, 0xe1, 
+	 0x80, 0x05, 0x80, 0x0d, 0x80, 0x0b, 0xff, 0x88, 
+	 0x00, 0x88, 0x0, 0x88, 0x00, 0x88, 0x00, 0x88, 
+	 0x00, 0x88, 0x00, 0x88, 0x00, 0x88, 0x00, 0xf8, },
+	{0x00, 0x00, 0x00, 0x3e, 0x00, 0x0e, 0x00, 0x1e, 
+	 0x7f, 0xfa, 0x7f, 0xf2, 0x7f, 0xf0, 0x00, 0x70, 
+	 0x00, 0x70, 0x00, 0x70, 0x00, 0x70, 0x00, 0x70, 
+	 0x00, 0x70, 0x00, 0x70, 0x00, 0x70, 0x00, 0x00, }
+};
+
+Cursor r = {
+	{-8, -7},
+	{0x07, 0xc0, 0x04, 0x40, 0x04, 0x40, 0x04, 0x58, 
+	 0x04, 0x68, 0x04, 0x6c, 0x04, 0x06, 0x04, 0x02, 
+	 0x04, 0x06, 0x04, 0x6c, 0x04, 0x68, 0x04, 0x58, 
+	 0x04, 0x40, 0x04, 0x40, 0x04, 0x40, 0x07, 0xc0, },
+	{0x00, 0x00, 0x03, 0x80, 0x03, 0x80, 0x03, 0x80, 
+	 0x03, 0x90, 0x03, 0x90, 0x03, 0xf8, 0x03, 0xfc, 
+	 0x03, 0xf8, 0x03, 0x90, 0x03, 0x90, 0x03, 0x80, 
+	 0x03, 0x80, 0x03, 0x80, 0x03, 0x80, 0x00, 0x00, }
+};
+
+Cursor br = {
+	{-11, -11},
+	{0x00, 0xf8, 0x00, 0x88, 0x00, 0x88, 0x00, 0x88, 
+	 0x00, 0x88, 0x00, 0x88, 0x00, 0x88, 0x00, 0x88, 
+	 0xff, 0x88, 0x80, 0x0b, 0x80, 0x0d, 0x80, 0x05, 
+	 0xff, 0xe1, 0x00, 0x31, 0x00, 0x41, 0x00, 0x7f, },
+	{0x00, 0x00, 0x00, 0x70, 0x00, 0x70, 0x00, 0x70, 
+	 0x0, 0x70, 0x00, 0x70, 0x00, 0x70, 0x00, 0x70, 
+	 0x00, 0x70, 0x7f, 0xf0, 0x7f, 0xf2, 0x7f, 0xfa, 
+	 0x00, 0x1e, 0x00, 0x0e, 0x00, 0x3e, 0x00, 0x00, }
+};
+
+Cursor b = {
+	{-7, -7},
+	{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
+	 0xff, 0xff, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 
+	 0xfc, 0x7f, 0x0c, 0x60, 0x10, 0x10, 0x1c, 0x70, 
+	 0x06, 0xc0, 0x03, 0x80, 0x00, 0x00, 0x00, 0x00, },
+	{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
+	 0x00, 0x00, 0x7f, 0xfe, 0x7f, 0xfe, 0x7f, 0xfe, 
+	 0x03, 0x80, 0x03, 0x80, 0x0f, 0xe0, 0x03, 0x80, 
+	 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }
+};
+
+Cursor bl = {
+	{-4, -11},
+	{0x1f, 0x00, 0x11, 0x00, 0x11, 0x00, 0x11, 0x00, 
+	 0x11, 0x00, 0x11, 0x00, 0x11, 0x00, 0x11, 0x00, 
+	 0x11, 0xff, 0xd0, 0x01, 0xb0, 0x01, 0xa0, 0x01, 
+	 0x87, 0xff, 0x8c, 0x00, 0x82, 0x00, 0xfe, 0x00, },
+	{0x00, 0x00, 0x0e, 0x00, 0x0e, 0x00, 0x0e, 0x00, 
+	 0x0e, 0x00, 0x0e, 0x00, 0x0e, 0x00, 0x0e, 0x00, 
+	 0x0e, 0x00, 0x0f, 0xfe, 0x4f, 0xfe, 0x5f, 0xfe, 
+	 0x78, 0x00, 0x70, 0x00, 0x7c, 0x00, 0x00, 0x0, }
+};
+
+Cursor l = {
+	{-7, -7},
+	{0x03, 0xe0, 0x02, 0x20, 0x02, 0x20, 0x1a, 0x20, 
+	 0x16, 0x20, 0x36, 0x20, 0x60, 0x20, 0x40, 0x20, 
+	 0x60, 0x20, 0x36, 0x20, 0x16, 0x20, 0x1a, 0x20, 
+	 0x02, 0x20, 0x02, 0x20, 0x02, 0x20, 0x03, 0xe0, },
+	{0x00, 0x00, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 
+	 0x09, 0xc0, 0x09, 0xc0, 0x1f, 0xc0, 0x3f, 0xc0, 
+	 0x1f, 0xc0, 0x09, 0xc0, 0x09, 0xc0, 0x01, 0xc0, 
+	 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x00, 0x00, }
+};
+
+Cursor *corners[9] = {
+	&tl,	&t,	&tr,
+	&l,	nil,	&r,
+	&bl,	&b,	&br,
+};
+
+*/
diff --git a/src/cmd/rio/dat.h b/src/cmd/rio/dat.h
new file mode 100644
index 0000000..ac5ee15
--- /dev/null
+++ b/src/cmd/rio/dat.h
@@ -0,0 +1,148 @@
+/* Copyright (c) 1994-1996 David Hogan, see README for licence details */
+
+#define BORDER		_border
+#define	INSET		_inset
+#define MAXHIDDEN	32
+#define B3FIXED 	5
+
+#define AllButtonMask	(Button1Mask|Button2Mask|Button3Mask \
+			|Button4Mask|Button5Mask)
+#define ButtonMask	(ButtonPressMask|ButtonReleaseMask)
+#define MenuMask	(ButtonMask|ButtonMotionMask|ExposureMask)
+#define MenuGrabMask	(ButtonMask|ButtonMotionMask|StructureNotifyMask)
+
+#ifdef	Plan9
+#define DEFSHELL	"/bin/rc"
+#else
+#define DEFSHELL	"/bin/sh"
+#endif
+
+typedef struct Client Client;
+typedef struct Menu Menu;
+typedef struct ScreenInfo ScreenInfo;
+
+struct Client {
+	Window		window;
+	Window		parent;
+	Window		trans;
+	Client		*next;
+	Client		*revert;
+
+	int 		x;
+	int 		y;
+	int 		dx;
+	int 		dy;
+	int 		border;
+
+	XSizeHints	size;
+	int 		min_dx;
+	int 		min_dy;
+
+	int 		state;
+	int 		init;
+	int 		reparenting;
+	int 		is9term;
+	int 		hold;
+	int 		proto;
+
+	char		*label;
+	char		*instance;
+	char		*class;
+	char		*name;
+	char		*iconname;
+
+	Colormap	cmap;
+	int 		ncmapwins;
+	Window		*cmapwins;
+	Colormap	*wmcmaps;
+	ScreenInfo	*screen;
+};
+
+#define hidden(c)	((c)->state == IconicState)
+#define withdrawn(c)	((c)->state == WithdrawnState)
+#define normal(c)	((c)->state == NormalState)
+
+/* c->proto */
+#define Pdelete 	1
+#define Ptakefocus	2
+
+struct Menu {
+	char	**item;
+	char	*(*gen)();
+	int	lasthit;
+};
+
+struct ScreenInfo {
+	int			num;
+	int			depth;
+	int			width;
+	int			height;
+	Window		root;
+	Window		menuwin;
+	Window		sweepwin;
+	Colormap		def_cmap;
+	GC			gc;
+	GC			gccopy;
+	GC			gcred;
+	GC			gcsweep;
+	GC			gcmenubg;
+	GC			gcmenubgs;
+	GC			gcmenufg;
+	GC			gcmenufgs;
+	unsigned long	black;
+	unsigned long	white;
+	unsigned long	activeholdborder;
+	unsigned long	inactiveholdborder;
+	unsigned long	activeborder;
+	unsigned long	inactiveborder;
+	unsigned long	red;
+	Pixmap		bkup[2];
+	int			min_cmaps;
+	Cursor		target;
+	Cursor		sweep0;
+	Cursor		boxcurs;
+	Cursor		arrow;
+	Pixmap		root_pixmap;
+	char			display[256];	/* arbitrary limit */
+};
+
+/* main.c */
+extern Display		*dpy;
+extern ScreenInfo	*screens;
+extern int			num_screens;
+extern int			initting;
+extern XFontStruct	*font;
+extern int			nostalgia;
+extern char		**myargv;
+extern Bool 		shape;
+extern char 		*termprog;
+extern char 		*shell;
+extern char 		*version[];
+extern int			_border;
+extern int			_inset;
+extern int			curtime;
+extern int			debug;
+extern int			solidsweep;
+
+extern Atom		exit_9wm;
+extern Atom		restart_9wm;
+extern Atom 		wm_state;
+extern Atom		wm_change_state;
+extern Atom 		_9wm_hold_mode;
+extern Atom 		wm_protocols;
+extern Atom 		wm_delete;
+extern Atom 		wm_take_focus;
+extern Atom 		wm_colormaps;
+
+/* client.c */
+extern Client		*clients;
+extern Client		*current;
+
+/* menu.c */
+extern Client		*hiddenc[];
+extern int 			numhidden;
+extern char 		*b3items[];
+extern Menu 		b3menu;
+
+/* error.c */
+extern int 			ignore_badwindow;
diff --git a/src/cmd/rio/error.c b/src/cmd/rio/error.c
new file mode 100644
index 0000000..731da9f
--- /dev/null
+++ b/src/cmd/rio/error.c
@@ -0,0 +1,98 @@
+/* Copyright (c) 1994-1996 David Hogan, see README for licence details */
+#include <stdio.h>
+#include <X11/X.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/Xproto.h>
+#include "dat.h"
+#include "fns.h"
+
+int 	ignore_badwindow;
+
+void
+fatal(char *s)
+{
+	fprintf(stderr, "9wm: ");
+	perror(s);
+	fprintf(stderr, "\n");
+	exit(1);
+}
+
+int
+handler(Display *d, XErrorEvent *e)
+{
+	char msg[80], req[80], number[80];
+
+	if (initting && (e->request_code == X_ChangeWindowAttributes) && (e->error_code == BadAccess)) {
+		fprintf(stderr, "9wm: it looks like there's already a window manager running;  9wm not started\n");
+		exit(1);
+	}
+
+	if (ignore_badwindow && (e->error_code == BadWindow || e->error_code == BadColor))
+		return 0;
+
+	XGetErrorText(d, e->error_code, msg, sizeof(msg));
+	sprintf(number, "%d", e->request_code);
+	XGetErrorDatabaseText(d, "XRequest", number, "", req, sizeof(req));
+	if (req[0] == '\0')
+		sprintf(req, "<request-code-%d>", e->request_code);
+
+	fprintf(stderr, "9wm: %s(0x%x): %s\n", req, e->resourceid, msg);
+
+	if (initting) {
+		fprintf(stderr, "9wm: failure during initialisation; aborting\n");
+		exit(1);
+	}
+	return 0;
+}
+
+void
+graberror(char *f, int err)
+{
+#ifdef	DEBUG	/* sick of "bug" reports; grab errors "just happen" */
+	char *s;
+
+	switch (err) {
+	case GrabNotViewable:
+		s = "not viewable";
+		break;
+	case AlreadyGrabbed:
+		s = "already grabbed";
+		break;
+	case GrabFrozen:
+		s = "grab frozen";
+		break;
+	case GrabInvalidTime:
+		s = "invalid time";
+		break;
+	case GrabSuccess:
+		return;
+	default:
+		fprintf(stderr, "9wm: %s: grab error: %d\n", f, err);
+		return;
+	}
+	fprintf(stderr, "9wm: %s: grab error: %s\n", f, s);
+#endif
+}
+
+#ifdef	DEBUG_EV
+#include "showevent/ShowEvent.c"
+#endif
+
+#ifdef	DEBUG
+
+void
+dotrace(char *s, Client *c, XEvent *e)
+{
+	fprintf(stderr, "9wm: %s: c=0x%x", s, c);
+	if (c)
+		fprintf(stderr, " x %d y %d dx %d dy %d w 0x%x parent 0x%x", c->x, c->y, c->dx, c->dy, c->window, c->parent);
+#ifdef	DEBUG_EV
+	if (e) {
+		fprintf(stderr, "\n\t");
+		ShowEvent(e);
+	}
+#endif
+	fprintf(stderr, "\n");
+}
+#endif
diff --git a/src/cmd/rio/event.c b/src/cmd/rio/event.c
new file mode 100644
index 0000000..1b27e85
--- /dev/null
+++ b/src/cmd/rio/event.c
@@ -0,0 +1,465 @@
+/* Copyright (c) 1994-1996 David Hogan, see README for licence details */
+#include <stdio.h>
+#include <X11/X.h>
+#include <X11/Xos.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/Xatom.h>
+#include <X11/extensions/shape.h>
+#include "dat.h"
+#include "fns.h"
+#include "patchlevel.h"
+
+void
+mainloop(int shape_event)
+{
+	XEvent ev;
+
+	for (;;) {
+		getevent(&ev);
+
+#ifdef	DEBUG_EV
+		if (debug) {
+			ShowEvent(&ev);
+			printf("\n");
+		}
+#endif
+		switch (ev.type) {
+		default:
+#ifdef	SHAPE
+			if (shape && ev.type == shape_event)
+				shapenotify((XShapeEvent *)&ev);
+			else
+#endif
+				fprintf(stderr, "9wm: unknown ev.type %d\n", ev.type);
+			break;
+		case ButtonPress:
+			button(&ev.xbutton);
+			break;
+		case ButtonRelease:
+			break;
+		case MapRequest:
+			mapreq(&ev.xmaprequest);
+			break;
+		case ConfigureRequest:
+			configurereq(&ev.xconfigurerequest);
+			break;
+		case CirculateRequest:
+			circulatereq(&ev.xcirculaterequest);
+			break;
+		case UnmapNotify:
+			unmap(&ev.xunmap);
+			break;
+		case CreateNotify:
+			newwindow(&ev.xcreatewindow);
+			break;
+		case DestroyNotify:
+			destroy(ev.xdestroywindow.window);
+			break;
+		case ClientMessage:
+			clientmesg(&ev.xclient);
+			break;
+		case ColormapNotify:
+			cmap(&ev.xcolormap);
+			break;
+		case PropertyNotify:
+			property(&ev.xproperty);
+			break;
+		case SelectionClear:
+			fprintf(stderr, "9wm: SelectionClear (this should not happen)\n");
+			break;
+		case SelectionNotify:
+			fprintf(stderr, "9wm: SelectionNotify (this should not happen)\n");
+			break;
+		case SelectionRequest:
+			fprintf(stderr, "9wm: SelectionRequest (this should not happen)\n");
+			break;
+		case EnterNotify:
+			enter(&ev.xcrossing);
+			break;
+		case ReparentNotify:
+			reparent(&ev.xreparent);
+			break;
+		case FocusIn:
+			focusin(&ev.xfocus);
+			break;
+		case MotionNotify:
+		case Expose:
+		case NoExpose:
+		case FocusOut:
+		case ConfigureNotify:
+		case MapNotify:
+		case MappingNotify:
+			/* not interested */
+			trace("ignore", 0, &ev);
+			break;
+		}
+	}
+}
+
+
+void
+configurereq(XConfigureRequestEvent *e)
+{
+	XWindowChanges wc;
+	Client *c;
+
+	/* we don't set curtime as nothing here uses it */
+	c = getclient(e->window, 0);
+	trace("configurereq", c, e);
+
+	e->value_mask &= ~CWSibling;
+
+	if (c) {
+		gravitate(c, 1);
+		if (e->value_mask & CWX)
+			c->x = e->x;
+		if (e->value_mask & CWY)
+			c->y = e->y;
+		if (e->value_mask & CWWidth)
+			c->dx = e->width;
+		if (e->value_mask & CWHeight)
+			c->dy = e->height;
+		if (e->value_mask & CWBorderWidth)
+			c->border = e->border_width;
+		gravitate(c, 0);
+		if (e->value_mask & CWStackMode) {
+			if (wc.stack_mode == Above)
+				top(c);
+			else
+				e->value_mask &= ~CWStackMode;
+		}
+		if (c->parent != c->screen->root && c->window == e->window) {
+			wc.x = c->x-BORDER;
+			wc.y = c->y-BORDER;
+			wc.width = c->dx+2*BORDER;
+			wc.height = c->dy+2*BORDER;
+			wc.border_width = 1;
+			wc.sibling = None;
+			wc.stack_mode = e->detail;
+			XConfigureWindow(dpy, c->parent, e->value_mask, &wc);
+			sendconfig(c);
+		}
+	}
+
+	if (c && c->init) {
+		wc.x = BORDER;
+		wc.y = BORDER;
+	}
+	else {
+		wc.x = e->x;
+		wc.y = e->y;
+	}
+	wc.width = e->width;
+	wc.height = e->height;
+	wc.border_width = 0;
+	wc.sibling = None;
+	wc.stack_mode = Above;
+	e->value_mask &= ~CWStackMode;
+	e->value_mask |= CWBorderWidth;
+
+	XConfigureWindow(dpy, e->window, e->value_mask, &wc);
+}
+
+void
+mapreq(XMapRequestEvent *e)
+{
+	Client *c;
+	int i;
+
+	curtime = CurrentTime;
+	c = getclient(e->window, 0);
+	trace("mapreq", c, e);
+
+	if (c == 0 || c->window != e->window) {
+		/* workaround for stupid NCDware */
+		fprintf(stderr, "9wm: bad mapreq c %x w %x, rescanning\n",
+			c, e->window);
+		for (i = 0; i < num_screens; i++)
+			scanwins(&screens[i]);
+		c = getclient(e->window, 0);
+		if (c == 0 || c->window != e->window) {
+			fprintf(stderr, "9wm: window not found after rescan\n");
+			return;
+		}
+	}
+
+	switch (c->state) {
+	case WithdrawnState:
+		if (c->parent == c->screen->root) {
+			if (!manage(c, 0))
+				return;
+			break;
+		}
+		XReparentWindow(dpy, c->window, c->parent, BORDER-1, BORDER-1);
+		XAddToSaveSet(dpy, c->window);
+		/* fall through... */
+	case NormalState:
+		XMapWindow(dpy, c->window);
+		XMapRaised(dpy, c->parent);
+		top(c);
+		setstate(c, NormalState);
+		if (c->trans != None && current && c->trans == current->window)
+				active(c);
+		break;
+	case IconicState:
+		unhidec(c, 1);
+		break;
+	}
+}
+
+void
+unmap(XUnmapEvent *e)
+{
+	Client *c;
+
+	curtime = CurrentTime;
+	c = getclient(e->window, 0);
+	if (c) {
+		switch (c->state) {
+		case IconicState:
+			if (e->send_event) {
+				unhidec(c, 0);
+				withdraw(c);
+			}
+			break;
+		case NormalState:
+			if (c == current)
+				nofocus();
+			if (!c->reparenting)
+				withdraw(c);
+			break;
+		}
+		c->reparenting = 0;
+	}
+}
+
+void
+circulatereq(XCirculateRequestEvent *e)
+{
+	fprintf(stderr, "It must be the warlock Krill!\n");  /* ☺ */
+}
+
+void
+newwindow(XCreateWindowEvent *e)
+{
+	Client *c;
+	ScreenInfo *s;
+
+	/* we don't set curtime as nothing here uses it */
+	if (e->override_redirect)
+		return;
+	c = getclient(e->window, 1);
+	if (c && c->window == e->window && (s = getscreen(e->parent))) {
+		c->x = e->x;
+		c->y = e->y;
+		c->dx = e->width;
+		c->dy = e->height;
+		c->border = e->border_width;
+		c->screen = s;
+		if (c->parent == None)
+			c->parent = c->screen->root;
+	}
+}
+
+void
+destroy(Window w)
+{
+	Client *c;
+
+	curtime = CurrentTime;
+	c = getclient(w, 0);
+	if (c == 0)
+		return;
+
+	rmclient(c);
+
+	/* flush any errors generated by the window's sudden demise */
+	ignore_badwindow = 1;
+	XSync(dpy, False);
+	ignore_badwindow = 0;
+}
+
+void
+clientmesg(XClientMessageEvent *e)
+{
+	Client *c;
+
+	curtime = CurrentTime;
+	if (e->message_type == exit_9wm) {
+		cleanup();
+		exit(0);
+	}
+	if (e->message_type == restart_9wm) {
+		fprintf(stderr, "*** 9wm restarting ***\n");
+		cleanup();
+		execvp(myargv[0], myargv);
+		perror("9wm: exec failed");
+		exit(1);
+	}
+	if (e->message_type == wm_change_state) {
+		c = getclient(e->window, 0);
+		if (e->format == 32 && e->data.l[0] == IconicState && c != 0) {
+			if (normal(c))
+				hide(c);
+		}
+		else
+			fprintf(stderr, "9wm: WM_CHANGE_STATE: format %d data %d w 0x%x\n",
+				e->format, e->data.l[0], e->window);
+		return;
+	}
+	fprintf(stderr, "9wm: strange ClientMessage, type 0x%x window 0x%x\n",
+		e->message_type, e->window);
+}
+
+void
+cmap(XColormapEvent *e)
+{
+	Client *c;
+	int i;
+
+	/* we don't set curtime as nothing here uses it */
+	if (e->new) {
+		c = getclient(e->window, 0);
+		if (c) {
+			c->cmap = e->colormap;
+			if (c == current)
+				cmapfocus(c);
+		}
+		else
+			for (c = clients; c; c = c->next) {
+				for (i = 0; i < c->ncmapwins; i++)
+					if (c->cmapwins[i] == e->window) {
+						c->wmcmaps[i] = e->colormap;
+						if (c == current)
+							cmapfocus(c);
+						return;
+					}
+			}
+	}
+}
+
+void
+property(XPropertyEvent *e)
+{
+	Atom a;
+	int delete;
+	Client *c;
+
+	/* we don't set curtime as nothing here uses it */
+	a = e->atom;
+	delete = (e->state == PropertyDelete);
+	c = getclient(e->window, 0);
+	if (c == 0)
+		return;
+
+	switch (a) {
+	case XA_WM_ICON_NAME:
+		if (c->iconname != 0)
+			XFree((char*) c->iconname);
+		c->iconname = delete ? 0 : getprop(c->window, a);
+		setlabel(c);
+		renamec(c, c->label);
+		return;
+	case XA_WM_NAME:
+		if (c->name != 0)
+			XFree((char*) c->name);
+		c->name = delete ? 0 : getprop(c->window, a);
+		setlabel(c);
+		renamec(c, c->label);
+		return;
+	case XA_WM_TRANSIENT_FOR:
+		gettrans(c);
+		return;
+	}
+	if (a == _9wm_hold_mode) {
+		c->hold = getiprop(c->window, _9wm_hold_mode);
+		if (c == current)
+			draw_border(c, 1);
+	}
+	else if (a == wm_colormaps) {
+		getcmaps(c);
+		if (c == current)
+			cmapfocus(c);
+	}
+}
+
+void
+reparent(XReparentEvent *e)
+{
+	Client *c;
+	XWindowAttributes attr;
+	ScreenInfo *s;
+
+	/* we don't set curtime as nothing here uses it */
+	if (!getscreen(e->event) || e->override_redirect)
+		return;
+	if ((s = getscreen(e->parent)) != 0) {
+		c = getclient(e->window, 1);
+		if (c != 0 && (c->dx == 0 || c->dy == 0)) {
+			XGetWindowAttributes(dpy, c->window, &attr);
+			c->x = attr.x;
+			c->y = attr.y;
+			c->dx = attr.width;
+			c->dy = attr.height;
+			c->border = attr.border_width;
+			c->screen = s;
+			if (c->parent == None)
+				c->parent = c->screen->root;
+		}
+	}
+	else {
+		c = getclient(e->window, 0);
+		if (c != 0 && (c->parent == c->screen->root || withdrawn(c)))
+			rmclient(c);
+	}
+}
+
+#ifdef	SHAPE
+void
+shapenotify(XShapeEvent *e)
+{
+	Client *c;
+
+	/* we don't set curtime as nothing here uses it */
+	c = getclient(e->window, 0);
+	if (c == 0)
+		return;
+
+	setshape(c);
+}
+#endif
+
+void
+enter(XCrossingEvent *e)
+{
+	Client *c;
+
+	curtime = e->time;
+	if (e->mode != NotifyGrab || e->detail != NotifyNonlinearVirtual)
+		return;
+	c = getclient(e->window, 0);
+	if (c != 0 && c != current) {
+		/* someone grabbed the pointer; make them current */
+		XMapRaised(dpy, c->parent);
+		top(c);
+		active(c);
+	}
+}
+
+void
+focusin(XFocusChangeEvent *e)
+{
+	Client *c;
+
+	curtime = CurrentTime;
+	if (e->detail != NotifyNonlinearVirtual)
+		return;
+	c = getclient(e->window, 0);
+	if (c != 0 && c->window == e->window && c != current) {
+		/* someone grabbed keyboard or seized focus; make them current */
+		XMapRaised(dpy, c->parent);
+		top(c);
+		active(c);
+	}
+}
diff --git a/src/cmd/rio/fns.h b/src/cmd/rio/fns.h
new file mode 100644
index 0000000..bfb9df8
--- /dev/null
+++ b/src/cmd/rio/fns.h
@@ -0,0 +1,96 @@
+/* Copyright (c) 1994-1996 David Hogan, see README for licence details */
+
+#ifdef	DEBUG
+#define	trace(s, c, e)	dotrace((s), (c), (e))
+#else
+#define	trace(s, c, e)
+#endif
+
+/* color.c */
+unsigned long colorpixel(Display*, int, unsigned long);
+
+/* main.c */
+void	usage();
+void	initscreen();
+ScreenInfo *getscreen();
+Time	timestamp();
+void	sendcmessage();
+void	sendconfig();
+void	sighandler();
+void	getevent();
+void	cleanup();
+
+/* event.c */
+void	mainloop();
+void	configurereq();
+void	mapreq();
+void	circulatereq();
+void	unmap();
+void	newwindow();
+void	destroy();
+void	clientmesg();
+void	cmap();
+void	property();
+void	shapenotify();
+void	enter();
+void	focusin();
+void	reparent();
+
+/* manage.c */
+int 	manage();
+void	scanwins();
+void	setshape();
+void	withdraw();
+void	gravitate();
+void	cmapfocus();
+void	cmapnofocus();
+void	getcmaps();
+int 	_getprop();
+char	*getprop();
+Window	getwprop();
+int 	getiprop();
+int 	getstate();
+void	setstate();
+void	setlabel();
+void	getproto();
+void	gettrans();
+
+/* menu.c */
+void	button();
+void	spawn();
+void	reshape();
+void	move();
+void	delete();
+void	hide();
+void	unhide();
+void	unhidec();
+void	renamec();
+
+/* client.c */
+void	setactive();
+void	draw_border();
+void	active();
+void	nofocus();
+void	top();
+Client	*getclient();
+void	rmclient();
+void	dump_revert();
+void	dump_clients();
+
+/* grab.c */
+int 	menuhit();
+Client	*selectwin();
+int 	sweep();
+int 	drag();
+void	getmouse();
+void	setmouse();
+
+/* error.c */
+int 	handler();
+void	fatal();
+void	graberror();
+void	showhints();
+void	dotrace();
+
+/* cursor.c */
+void	initcurs();
diff --git a/src/cmd/rio/grab.c b/src/cmd/rio/grab.c
new file mode 100644
index 0000000..233233d
--- /dev/null
+++ b/src/cmd/rio/grab.c
@@ -0,0 +1,498 @@
+/* Copyright (c) 1994-1996 David Hogan, see README for licence details */
+#include <stdio.h>
+#include <X11/X.h>
+#include <X11/Xos.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include "dat.h"
+#include "fns.h"
+
+int
+nobuttons(XButtonEvent *e)	/* Einstuerzende */
+{
+	int state;
+
+	state = (e->state & AllButtonMask);
+	return (e->type == ButtonRelease) && (state & (state - 1)) == 0;
+}
+
+int
+grab(Window w, Window constrain, int mask, Cursor curs, int t)
+{
+	int status;
+
+	if (t == 0)
+		t = timestamp();
+	status = XGrabPointer(dpy, w, False, mask,
+		GrabModeAsync, GrabModeAsync, constrain, curs, t);
+	return status;
+}
+
+void
+ungrab(XButtonEvent *e)
+{
+	XEvent ev;
+
+	if (!nobuttons(e))
+		for (;;) {
+			XMaskEvent(dpy, ButtonMask | ButtonMotionMask, &ev);
+			if (ev.type == MotionNotify)
+				continue;
+			e = &ev.xbutton;
+			if (nobuttons(e))
+				break;
+		}
+	XUngrabPointer(dpy, e->time);
+	curtime = e->time;
+}
+
+static void
+drawstring(Display *dpy, ScreenInfo *s, Menu *m, int wide, int high, int i, int selected)
+{
+	int tx, ty;
+
+	tx = (wide - XTextWidth(font, m->item[i], strlen(m->item[i])))/2;
+	ty = i*high + font->ascent + 1;
+	XFillRectangle(dpy, s->menuwin, selected ? s->gcmenubgs : s->gcmenubg, 0, i*high, wide, high);
+	XDrawString(dpy, s->menuwin, selected ? s->gcmenufgs : s->gcmenufg, tx, ty, m->item[i], strlen(m->item[i]));
+}
+
+int
+menuhit(XButtonEvent *e, Menu *m)
+{
+	XEvent ev;
+	int i, n, cur, old, wide, high, status, drawn, warp;
+	int x, y, dx, dy, xmax, ymax;
+	int tx, ty;
+	ScreenInfo *s;
+
+	if (font == 0)
+		return -1;
+	s = getscreen(e->root);
+	if (s == 0 || e->window == s->menuwin)	   /* ugly event mangling */
+		return -1;
+
+	dx = 0;
+	for (n = 0; m->item[n]; n++) {
+		wide = XTextWidth(font, m->item[n], strlen(m->item[n])) + 4;
+		if (wide > dx)
+			dx = wide;
+	}
+	wide = dx;
+	cur = m->lasthit;
+	if (cur >= n)
+		cur = n - 1;
+
+	high = font->ascent + font->descent + 1;
+	dy = n*high;
+	x = e->x - wide/2;
+	y = e->y - cur*high - high/2;
+	warp = 0;
+	xmax = DisplayWidth(dpy, s->num);
+	ymax = DisplayHeight(dpy, s->num);
+	if (x < 0) {
+		e->x -= x;
+		x = 0;
+		warp++;
+	}
+	if (x+wide >= xmax) {
+		e->x -= x+wide-xmax;
+		x = xmax-wide;
+		warp++;
+	}
+	if (y < 0) {
+		e->y -= y;
+		y = 0;
+		warp++;
+	}
+	if (y+dy >= ymax) {
+		e->y -= y+dy-ymax;
+		y = ymax-dy;
+		warp++;
+	}
+	if (warp)
+		setmouse(e->x, e->y, s);
+	XMoveResizeWindow(dpy, s->menuwin, x, y, dx, dy);
+	XSelectInput(dpy, s->menuwin, MenuMask);
+	XMapRaised(dpy, s->menuwin);
+	status = grab(s->menuwin, None, MenuGrabMask, None, e->time);
+	if (status != GrabSuccess) {
+		/* graberror("menuhit", status); */
+		XUnmapWindow(dpy, s->menuwin);
+		return -1;
+	}
+	drawn = 0;
+	for (;;) {
+		XMaskEvent(dpy, MenuMask, &ev);
+		switch (ev.type) {
+		default:
+			fprintf(stderr, "9wm: menuhit: unknown ev.type %d\n", ev.type);
+			break;
+		case ButtonPress:
+			break;
+		case ButtonRelease:
+			if (ev.xbutton.button != e->button)
+				break;
+			x = ev.xbutton.x;
+			y = ev.xbutton.y;
+			i = y/high;
+			if (cur >= 0 && y >= cur*high-3 && y < (cur+1)*high+3)
+				i = cur;
+			if (x < 0 || x > wide || y < -3)
+				i = -1;
+			else if (i < 0 || i >= n)
+				i = -1;
+			else
+				m->lasthit = i;
+			if (!nobuttons(&ev.xbutton))
+				i = -1;
+			ungrab(&ev.xbutton);
+			XUnmapWindow(dpy, s->menuwin);
+			return i;
+		case MotionNotify:
+			if (!drawn)
+				break;
+			x = ev.xbutton.x;
+			y = ev.xbutton.y;
+			old = cur;
+			cur = y/high;
+			if (old >= 0 && y >= old*high-3 && y < (old+1)*high+3)
+				cur = old;
+			if (x < 0 || x > wide || y < -3)
+				cur = -1;
+			else if (cur < 0 || cur >= n)
+				cur = -1;
+			if (cur == old)
+				break;
+			if (old >= 0 && old < n)
+				drawstring(dpy, s, m, wide, high, old, 0);
+			if (cur >= 0 && cur < n)
+				drawstring(dpy, s, m, wide, high, cur, 1);
+			break;
+		case Expose:
+			XClearWindow(dpy, s->menuwin);
+			for (i = 0; i < n; i++)
+				drawstring(dpy, s, m, wide, high, i, cur==i);
+			drawn = 1;
+		}
+	}
+}
+
+Client *
+selectwin(int release, int *shift, ScreenInfo *s)
+{
+	XEvent ev;
+	XButtonEvent *e;
+	int status;
+	Window w;
+	Client *c;
+
+	status = grab(s->root, s->root, ButtonMask, s->target, 0);
+	if (status != GrabSuccess) {
+		graberror("selectwin", status); /* */
+		return 0;
+	}
+	w = None;
+	for (;;) {
+		XMaskEvent(dpy, ButtonMask, &ev);
+		e = &ev.xbutton;
+		switch (ev.type) {
+		case ButtonPress:
+			if (e->button != Button3) {
+				ungrab(e);
+				return 0;
+			}
+			w = e->subwindow;
+			if (!release) {
+				c = getclient(w, 0);
+				if (c == 0)
+					ungrab(e);
+				if (shift != 0)
+					*shift = (e->state&ShiftMask) != 0;
+				return c;
+			}
+			break;
+		case ButtonRelease:
+			ungrab(e);
+			if (e->button != Button3 || e->subwindow != w)
+				return 0;
+			if (shift != 0)
+				*shift = (e->state&ShiftMask) != 0;
+			return getclient(w, 0);
+		}
+	}
+}
+
+void
+sweepcalc(Client *c, int x, int y)
+{
+	int dx, dy, sx, sy;
+
+	dx = x - c->x;
+	dy = y - c->y;
+	sx = sy = 1;
+	if (dx < 0) {
+		dx = -dx;
+		sx = -1;
+	}
+	if (dy < 0) {
+		dy = -dy;
+		sy = -1;
+	}
+
+	dx -= 2*BORDER;
+	dy -= 2*BORDER;
+
+	if (!c->is9term) {
+		if (dx < c->min_dx)
+			dx = c->min_dx;
+		if (dy < c->min_dy)
+			dy = c->min_dy;
+	}
+
+	if (c->size.flags & PResizeInc) {
+		dx = c->min_dx + (dx-c->min_dx)/c->size.width_inc*c->size.width_inc;
+		dy = c->min_dy + (dy-c->min_dy)/c->size.height_inc*c->size.height_inc;
+	}
+
+	if (c->size.flags & PMaxSize) {
+		if (dx > c->size.max_width)
+			dx = c->size.max_width;
+		if (dy > c->size.max_height)
+			dy = c->size.max_height;
+	}
+	c->dx = sx*(dx + 2*BORDER);
+	c->dy = sy*(dy + 2*BORDER);
+}
+
+void
+dragcalc(Client *c, int x, int y)
+{
+	c->x = x;
+	c->y = y;
+}
+
+static void
+xcopy(int fwd, Display *dpy, Drawable src, Drawable dst, GC gc, int x, int y, int dx, int dy, int x1, int y1)
+{
+	if(fwd)
+		XCopyArea(dpy, src, dst, gc, x, y, dx, dy, x1, y1);
+	else
+		XCopyArea(dpy, dst, src, gc, x1, y1, dx, dy, x, y);
+}
+
+void
+drawbound(Client *c, int drawing)
+{
+	int x, y, dx, dy;
+	ScreenInfo *s;
+	
+	s = c->screen;
+	x = c->x;
+	y = c->y;
+	dx = c->dx;
+	dy = c->dy;
+	if (dx < 0) {
+		x += dx;
+		dx = -dx;
+	}
+	if (dy < 0) {
+		y += dy;
+		dy = -dy;
+	}
+	if (dx <= 2 || dy <= 2)
+		return;
+
+	if(solidsweep){
+		if(drawing == -1){
+			XUnmapWindow(dpy, s->sweepwin);
+			return;
+		}
+		
+		x += BORDER;
+		y += BORDER;
+		dx -= 2*BORDER;
+		dy -= 2*BORDER;
+
+		if(drawing){
+			XMoveResizeWindow(dpy, s->sweepwin, x, y, dx, dy);
+			XSelectInput(dpy, s->sweepwin, MenuMask);
+			XMapRaised(dpy, s->sweepwin);
+		}
+		return;
+	}
+
+	if(drawing == -1)
+		return;
+
+	xcopy(drawing, dpy, s->root, s->bkup[0], s->gccopy, x, y, dx, BORDER, 0, 0);
+	xcopy(drawing, dpy, s->root, s->bkup[0], s->gccopy, x, y+dy-BORDER, dx, BORDER, dx, 0);
+	xcopy(drawing, dpy, s->root, s->bkup[1], s->gccopy, x, y, BORDER, dy, 0, 0);
+	xcopy(drawing, dpy, s->root, s->bkup[1], s->gccopy, x+dx-BORDER, y, BORDER, dy, 0, dy);
+
+	if(drawing){
+		XFillRectangle(dpy, s->root, s->gcred, x, y, dx, BORDER);
+		XFillRectangle(dpy, s->root, s->gcred, x, y+dy-BORDER, dx, BORDER);
+		XFillRectangle(dpy, s->root, s->gcred, x, y, BORDER, dy);
+		XFillRectangle(dpy, s->root, s->gcred, x+dx-BORDER, y, BORDER, dy);
+	}
+}
+
+void
+misleep(int msec)
+{
+	struct timeval t;
+
+	t.tv_sec = msec/1000;
+	t.tv_usec = (msec%1000)*1000;
+	select(0, 0, 0, 0, &t);
+}
+
+int
+sweepdrag(Client *c, XButtonEvent *e0, void (*recalc)(Client*, int, int))
+{
+	XEvent ev;
+	int idle;
+	int cx, cy, rx, ry;
+	int ox, oy, odx, ody;
+	XButtonEvent *e;
+
+	ox = c->x;
+	oy = c->y;
+	odx = c->dx;
+	ody = c->dy;
+	c->x -= BORDER;
+	c->y -= BORDER;
+	c->dx += 2*BORDER;
+	c->dy += 2*BORDER;
+	if (e0) {
+		c->x = cx = e0->x;
+		c->y = cy = e0->y;
+		recalc(c, e0->x, e0->y);
+	}
+	else
+		getmouse(&cx, &cy, c->screen);
+	XGrabServer(dpy);
+	drawbound(c, 1);
+	idle = 0;
+	for (;;) {
+		if (XCheckMaskEvent(dpy, ButtonMask, &ev) == 0) {
+			getmouse(&rx, &ry, c->screen);
+			if (rx != cx || ry != cy || ++idle > 300) {
+				drawbound(c, 0);
+				if (rx == cx && ry == cy) {
+					XUngrabServer(dpy);
+					XFlush(dpy);
+					misleep(500);
+					XGrabServer(dpy);
+					idle = 0;
+				}
+				recalc(c, rx, ry);
+				cx = rx;
+				cy = ry;
+				drawbound(c, 1);
+				XFlush(dpy);
+			}
+			misleep(50);
+			continue;
+		}
+		e = &ev.xbutton;
+		switch (ev.type) {
+		case ButtonPress:
+		case ButtonRelease:
+			drawbound(c, 0);
+			ungrab(e);
+			XUngrabServer(dpy);
+			if (e->button != Button3 && c->init)
+				goto bad;
+			recalc(c, ev.xbutton.x, ev.xbutton.y);
+			if (c->dx < 0) {
+				c->x += c->dx;
+				c->dx = -c->dx;
+			}
+			if (c->dy < 0) {
+				c->y += c->dy;
+				c->dy = -c->dy;
+			}
+			c->x += BORDER;
+			c->y += BORDER;
+			c->dx -= 2*BORDER;
+			c->dy -= 2*BORDER;
+			if (c->dx < 4 || c->dy < 4 || c->dx < c->min_dx || c->dy < c->min_dy)
+				goto bad;
+			return 1;
+		}
+	}
+bad:
+	c->x = ox;
+	c->y = oy;
+	c->dx = odx;
+	c->dy = ody;
+	drawbound(c, -1);
+	return 0;
+}
+
+int
+sweep(Client *c)
+{
+	XEvent ev;
+	int status;
+	XButtonEvent *e;
+	ScreenInfo *s;
+
+	s = c->screen;
+	status = grab(s->root, s->root, ButtonMask, s->sweep0, 0);
+	if (status != GrabSuccess) {
+		graberror("sweep", status); /* */
+		return 0;
+	}
+
+	XMaskEvent(dpy, ButtonMask, &ev);
+	e = &ev.xbutton;
+	if (e->button != Button3) {
+		ungrab(e);
+		return 0;
+	}
+	if (c->size.flags & (PMinSize|PBaseSize))
+		 setmouse(e->x+c->min_dx, e->y+c->min_dy, s);
+	XChangeActivePointerGrab(dpy, ButtonMask, s->boxcurs, e->time);
+	return sweepdrag(c, e, sweepcalc);
+}
+
+int
+drag(Client *c)
+{
+	int status;
+	ScreenInfo *s;
+
+	s = c->screen;
+	if (c->init)
+		setmouse(c->x-BORDER, c->y-BORDER, s);
+	else {
+		getmouse(&c->x, &c->y, s);		   /* start at current mouse pos */
+		c->x += BORDER;
+		c->y += BORDER;
+	}
+	status = grab(s->root, s->root, ButtonMask, s->boxcurs, 0);
+	if (status != GrabSuccess) {
+		graberror("drag", status); /* */
+		return 0;
+	}
+	return sweepdrag(c, 0, dragcalc);
+}
+
+void
+getmouse(int *x, int *y, ScreenInfo *s)
+{
+	Window dw1, dw2;
+	int t1, t2;
+	unsigned int t3;
+
+	XQueryPointer(dpy, s->root, &dw1, &dw2, x, y, &t1, &t2, &t3);
+}
+
+void
+setmouse(int x, int y, ScreenInfo *s)
+{
+	XWarpPointer(dpy, None, s->root, None, None, None, None, x, y);
+}
diff --git a/src/cmd/rio/main.c b/src/cmd/rio/main.c
new file mode 100644
index 0000000..d9bec82
--- /dev/null
+++ b/src/cmd/rio/main.c
@@ -0,0 +1,437 @@
+/* Copyright (c) 1994-1996 David Hogan, see README for licence details */
+#include <stdio.h>
+#include <signal.h>
+#include <errno.h>
+#include <X11/X.h>
+#include <X11/Xos.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/Xatom.h>
+#include "dat.h"
+#include "fns.h"
+#include "patchlevel.h"
+
+char	*version[] =
+{
+	"rio version 1.0, Copyright (c) 1994-1996 David Hogan, (c) 2004 Russ Cox", 0,
+};
+
+Display 		*dpy;
+ScreenInfo	*screens;
+int 			initting;
+XFontStruct 	*font;
+int 			nostalgia;
+char			**myargv;
+char			*termprog;
+char			*shell;
+Bool			shape;
+int 			_border = 4;
+int 			_inset = 1;
+int 			curtime;
+int 			debug;
+int 			signalled;
+int 			num_screens;
+int			solidsweep = 0;
+
+Atom		exit_9wm;
+Atom		restart_9wm;
+Atom		wm_state;
+Atom		wm_change_state;
+Atom		wm_protocols;
+Atom		wm_delete;
+Atom		wm_take_focus;
+Atom		wm_colormaps;
+Atom		_9wm_running;
+Atom		_9wm_hold_mode;
+
+char	*fontlist[] = {
+	"lucm.latin1.9",
+	"blit",
+	"lucidasanstypewriter-bold-10",
+	"9x15bold",
+	"fixed",
+	"*",
+	0,
+};
+
+void
+usage(void)
+{
+	fprintf(stderr, "usage: rio [-grey] [-version] [-font fname] [-term prog] [exit|restart]\n");
+	exit(1);
+}
+
+int
+main(int argc, char *argv[])
+{
+	int i, background, do_exit, do_restart;
+	char *fname;
+	int shape_event, dummy;
+
+	myargv = argv;			/* for restart */
+
+	do_exit = do_restart = 0;
+	background = 1;
+	font = 0;
+	fname = 0;
+	for (i = 1; i < argc; i++)
+		if (strcmp(argv[i], "-nostalgia") == 0)
+			nostalgia++;
+		else if (strcmp(argv[i], "-grey") == 0)
+			background = 1;
+		else if (strcmp(argv[i], "-debug") == 0)
+			debug++;
+		else if (strcmp(argv[i], "-font") == 0 && i+1<argc) {
+			i++;
+			fname = argv[i];
+		}
+		else if (strcmp(argv[i], "-term") == 0 && i+1<argc)
+			termprog = argv[++i];
+		else if (strcmp(argv[i], "-version") == 0) {
+			fprintf(stderr, "%s", version[0]);
+			if (PATCHLEVEL > 0)
+				fprintf(stderr, "; patch level %d", PATCHLEVEL);
+			fprintf(stderr, "\n");
+			exit(0);
+		}
+		else if (argv[i][0] == '-')
+			usage();
+		else
+			break;
+	for (; i < argc; i++)
+		if (strcmp(argv[i], "exit") == 0)
+			do_exit++;
+		else if (strcmp(argv[i], "restart") == 0)
+			do_restart++;
+		else
+			usage();
+
+	if (do_exit && do_restart)
+		usage();
+
+	shell = (char *)getenv("SHELL");
+	if (shell == NULL)
+		shell = DEFSHELL;
+
+	dpy = XOpenDisplay("");
+	if (dpy == 0)
+		fatal("can't open display");
+
+	initting = 1;
+	XSetErrorHandler(handler);
+	if (signal(SIGTERM, sighandler) == SIG_IGN)
+		signal(SIGTERM, SIG_IGN);
+	if (signal(SIGINT, sighandler) == SIG_IGN)
+		signal(SIGINT, SIG_IGN);
+	if (signal(SIGHUP, sighandler) == SIG_IGN)
+		signal(SIGHUP, SIG_IGN);
+
+	exit_9wm = XInternAtom(dpy, "9WM_EXIT", False);
+	restart_9wm = XInternAtom(dpy, "9WM_RESTART", False);
+
+	curtime = -1;		/* don't care */
+	if (do_exit) {
+		sendcmessage(DefaultRootWindow(dpy), exit_9wm, 0L, 1);
+		XSync(dpy, False);
+		exit(0);
+	}
+	if (do_restart) {
+		sendcmessage(DefaultRootWindow(dpy), restart_9wm, 0L, 1);
+		XSync(dpy, False);
+		exit(0);
+	}
+
+	wm_state = XInternAtom(dpy, "WM_STATE", False);
+	wm_change_state = XInternAtom(dpy, "WM_CHANGE_STATE", False);
+	wm_protocols = XInternAtom(dpy, "WM_PROTOCOLS", False);
+	wm_delete = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
+	wm_take_focus = XInternAtom(dpy, "WM_TAKE_FOCUS", False);
+	wm_colormaps = XInternAtom(dpy, "WM_COLORMAP_WINDOWS", False);
+	_9wm_running = XInternAtom(dpy, "_9WM_RUNNING", False);
+	_9wm_hold_mode = XInternAtom(dpy, "_9WM_HOLD_MODE", False);
+
+	if (fname != 0)
+		if ((font = XLoadQueryFont(dpy, fname)) == 0)
+			fprintf(stderr, "9wm: warning: can't load font %s\n", fname);
+
+	if (font == 0) {
+		i = 0;
+		for (;;) {
+			fname = fontlist[i++];
+			if (fname == 0) {
+				fprintf(stderr, "9wm: warning: can't find a font\n");
+				break;
+			}
+			font = XLoadQueryFont(dpy, fname);
+			if (font != 0)
+				break;
+		}
+	}
+	if (nostalgia) {
+		_border--;
+		_inset--;
+	}
+
+#ifdef	SHAPE
+	shape = XShapeQueryExtension(dpy, &shape_event, &dummy);
+#endif
+
+	num_screens = ScreenCount(dpy);
+	screens = (ScreenInfo *)malloc(sizeof(ScreenInfo) * num_screens);
+
+	for (i = 0; i < num_screens; i++)
+		initscreen(&screens[i], i, background);
+
+	/* set selection so that 9term knows we're running */
+	curtime = CurrentTime;
+	XSetSelectionOwner(dpy, _9wm_running, screens[0].menuwin, timestamp());
+
+	XSync(dpy, False);
+	initting = 0;
+
+	nofocus();
+
+	for (i = 0; i < num_screens; i++)
+		scanwins(&screens[i]);
+
+	mainloop(shape_event);
+}
+
+void
+initscreen(ScreenInfo *s, int i, int background)
+{
+	char *ds, *colon, *dot1;
+	unsigned long mask;
+	XGCValues gv;
+	XSetWindowAttributes attr;
+
+	s->num = i;
+	s->root = RootWindow(dpy, i);
+	s->def_cmap = DefaultColormap(dpy, i);
+	s->min_cmaps = MinCmapsOfScreen(ScreenOfDisplay(dpy, i));
+	s->depth = DefaultDepth(dpy, i);
+
+	ds = DisplayString(dpy);
+	colon = rindex(ds, ':');
+	if (colon && num_screens > 1) {
+		strcpy(s->display, "DISPLAY=");
+		strcat(s->display, ds);
+		colon = s->display + 8 + (colon - ds);	/* use version in buf */
+		dot1 = index(colon, '.');	/* first period after colon */
+		if (!dot1)
+			dot1 = colon + strlen(colon);	/* if not there, append */
+		sprintf(dot1, ".%d", i);
+	}
+	else
+		s->display[0] = '\0';
+
+	s->activeholdborder = colorpixel(dpy, s->depth, 0x000099);
+	s->inactiveholdborder = colorpixel(dpy, s->depth, 0x005DBB);
+	s->activeborder = colorpixel(dpy, s->depth ,0x55AAAA);
+	s->inactiveborder = colorpixel(dpy, s->depth, 0x9EEEEE);
+	s->red = colorpixel(dpy, s->depth, 0xDD0000);
+	s->black = BlackPixel(dpy, i);
+	s->white = WhitePixel(dpy, i);
+	s->width = WidthOfScreen(ScreenOfDisplay(dpy, i));
+	s->height = HeightOfScreen(ScreenOfDisplay(dpy, i));
+	s->bkup[0] = XCreatePixmap(dpy, s->root, 2*s->width, BORDER, DefaultDepth(dpy, i));
+	s->bkup[1] = XCreatePixmap(dpy, s->root, BORDER, 2*s->height, DefaultDepth(dpy, i));
+
+	gv.foreground = s->black^s->white;
+	gv.background = s->white;
+	gv.function = GXxor;
+	gv.line_width = 0;
+	gv.subwindow_mode = IncludeInferiors;
+	mask = GCForeground | GCBackground | GCFunction | GCLineWidth
+		| GCSubwindowMode;
+	if (font != 0) {
+		gv.font = font->fid;
+		mask |= GCFont;
+	}
+	s->gc = XCreateGC(dpy, s->root, mask, &gv);
+
+	gv.function = GXcopy;
+	s->gccopy = XCreateGC(dpy, s->root, mask, &gv);
+
+	gv.foreground = s->red;
+	s->gcred = XCreateGC(dpy, s->root, mask, &gv);
+
+	gv.foreground = colorpixel(dpy, s->depth, 0xEEEEEE);
+	s->gcsweep = XCreateGC(dpy, s->root, mask, &gv);
+
+	gv.foreground = colorpixel(dpy, s->depth, 0xE9FFE9);
+	s->gcmenubg = XCreateGC(dpy, s->root, mask, &gv);
+
+	gv.foreground = colorpixel(dpy, s->depth, 0x448844);
+	s->gcmenubgs = XCreateGC(dpy, s->root, mask, &gv);
+
+	gv.foreground = s->black;
+	gv.background = colorpixel(dpy, s->depth, 0xE9FFE9);
+	s->gcmenufg = XCreateGC(dpy, s->root, mask, &gv);
+
+	gv.foreground = colorpixel(dpy, s->depth, 0xE9FFE9);
+	gv.background = colorpixel(dpy, s->depth, 0x448844);
+	s->gcmenufgs = XCreateGC(dpy, s->root, mask, &gv);
+
+	initcurs(s);
+
+	attr.cursor = s->arrow;
+	attr.event_mask = SubstructureRedirectMask
+		| SubstructureNotifyMask | ColormapChangeMask
+		| ButtonPressMask | ButtonReleaseMask | PropertyChangeMask;
+	mask = CWCursor|CWEventMask;
+	XChangeWindowAttributes(dpy, s->root, mask, &attr);
+	XSync(dpy, False);
+
+	if (background) {
+/*
+		XSetWindowBackgroundPixmap(dpy, s->root, s->root_pixmap);
+		XClearWindow(dpy, s->root);
+*/
+		system("xsetroot -solid grey30");
+	}
+	s->menuwin = XCreateSimpleWindow(dpy, s->root, 0, 0, 1, 1, 2, colorpixel(dpy, s->depth, 0xAAFFAA), colorpixel(dpy, s->depth, 0xE9FFE9));
+	s->sweepwin = XCreateSimpleWindow(dpy, s->root, 0, 0, 1, 1, 4, s->red, colorpixel(dpy, s->depth, 0xEEEEEE));
+}
+
+ScreenInfo*
+getscreen(Window w)
+{
+	int i;
+
+	for (i = 0; i < num_screens; i++)
+		if (screens[i].root == w)
+			return &screens[i];
+
+	return 0;
+}
+
+Time
+timestamp(void)
+{
+	XEvent ev;
+
+	if (curtime == CurrentTime) {
+		XChangeProperty(dpy, screens[0].root, _9wm_running, _9wm_running, 8,
+				PropModeAppend, (unsigned char *)"", 0);
+		XMaskEvent(dpy, PropertyChangeMask, &ev);
+		curtime = ev.xproperty.time;
+	}
+	return curtime;
+}
+
+void
+sendcmessage(Window w, Atom a, long x, int isroot)
+{
+	XEvent ev;
+	int status;
+	long mask;
+
+	memset(&ev, 0, sizeof(ev));
+	ev.xclient.type = ClientMessage;
+	ev.xclient.window = w;
+	ev.xclient.message_type = a;
+	ev.xclient.format = 32;
+	ev.xclient.data.l[0] = x;
+	ev.xclient.data.l[1] = timestamp();
+	mask = 0L;
+	if (isroot)
+		mask = SubstructureRedirectMask;		/* magic! */
+	status = XSendEvent(dpy, w, False, mask, &ev);
+	if (status == 0)
+		fprintf(stderr, "9wm: sendcmessage failed\n");
+}
+
+void
+sendconfig(Client *c)
+{
+	XConfigureEvent ce;
+
+	ce.type = ConfigureNotify;
+	ce.event = c->window;
+	ce.window = c->window;
+	ce.x = c->x;
+	ce.y = c->y;
+	ce.width = c->dx;
+	ce.height = c->dy;
+	ce.border_width = c->border;
+	ce.above = None;
+	ce.override_redirect = 0;
+	XSendEvent(dpy, c->window, False, StructureNotifyMask, (XEvent*)&ce);
+}
+
+void
+sighandler(void)
+{
+	signalled = 1;
+}
+
+void
+getevent(XEvent *e)
+{
+	int fd;
+	fd_set rfds;
+	struct timeval t;
+
+	if (!signalled) {
+		if (QLength(dpy) > 0) {
+			XNextEvent(dpy, e);
+			return;
+		}
+		fd = ConnectionNumber(dpy);
+		FD_ZERO(&rfds);
+		FD_SET(fd, &rfds);
+		t.tv_sec = t.tv_usec = 0;
+		if (select(fd+1, &rfds, NULL, NULL, &t) == 1) {
+			XNextEvent(dpy, e);
+			return;
+		}
+		XFlush(dpy);
+		FD_SET(fd, &rfds);
+		if (select(fd+1, &rfds, NULL, NULL, NULL) == 1) {
+			XNextEvent(dpy, e);
+			return;
+		}
+		if (errno != EINTR || !signalled) {
+			perror("9wm: select failed");
+			exit(1);
+		}
+	}
+	fprintf(stderr, "9wm: exiting on signal\n");
+	cleanup();
+	exit(1);
+}
+
+void
+cleanup(void)
+{
+	Client *c, *cc[2], *next;
+	XWindowChanges wc;
+	int i;
+
+	/* order of un-reparenting determines final stacking order... */
+	cc[0] = cc[1] = 0;
+	for (c = clients; c; c = next) {
+		next = c->next;
+		i = normal(c);
+		c->next = cc[i];
+		cc[i] = c;
+	}
+
+	for (i = 0; i < 2; i++) {
+		for (c = cc[i]; c; c = c->next) {
+			if (!withdrawn(c)) {
+				gravitate(c, 1);
+				XReparentWindow(dpy, c->window, c->screen->root,
+						c->x, c->y);
+			}
+			wc.border_width = c->border;
+			XConfigureWindow(dpy, c->window, CWBorderWidth, &wc);
+		}
+	}
+
+	XSetInputFocus(dpy, PointerRoot, RevertToPointerRoot, timestamp());
+	for (i = 0; i < num_screens; i++)
+		cmapnofocus(&screens[i]);
+	XCloseDisplay(dpy);
+}
diff --git a/src/cmd/rio/manage.c b/src/cmd/rio/manage.c
new file mode 100644
index 0000000..3384daf
--- /dev/null
+++ b/src/cmd/rio/manage.c
@@ -0,0 +1,482 @@
+/* Copyright (c) 1994-1996 David Hogan, see README for licence details */
+#include <stdio.h>
+#include <X11/X.h>
+#include <X11/Xos.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/Xatom.h>
+#include <X11/extensions/shape.h>
+#include "dat.h"
+#include "fns.h"
+
+int
+manage(Client *c, int mapped)
+{
+	int fixsize, dohide, doreshape, state;
+	long msize;
+	XClassHint class;
+	XWMHints *hints;
+
+	trace("manage", c, 0);
+	XSelectInput(dpy, c->window, ColormapChangeMask | EnterWindowMask | PropertyChangeMask | FocusChangeMask);
+
+	/* Get loads of hints */
+
+	if (XGetClassHint(dpy, c->window, &class) != 0) {	/* ``Success'' */
+		c->instance = class.res_name;
+		c->class = class.res_class;
+		c->is9term = (strcmp(c->class, "9term") == 0);
+	}
+	else {
+		c->instance = 0;
+		c->class = 0;
+		c->is9term = 0;
+	}
+	c->iconname = getprop(c->window, XA_WM_ICON_NAME);
+	c->name = getprop(c->window, XA_WM_NAME);
+	setlabel(c);
+
+	hints = XGetWMHints(dpy, c->window);
+	if (XGetWMNormalHints(dpy, c->window, &c->size, &msize) == 0 || c->size.flags == 0)
+		c->size.flags = PSize;		/* not specified - punt */
+
+	getcmaps(c);
+	getproto(c);
+	gettrans(c);
+	if (c->is9term)
+		c->hold = getiprop(c->window, _9wm_hold_mode);
+
+	/* Figure out what to do with the window from hints */
+
+	if (!getstate(c->window, &state))
+		state = hints ? hints->initial_state : NormalState;
+	dohide = (state == IconicState);
+
+	fixsize = 0;
+	if ((c->size.flags & (USSize|PSize)))
+		fixsize = 1;
+	if ((c->size.flags & (PMinSize|PMaxSize)) == (PMinSize|PMaxSize) && c->size.min_width == c->size.max_width && c->size.min_height == c->size.max_height)
+		fixsize = 1;
+	doreshape = !mapped;
+	if (fixsize) {
+		if (c->size.flags & USPosition)
+			doreshape = 0;
+		if (dohide && (c->size.flags & PPosition))
+			doreshape = 0;
+		if (c->trans != None)
+			doreshape = 0;
+	}
+	if (c->is9term)
+		fixsize = 0;
+	if (c->size.flags & PBaseSize) {
+		c->min_dx = c->size.base_width;
+		c->min_dy = c->size.base_height;
+	}
+	else if (c->size.flags & PMinSize) {
+		c->min_dx = c->size.min_width;
+		c->min_dy = c->size.min_height;
+	}
+	else if (c->is9term) {
+		c->min_dx = 100;
+		c->min_dy = 50;
+	}
+	else
+		c->min_dx = c->min_dy = 0;
+
+	if (hints)
+		XFree(hints);
+
+	/* Now do it!!! */
+
+	if (doreshape) {
+		if (current && current->screen == c->screen)
+			cmapnofocus(c->screen);
+		if (!c->is9term && c->x==0 && c->y==0) {
+			static int nwin;
+
+			c->x = 20*nwin+BORDER;
+			c->y = 20*nwin+BORDER;
+			nwin++;
+			nwin %= 10;
+		}
+
+		if (c->is9term && !(fixsize ? drag(c) : sweep(c))) {
+			XKillClient(dpy, c->window);
+			rmclient(c);
+			if (current && current->screen == c->screen)
+				cmapfocus(current);
+			return 0;
+		}
+	}
+	else
+		gravitate(c, 0);
+
+	c->parent = XCreateSimpleWindow(dpy, c->screen->root,
+			c->x - BORDER, c->y - BORDER,
+			c->dx + 2*BORDER, c->dy + 2*BORDER,
+			0, c->screen->black, c->screen->white);
+	XSelectInput(dpy, c->parent, SubstructureRedirectMask | SubstructureNotifyMask);
+	if (mapped)
+		c->reparenting = 1;
+	if (doreshape && !fixsize)
+		XResizeWindow(dpy, c->window, c->dx, c->dy);
+	XSetWindowBorderWidth(dpy, c->window, 0);
+	XReparentWindow(dpy, c->window, c->parent, BORDER, BORDER);
+#ifdef	SHAPE
+	if (shape) {
+		XShapeSelectInput(dpy, c->window, ShapeNotifyMask);
+		ignore_badwindow = 1;		/* magic */
+		setshape(c);
+		ignore_badwindow = 0;
+	}
+#endif
+	XAddToSaveSet(dpy, c->window);
+	if (dohide)
+		hide(c);
+	else {
+		XMapWindow(dpy, c->window);
+		XMapWindow(dpy, c->parent);
+		XUnmapWindow(dpy, c->screen->sweepwin);
+		if (nostalgia || doreshape)
+			active(c);
+		else if (c->trans != None && current && current->window == c->trans)
+			active(c);
+		else
+			setactive(c, 0);
+		setstate(c, NormalState);
+	}
+	if (current && (current != c))
+		cmapfocus(current);
+	c->init = 1;
+	return 1;
+}
+
+void
+scanwins(ScreenInfo *s)
+{
+	unsigned int i, nwins;
+	Client *c;
+	Window dw1, dw2, *wins;
+	XWindowAttributes attr;
+
+	XQueryTree(dpy, s->root, &dw1, &dw2, &wins, &nwins);
+	for (i = 0; i < nwins; i++) {
+		XGetWindowAttributes(dpy, wins[i], &attr);
+		if (attr.override_redirect || wins[i] == s->menuwin)
+			continue;
+		c = getclient(wins[i], 1);
+		if (c != 0 && c->window == wins[i] && !c->init) {
+			c->x = attr.x;
+			c->y = attr.y;
+			c->dx = attr.width;
+			c->dy = attr.height;
+			c->border = attr.border_width;
+			c->screen = s;
+			c->parent = s->root;
+			if (attr.map_state == IsViewable)
+				manage(c, 1);
+		}
+	}
+	XFree((void *) wins);	/* cast is to shut stoopid compiler up */
+}
+
+void
+gettrans(Client *c)
+{
+	Window trans;
+
+	trans = None;
+	if (XGetTransientForHint(dpy, c->window, &trans) != 0)
+		c->trans = trans;
+	else
+		c->trans = None;
+}
+
+void
+withdraw(Client *c)
+{
+	XUnmapWindow(dpy, c->parent);
+	gravitate(c, 1);
+	XReparentWindow(dpy, c->window, c->screen->root, c->x, c->y);
+	gravitate(c, 0);
+	XRemoveFromSaveSet(dpy, c->window);
+	setstate(c, WithdrawnState);
+
+	/* flush any errors */
+	ignore_badwindow = 1;
+	XSync(dpy, False);
+	ignore_badwindow = 0;
+}
+
+void
+gravitate(Client *c, int invert)
+{
+	int gravity, dx, dy, delta;
+
+	gravity = NorthWestGravity;
+	if (c->size.flags & PWinGravity)
+		gravity = c->size.win_gravity;
+
+	delta = c->border-BORDER;
+	switch (gravity) {
+	case NorthWestGravity:
+		dx = 0;
+		dy = 0;
+		break;
+	case NorthGravity:
+		dx = delta;
+		dy = 0;
+		break;
+	case NorthEastGravity:
+		dx = 2*delta;
+		dy = 0;
+		break;
+	case WestGravity:
+		dx = 0;
+		dy = delta;
+		break;
+	case CenterGravity:
+	case StaticGravity:
+		dx = delta;
+		dy = delta;
+		break;
+	case EastGravity:
+		dx = 2*delta;
+		dy = delta;
+		break;
+	case SouthWestGravity:
+		dx = 0;
+		dy = 2*delta;
+		break;
+	case SouthGravity:
+		dx = delta;
+		dy = 2*delta;
+		break;
+	case SouthEastGravity:
+		dx = 2*delta;
+		dy = 2*delta;
+		break;
+	default:
+		fprintf(stderr, "9wm: bad window gravity %d for 0x%x\n", gravity, c->window);
+		return;
+	}
+	dx += BORDER;
+	dy += BORDER;
+	if (invert) {
+		dx = -dx;
+		dy = -dy;
+	}
+	c->x += dx;
+	c->y += dy;
+}
+
+static void
+installcmap(ScreenInfo *s, Colormap cmap)
+{
+	if (cmap == None)
+		XInstallColormap(dpy, s->def_cmap);
+	else
+		XInstallColormap(dpy, cmap);
+}
+
+void
+cmapfocus(Client *c)
+{
+	int i, found;
+	Client *cc;
+
+	if (c == 0)
+		return;
+	else if (c->ncmapwins != 0) {
+		found = 0;
+		for (i = c->ncmapwins-1; i >= 0; i--) {
+			installcmap(c->screen, c->wmcmaps[i]);
+			if (c->cmapwins[i] == c->window)
+				found++;
+		}
+		if (!found)
+			installcmap(c->screen, c->cmap);
+	}
+	else if (c->trans != None && (cc = getclient(c->trans, 0)) != 0 && cc->ncmapwins != 0)
+		cmapfocus(cc);
+	else
+		installcmap(c->screen, c->cmap);
+}
+
+void
+cmapnofocus(ScreenInfo *s)
+{
+	installcmap(s, None);
+}
+
+void
+getcmaps(Client *c)
+{
+	int n, i;
+	Window *cw;
+	XWindowAttributes attr;
+
+	if (!c->init) {
+		XGetWindowAttributes(dpy, c->window, &attr);
+		c->cmap = attr.colormap;
+	}
+
+	n = _getprop(c->window, wm_colormaps, XA_WINDOW, 100L, (unsigned char **)&cw);
+	if (c->ncmapwins != 0) {
+		XFree((char *)c->cmapwins);
+		free((char *)c->wmcmaps);
+	}
+	if (n <= 0) {
+		c->ncmapwins = 0;
+		return;
+	}
+
+	c->ncmapwins = n;
+	c->cmapwins = cw;
+
+	c->wmcmaps = (Colormap*)malloc(n*sizeof(Colormap));
+	for (i = 0; i < n; i++) {
+		if (cw[i] == c->window)
+			c->wmcmaps[i] = c->cmap;
+		else {
+			XSelectInput(dpy, cw[i], ColormapChangeMask);
+			XGetWindowAttributes(dpy, cw[i], &attr);
+			c->wmcmaps[i] = attr.colormap;
+		}
+	}
+}
+
+void
+setlabel(Client *c)
+{
+	char *label, *p;
+
+	if (c->iconname != 0)
+		label = c->iconname;
+	else if (c->name != 0)
+		label = c->name;
+	else if (c->instance != 0)
+		label = c->instance;
+	else if (c->class != 0)
+		label = c->class;
+	else
+		label = "no label";
+	if ((p = index(label, ':')) != 0)
+		*p = '\0';
+	c->label = label;
+}
+
+#ifdef	SHAPE
+void
+setshape(Client *c)
+{
+	int n, order;
+	XRectangle *rect;
+
+	/* don't try to add a border if the window is non-rectangular */
+	rect = XShapeGetRectangles(dpy, c->window, ShapeBounding, &n, &order);
+	if (n > 1)
+		XShapeCombineShape(dpy, c->parent, ShapeBounding, BORDER, BORDER,
+			c->window, ShapeBounding, ShapeSet);
+	XFree((void*)rect);
+}
+#endif
+
+int
+_getprop(Window w, Atom a, Atom type, long len, unsigned char **p)
+{
+	Atom real_type;
+	int format;
+	unsigned long n, extra;
+	int status;
+
+	status = XGetWindowProperty(dpy, w, a, 0L, len, False, type, &real_type, &format, &n, &extra, p);
+	if (status != Success || *p == 0)
+		return -1;
+	if (n == 0)
+		XFree((void*) *p);
+	/* could check real_type, format, extra here... */
+	return n;
+}
+
+char *
+getprop(Window w, Atom a)
+{
+	unsigned char *p;
+
+	if (_getprop(w, a, XA_STRING, 100L, &p) <= 0)
+		return 0;
+	return (char *)p;
+}
+
+int
+get1prop(Window w, Atom a, Atom type)
+{
+	char **p, *x;
+
+	if (_getprop(w, a, type, 1L, (unsigned char**)&p) <= 0)
+		return 0;
+	x = *p;
+	XFree((void*) p);
+	return (int)x;
+}
+
+Window
+getwprop(Window w, Atom a)
+{
+	return get1prop(w, a, XA_WINDOW);
+}
+
+int
+getiprop(Window w, Atom a)
+{
+	return get1prop(w, a, XA_INTEGER);
+}
+
+void
+setstate(Client *c, int state)
+{
+	long data[2];
+
+	data[0] = (long) state;
+	data[1] = (long) None;
+
+	c->state = state;
+	XChangeProperty(dpy, c->window, wm_state, wm_state, 32,
+		PropModeReplace, (unsigned char *)data, 2);
+}
+
+int
+getstate(Window w, int *state)
+{
+	long *p = 0;
+
+	if (_getprop(w, wm_state, wm_state, 2L, (unsigned char**)&p) <= 0)
+		return 0;
+
+	*state = (int) *p;
+	XFree((char *) p);
+	return 1;
+}
+
+void
+getproto(Client *c)
+{
+	Atom *p;
+	int i;
+	long n;
+	Window w;
+
+	w = c->window;
+	c->proto = 0;
+	if ((n = _getprop(w, wm_protocols, XA_ATOM, 20L, (unsigned char**)&p)) <= 0)
+		return;
+
+	for (i = 0; i < n; i++)
+		if (p[i] == wm_delete)
+			c->proto |= Pdelete;
+		else if (p[i] == wm_take_focus)
+			c->proto |= Ptakefocus;
+
+	XFree((char *) p);
+}
diff --git a/src/cmd/rio/menu.c b/src/cmd/rio/menu.c
new file mode 100644
index 0000000..5445395
--- /dev/null
+++ b/src/cmd/rio/menu.c
@@ -0,0 +1,258 @@
+/* Copyright (c) 1994-1996 David Hogan, see README for licence details */
+#include <stdio.h>
+#include <signal.h>
+#include <X11/X.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include "dat.h"
+#include "fns.h"
+
+Client	*hiddenc[MAXHIDDEN];
+
+int	numhidden;
+
+char	*b3items[B3FIXED+MAXHIDDEN+1] =
+{
+	"New",
+	"Reshape",
+	"Move",
+	"Delete",
+	"Hide",
+	0,
+};
+
+Menu	b3menu =
+{
+	b3items,
+};
+
+Menu	egg =
+{
+	version,
+};
+
+void
+button(XButtonEvent *e)
+{
+	int n, shift;
+	Client *c;
+	Window dw;
+	ScreenInfo *s;
+
+	curtime = e->time;
+	s = getscreen(e->root);
+	if (s == 0)
+		return;
+	c = getclient(e->window, 0);
+	if (c) {
+		e->x += c->x - BORDER;
+		e->y += c->y - BORDER;
+	}
+	else if (e->window != e->root)
+		XTranslateCoordinates(dpy, e->window, s->root, e->x, e->y,
+				&e->x, &e->y, &dw);
+	switch (e->button) {
+	case Button1:
+		if (c) {
+			XMapRaised(dpy, c->parent);
+			top(c);
+			active(c);
+		}
+		return;
+	case Button2:
+		if ((e->state&(ShiftMask|ControlMask))==(ShiftMask|ControlMask))
+			menuhit(e, &egg);
+		return;
+	default:
+		return;
+	case Button3:
+		break;
+	}
+
+	if (current && current->screen == s)
+		cmapnofocus(s);
+	switch (n = menuhit(e, &b3menu)) {
+	case 0: 	/* New */
+		spawn(s);
+		break;
+	case 1: 	/* Reshape */
+		reshape(selectwin(1, 0, s));
+		break;
+	case 2: 	/* Move */
+		move(selectwin(0, 0, s));
+		break;
+	case 3: 	/* Delete */
+		shift = 0;
+		c = selectwin(1, &shift, s);
+		delete(c, shift);
+		break;
+	case 4: 	/* Hide */
+		hide(selectwin(1, 0, s));
+		break;
+	default:	/* unhide window */
+		unhide(n - B3FIXED, 1);
+		break;
+	case -1:	/* nothing */
+		break;
+	}
+	if (current && current->screen == s)
+		cmapfocus(current);
+}
+
+void
+spawn(ScreenInfo *s)
+{
+	/*
+	 * ugly dance to avoid leaving zombies.  Could use SIGCHLD,
+	 * but it's not very portable.
+	 */
+	if (fork() == 0) {
+		if (fork() == 0) {
+			close(ConnectionNumber(dpy));
+			if (s->display[0] != '\0')
+				putenv(s->display);
+			if (termprog != NULL) {
+				execl(shell, shell, "-c", termprog, 0);
+				fprintf(stderr, "9wm: exec %s", shell);
+				perror(" failed");
+			}
+			execlp("9term", "9term", "-w", 0);
+			execlp("xterm", "xterm", "-ut", 0);
+			perror("9wm: exec 9term/xterm failed");
+			exit(1);
+		}
+		exit(0);
+	}
+	wait((int *) 0);
+}
+
+void
+reshape(Client *c)
+{
+	int odx, ody;
+
+	if (c == 0)
+		return;
+	odx = c->dx;
+	ody = c->dy;
+	if (sweep(c) == 0)
+		return;
+	active(c);
+	top(c);
+	XRaiseWindow(dpy, c->parent);
+	XMoveResizeWindow(dpy, c->parent, c->x-BORDER, c->y-BORDER,
+					c->dx+2*BORDER, c->dy+2*BORDER);
+	if (c->dx == odx && c->dy == ody)
+		sendconfig(c);
+	else
+		XMoveResizeWindow(dpy, c->window, BORDER, BORDER, c->dx, c->dy);
+}
+
+void
+move(Client *c)
+{
+	if (c == 0)
+		return;
+	if (drag(c) == 0)
+		return;
+	active(c);
+	top(c);
+	XRaiseWindow(dpy, c->parent);
+	XMoveWindow(dpy, c->parent, c->x-BORDER, c->y-BORDER);
+	sendconfig(c);
+}
+
+void
+delete(Client *c, int shift)
+{
+	if (c == 0)
+		return;
+	if ((c->proto & Pdelete) && !shift)
+		sendcmessage(c->window, wm_protocols, wm_delete, 0);
+	else
+		XKillClient(dpy, c->window);		/* let event clean up */
+}
+
+void
+hide(Client *c)
+{
+	if (c == 0 || numhidden == MAXHIDDEN)
+		return;
+	if (hidden(c)) {
+		fprintf(stderr, "9wm: already hidden: %s\n", c->label);
+		return;
+	}
+	XUnmapWindow(dpy, c->parent);
+	XUnmapWindow(dpy, c->window);
+	setstate(c, IconicState);
+	if (c == current)
+		nofocus();
+	hiddenc[numhidden] = c;
+	b3items[B3FIXED+numhidden] = c->label;
+	numhidden++;
+	b3items[B3FIXED+numhidden] = 0;
+}
+
+void
+unhide(int n, int map)
+{
+	Client *c;
+	int i;
+
+	if (n >= numhidden) {
+		fprintf(stderr, "9wm: unhide: n %d numhidden %d\n", n, numhidden);
+		return;
+	}
+	c = hiddenc[n];
+	if (!hidden(c)) {
+		fprintf(stderr, "9wm: unhide: not hidden: %s(0x%x)\n",
+			c->label, c->window);
+		return;
+	}
+
+	if (map) {
+		XMapWindow(dpy, c->window);
+		XMapRaised(dpy, c->parent);
+		setstate(c, NormalState);
+		active(c);
+		top(c);
+	}
+
+	numhidden--;
+	for (i = n; i < numhidden; i ++) {
+		hiddenc[i] = hiddenc[i+1];
+		b3items[B3FIXED+i] = b3items[B3FIXED+i+1];
+	}
+	b3items[B3FIXED+numhidden] = 0;
+}
+
+void
+unhidec(Client *c, int map)
+{
+	int i;
+
+	for (i = 0; i < numhidden; i++)
+		if (c == hiddenc[i]) {
+			unhide(i, map);
+			return;
+		}
+	fprintf(stderr, "9wm: unhidec: not hidden: %s(0x%x)\n",
+		c->label, c->window);
+}
+
+void
+renamec(Client *c, char *name)
+{
+	int i;
+
+	if (name == 0)
+		name = "???";
+	c->label = name;
+	if (!hidden(c))
+		return;
+	for (i = 0; i < numhidden; i++)
+		if (c == hiddenc[i]) {
+			b3items[B3FIXED+i] = name;
+			return;
+		}
+}
diff --git a/src/cmd/rio/patchlevel.h b/src/cmd/rio/patchlevel.h
new file mode 100644
index 0000000..935ec35
--- /dev/null
+++ b/src/cmd/rio/patchlevel.h
@@ -0,0 +1 @@
+#define PATCHLEVEL 0