Importing wm2-1
18 files changed, 4982 insertions(+), 0 deletions(-)

A => Border.C
A => Border.h
A => Buttons.C
A => Client.C
A => Client.h
A => Config.h
A => Cursors.h
A => Events.C
A => General.h
A => Main.C
A => Makefile
A => Manager.C
A => Manager.h
A => README
A => Rotated.C
A => Rotated.h
A => listimpl.C
A => listmacro.h
A => Border.C +808 -0
@@ 0,0 1,808 @@ 
+
+#include "Border.h"
+#include "Client.h"
+#include "Manager.h"
+#include "Rotated.h"
+
+// These distances all exclude the 1-pixel borders.  You could
+// probably change these a certain amount before breaking the shoddy
+// code in the rest of this file.
+
+int Border::m_tabTopHeight = 2;
+int Border::m_frameWidth = 7;
+int Border::m_transientFrameWidth = 4;
+
+// These are degenerate initialisations, don't change them
+int Border::m_tabWidth = -1;
+XRotFontStruct *Border::m_tabFont = 0;
+GC Border::m_drawGC = 0;
+
+unsigned long Border::m_foregroundPixel;
+unsigned long Border::m_backgroundPixel;
+unsigned long Border::m_frameBackgroundPixel;
+unsigned long Border::m_buttonBackgroundPixel;
+unsigned long Border::m_borderPixel;
+
+
+Border::Border(Client *const c, Window child) :
+    m_client(c), m_parent(0), m_tab(0),
+    m_child(child), m_button(0), m_resize(0), m_label(0),
+    m_tabHeight(-1), m_prevW(-1), m_prevH(-1)
+{
+    m_parent = root();
+
+    if (m_tabFont == 0) {
+
+	if (!(m_tabFont = XRotLoadFont(display(), CONFIG_NICE_FONT, 90.0)) &&
+	    !(m_tabFont = XRotLoadFont(display(), CONFIG_NASTY_FONT, 90.0))) {
+	    windowManager()->fatal
+		("couldn't load default rotated font, bailing out");
+	}
+
+	m_tabWidth = m_tabFont->height + 4;
+	if (m_tabWidth < m_tabTopHeight * 2 + 8) {
+	    m_tabWidth = m_tabTopHeight * 2 + 8;
+	}
+
+	int screen = 0;		// !!
+	XColor nearest, ideal;
+
+	if (!XAllocNamedColor(display(), DefaultColormap(display(), screen),
+			      CONFIG_TAB_FOREGROUND, &nearest, &ideal)) {
+	    fatal("couldn't load tab foreground colour");
+	} else m_foregroundPixel = nearest.pixel;
+
+	if (!XAllocNamedColor(display(), DefaultColormap(display(), screen),
+			      CONFIG_TAB_BACKGROUND, &nearest, &ideal)) {
+	    fatal("couldn't load tab background colour");
+	} else m_backgroundPixel = nearest.pixel;
+
+	if (!XAllocNamedColor(display(), DefaultColormap(display(), screen),
+			      CONFIG_FRAME_BACKGROUND, &nearest, &ideal)) {
+	    fatal("couldn't load frame background colour");
+	} else m_frameBackgroundPixel = nearest.pixel;
+
+	if (!XAllocNamedColor(display(), DefaultColormap(display(), screen),
+			      CONFIG_BUTTON_BACKGROUND, &nearest, &ideal)) {
+	    fatal("couldn't load button background colour");
+	} else m_buttonBackgroundPixel = nearest.pixel;
+
+	if (!XAllocNamedColor(display(), DefaultColormap(display(), screen),
+			      CONFIG_BORDERS, &nearest, &ideal)) {
+	    fatal("couldn't load border colour");
+	} else m_borderPixel = nearest.pixel;
+
+	XGCValues values;
+	values.foreground = m_foregroundPixel;
+	values.background = m_backgroundPixel;
+	values.function = GXcopy;
+	values.line_width = 0;
+	values.subwindow_mode = IncludeInferiors;
+
+	m_drawGC = XCreateGC(display(), root(),
+			     GCForeground | GCBackground | GCFunction |
+			     GCLineWidth | GCSubwindowMode,
+			     &values);
+
+	if (!m_drawGC) {
+	    windowManager()->fatal("couldn't allocate border GC");
+	}
+    }
+}
+
+
+Border::~Border()
+{
+    if (m_parent != root()) {
+	if (!m_parent) fprintf(stderr,"wm2: zero parent in Border::~Border\n");
+	else {
+	    XDestroyWindow(display(), m_tab);
+	    XDestroyWindow(display(), m_button);
+	    XDestroyWindow(display(), m_parent);
+
+	    // bad window if its parent has already gone:
+	    XDestroyWindow(display(), m_resize);
+	}
+    }
+
+    if (m_label) free(m_label);
+}
+
+
+void Border::fatal(char *s)
+{
+    windowManager()->fatal(s);
+}
+
+
+Display *Border::display()
+{
+    return m_client->display();
+}
+
+
+WindowManager *Border::windowManager()
+{
+    return m_client->windowManager();
+}
+
+
+Window Border::root()
+{
+    return m_client->root();
+}
+
+
+void Border::expose(XExposeEvent *e)
+{
+    if (e->window != m_tab) return;
+    drawLabel();
+}
+
+
+void Border::drawLabel()
+{
+    if (m_label) {
+	XClearWindow(display(), m_tab);
+	XRotDrawString(display(), m_tabFont, m_tab, m_drawGC,
+		       2 + m_tabFont->max_ascent, m_tabHeight - 1,
+		       m_label, strlen(m_label));
+    }
+}
+
+
+Boolean Border::isTransient(void)
+{
+    return m_client->isTransient();
+}
+
+
+Boolean Border::isFixedSize(void)
+{
+    return m_client->isFixedSize();
+}
+
+
+void Border::fixTabHeight(int maxHeight)
+{
+    m_tabHeight = 0x7fff;
+    maxHeight -= m_tabWidth;	// for diagonal
+
+    if (m_label) free(m_label);
+    m_label = NewString(m_client->label());
+    
+    if (m_label) {
+	m_tabHeight =
+	    XRotTextWidth(m_tabFont, m_label, strlen(m_label)) + 6 + m_tabWidth;
+    }
+
+    if (m_tabHeight <= maxHeight) return;
+
+    if (m_label) free(m_label);
+    m_label = m_client->iconName() ?
+	NewString(m_client->iconName()) : NewString("incognito");
+
+    int len = strlen(m_label);
+    m_tabHeight = XRotTextWidth(m_tabFont, m_label, len) + 6 + m_tabWidth;
+    if (m_tabHeight <= maxHeight) return;
+
+    char *newLabel = (char *)malloc(len + 3);
+
+    do {
+	strncpy(newLabel, m_label, len - 1);
+	strcpy(newLabel + len - 1, "...");
+	m_tabHeight = XRotTextWidth(m_tabFont, newLabel,
+				    strlen(newLabel)) + 6 + m_tabWidth;
+	--len;
+
+    } while (m_tabHeight > maxHeight && len > 2);
+
+    free(m_label);
+    m_label = newLabel;
+
+    if (m_tabHeight > maxHeight) m_tabHeight = maxHeight;
+}
+
+
+declareList(RectangleList, XRectangle);
+implementList(RectangleList, XRectangle);
+
+
+void Border::shapeTransientParent(int w, int h)
+{
+    XRectangle r;
+
+    r.x = xIndent() - 1; r.y = yIndent() - 1;
+    r.width = w + 2; r.height = h + 2;
+
+    XShapeCombineRectangles
+	(display(), m_parent, ShapeBounding, 0, 0, &r, 1, ShapeSet, YXBanded);
+
+    r.x = xIndent(); r.y = yIndent();
+    r.width = w; r.height = h;
+
+    XShapeCombineRectangles
+	(display(), m_parent, ShapeClip, 0, 0, &r, 1, ShapeSet, YXBanded);
+}
+
+
+void Border::setTransientFrameVisibility(Boolean visible, int w, int h)
+{
+    int i;
+    XRectangle r;
+    RectangleList rl;
+
+    r.x = 0; r.y = 0;
+    r.width = w + 1; r.height = yIndent() - 1;
+    rl.append(r);
+
+    for (i = 1; i < yIndent(); ++i) {
+	r.x = w + 1; r.y = i - 1;
+	r.width = i + 1; r.height = 1;
+	rl.append(r);
+    }
+
+    r.x = 0; r.y = yIndent() - 1;
+    r.width = xIndent() - 1; r.height = h - yIndent() + 2;
+    rl.append(r);
+
+    for (i = 1; i < yIndent(); ++i) {
+	r.x = i - 1; r.y = h;
+	r.width = 1; r.height = i + 2;
+	rl.append(r);
+    }
+
+    XShapeCombineRectangles
+	(display(), m_parent, ShapeBounding,
+	 0, 0, rl.array(0, rl.count()), rl.count(),
+	 visible ? ShapeUnion : ShapeSubtract, YXSorted);
+
+    rl.remove_all();
+
+    r.x = 1; r.y = 1;
+    r.width = w; r.height = yIndent() - 2;
+    rl.append(r);
+
+    for (i = 2; i < yIndent(); ++i) {
+	r.x = w + 1; r.y = i - 1;
+	r.width = i; r.height = 1;
+	rl.append(r);
+    }
+
+    r.x = 1; r.y = yIndent() - 1;
+    r.width = xIndent() - 2; r.height = h - yIndent() + 1;
+    rl.append(r);
+
+    for (i = 2; i < yIndent(); ++i) {
+	r.x = i - 1; r.y = h;
+	r.width = 1; r.height = i + 1;
+	rl.append(r);
+    }
+
+    XShapeCombineRectangles
+	(display(), m_parent, ShapeClip,
+	 0, 0, rl.array(0, rl.count()), rl.count(),
+	 visible ? ShapeUnion : ShapeSubtract, YXSorted);
+}   
+
+
+void Border::shapeParent(int w, int h)
+{
+    int i;
+    XRectangle r;
+    int mainRect;
+    RectangleList rl;
+
+    if (isTransient()) {
+	shapeTransientParent(w, h);
+	return;
+    }
+
+    // Bounding rectangles -- clipping will be the same except for
+    // child window border
+
+    // top of tab
+    r.x = 0; r.y = 0;
+    r.width = w + m_tabWidth + 1; r.height = m_tabTopHeight + 2;
+    rl.append(r);
+    
+    // struts in tab, left...
+    r.x = 0; r.y = m_tabTopHeight + 1;
+    r.width = m_tabTopHeight + 2; r.height = m_tabWidth - m_tabTopHeight*2 - 1;
+    rl.append(r);
+
+    // ...and right
+    r.x = m_tabWidth - m_tabTopHeight; r.y = m_tabTopHeight + 1;
+    r.width = m_tabTopHeight + 2; r.height = m_tabWidth - m_tabTopHeight*2 - 1;
+    rl.append(r);
+
+    // main window
+    r.x = xIndent() - 1; r.y = yIndent() - 1;
+    r.width = w + 2; r.height = h + 2;
+    mainRect = rl.count();
+    rl.append(r);
+
+    // main tab
+    r.x = 0; r.y = m_tabWidth - m_tabTopHeight;
+    r.width = m_tabWidth + 2;
+    r.height = m_tabHeight - m_tabWidth + m_tabTopHeight;
+    rl.append(r);
+
+    // diagonal
+    for (i = 1; i < m_tabWidth - 1; ++i) {
+	r.x = i; r.y = m_tabHeight + i - 1;
+	r.width = m_tabWidth - i + 2; r.height = 1;
+	rl.append(r);
+    }
+
+    XShapeCombineRectangles
+	(display(), m_parent, ShapeBounding,
+	 0, 0, rl.array(0, rl.count()), rl.count(), ShapeSet, YXSorted);
+
+    rl.item(mainRect).x ++;
+    rl.item(mainRect).y ++;
+    rl.item(mainRect).width -= 2;
+    rl.item(mainRect).height -= 2;
+
+    XShapeCombineRectangles
+	(display(), m_parent, ShapeClip,
+	 0, 0, rl.array(0, rl.count()), rl.count(), ShapeSet, YXSorted);
+}
+
+
+void Border::shapeTab(int w, int)
+{
+    int i;
+    XRectangle r;
+    RectangleList rl;
+
+    if (isTransient()) {
+	return;
+    }
+
+    // Bounding rectangles
+
+    r.x = 0; r.y = 0;
+    r.width = w + m_tabWidth + 1; r.height = m_tabTopHeight + 2;
+    rl.append(r);
+    
+    r.x = 0; r.y = m_tabTopHeight + 1;
+    r.width = m_tabTopHeight + 2; r.height = m_tabWidth - m_tabTopHeight*2 - 1;
+    rl.append(r);
+
+    r.x = m_tabWidth - m_tabTopHeight; r.y = m_tabTopHeight + 1;
+    r.width = m_tabTopHeight + 2; r.height = m_tabWidth - m_tabTopHeight*2 - 1;
+    rl.append(r);
+
+    r.x = 0; r.y = m_tabWidth - m_tabTopHeight;
+    r.width = m_tabWidth + 2;
+    r.height = m_tabHeight - m_tabWidth + m_tabTopHeight;
+    rl.append(r);
+
+    for (i = 1; i < m_tabWidth - 1; ++i) {
+	r.x = i; r.y = m_tabHeight + i - 1;
+	r.width = m_tabWidth - i + 2; r.height = 1;
+	rl.append(r);
+    }
+
+    XShapeCombineRectangles
+	(display(), m_tab, ShapeBounding,
+	 0, 0, rl.array(0, rl.count()), rl.count(), ShapeSet, YXSorted);
+
+    rl.remove_all();
+
+    // Clipping rectangles
+
+    r.x = 1; r.y = 1;
+    r.width = w + m_tabWidth - 1; r.height = m_tabTopHeight;
+    rl.append(r);
+
+    r.x = 1; r.y = m_tabTopHeight + 1;
+    r.width = m_tabTopHeight; r.height = m_tabWidth + m_tabTopHeight*2 - 1;
+    rl.append(r);
+
+    r.x = m_tabWidth - m_tabTopHeight + 1; r.y = m_tabTopHeight + 1;
+    r.width = m_tabTopHeight; r.height = m_tabWidth + m_tabTopHeight*2 - 1;
+    rl.append(r);
+
+    r.x = 1; r.y = m_tabWidth - m_tabTopHeight + 1;
+    r.width = m_tabWidth;
+    r.height = m_tabHeight - m_tabWidth + m_tabTopHeight - 1;
+    rl.append(r);
+
+    for (i = 1; i < m_tabWidth - 2; ++i) {
+	r.x = i + 1; r.y = m_tabHeight + i - 1;
+	r.width = m_tabWidth - i; r.height = 1;
+	rl.append(r);
+    }
+
+    XShapeCombineRectangles
+	(display(), m_tab, ShapeClip,
+	 0, 0, rl.array(0, rl.count()), rl.count(), ShapeSet, YXSorted);
+
+//    XSync(display(), False);
+}
+
+
+void Border::resizeTab(int h)
+{
+    int i;
+    XRectangle r;
+    RectangleList rl;
+    int shorter, longer, operation;
+
+    if (isTransient()) {
+	return;
+    }
+
+    int prevTabHeight = m_tabHeight;
+    fixTabHeight(h);
+    if (m_tabHeight == prevTabHeight) return;
+
+    XWindowChanges wc;
+    wc.height = m_tabHeight + 2 + m_tabWidth;
+    XConfigureWindow(display(), m_tab, CWHeight, &wc);
+
+    if (m_tabHeight > prevTabHeight) {
+
+	shorter = prevTabHeight;
+	longer = m_tabHeight;
+	operation = ShapeUnion;
+
+    } else {
+
+	shorter = m_tabHeight;
+	longer = prevTabHeight + m_tabWidth;
+	operation = ShapeSubtract;
+    }
+
+    r.x = 0; r.y = shorter - 2;
+    r.width = m_tabWidth + 2; r.height = longer - shorter;
+
+    XShapeCombineRectangles(display(), m_parent, ShapeBounding,
+			    0, 0, &r, 1, operation, YXBanded);
+
+    XShapeCombineRectangles(display(), m_parent, ShapeClip,
+			    0, 0, &r, 1, operation, YXBanded);
+
+    XShapeCombineRectangles(display(), m_tab, ShapeBounding,
+			    0, 0, &r, 1, operation, YXBanded);
+
+    r.x ++; r.width -= 2;
+
+    XShapeCombineRectangles(display(), m_tab, ShapeClip,
+			    0, 0, &r, 1, operation, YXBanded);
+
+    // restore a bit of the frame edge
+    r.x = m_tabWidth + 1; r.y = shorter;
+    r.width = m_frameWidth - 1; r.height = longer - shorter;
+    XShapeCombineRectangles(display(), m_parent, ShapeBounding,
+			    0, 0, &r, 1, ShapeUnion, YXBanded);
+
+    for (i = 1; i < m_tabWidth - 1; ++i) {
+	r.x = i; r.y = m_tabHeight + i - 3;
+	r.width = m_tabWidth - i + 2; r.height = 1;
+	rl.append(r);
+    }
+	
+    XShapeCombineRectangles
+	(display(), m_parent, ShapeBounding,
+	 0, 0, rl.array(0, rl.count()), rl.count(), ShapeUnion, YXBanded);
+
+    XShapeCombineRectangles
+	(display(), m_parent, ShapeClip,
+	 0, 0, rl.array(0, rl.count()), rl.count(), ShapeUnion, YXBanded);
+
+    XShapeCombineRectangles
+	(display(), m_tab, ShapeBounding,
+	 0, 0, rl.array(0, rl.count()), rl.count(), ShapeUnion, YXBanded);
+
+    if (rl.count() < 2) return;
+
+    for (i = 0; i < rl.count() - 1; ++i) {
+	rl.item(i).x ++; rl.item(i).width -= 2;
+    }
+
+    XShapeCombineRectangles
+	(display(), m_tab, ShapeClip,
+	 0, 0, rl.array(0, rl.count() - 1), rl.count() - 1,
+	 ShapeUnion, YXBanded);
+
+//    XSync(display(), False);
+}
+
+
+void Border::shapeResize()
+{
+    int i;
+    XRectangle r;
+    RectangleList rl;
+
+    for (i = 0; i < m_frameWidth*2; ++i) {
+	r.x = m_frameWidth*2 - i - 1; r.y = i; r.width = i + 1; r.height = 1;
+	rl.append(r);
+    }
+
+    XShapeCombineRectangles
+	(display(), m_resize, ShapeBounding, 0, 0,
+	 rl.array(0, rl.count()), rl.count(), ShapeSet, YXBanded);
+
+    rl.remove_all();
+
+    for (i = 1; i < m_frameWidth*2; ++i) {
+	r.x = m_frameWidth*2 - i; r.y = i; r.width = i; r.height = 1;
+	rl.append(r);
+    }
+
+    XShapeCombineRectangles
+	(display(), m_resize, ShapeClip, 0, 0,
+	 rl.array(0, rl.count()), rl.count(), ShapeSet, YXBanded);
+
+    rl.remove_all();
+
+    for (i = 0; i < m_frameWidth*2 - 3; ++i) {
+	r.x = m_frameWidth*2 - i - 1; r.y = i + 3; r.width = 1; r.height = 1;
+	rl.append(r);
+    }
+
+    XShapeCombineRectangles
+	(display(), m_resize, ShapeClip, 0, 0,
+	 rl.array(0, rl.count()), rl.count(), ShapeSubtract, YXBanded);
+
+    windowManager()->installCursorOnWindow
+	(WindowManager::DownrightCursor, m_resize);
+}
+
+
+void Border::setFrameVisibility(Boolean visible, int w, int h)
+{
+    XRectangle r;
+    RectangleList rl;
+
+    if (isTransient()) {
+	setTransientFrameVisibility(visible, w, h);
+	return;
+    }
+
+    // Bounding rectangles
+
+    r.x = m_tabWidth + w + 1; r.y = 0;
+    r.width = m_frameWidth + 1; r.height = m_frameWidth;
+    rl.append(r);
+
+    r.x = m_tabWidth + 2; r.y = m_tabTopHeight + 2;
+    r.width = w; r.height = m_frameWidth - m_tabTopHeight - 2;
+    rl.append(r);
+
+    // for button
+    r.width = r.height = m_tabWidth - m_tabTopHeight*2 - 4;
+    r.x = r.y = (m_tabWidth + 2 - r.width) / 2;
+    rl.append(r);
+
+    r.x = m_tabWidth + 2; r.y = m_frameWidth;
+    r.width = m_frameWidth - 2;
+    r.height = m_tabHeight + m_tabWidth - m_frameWidth - 2;
+    rl.append(r);
+
+    // swap last two if sorted wrong
+    if (rl.item(rl.count()-2).y > rl.item(rl.count()-1).y) {
+	rl.append(rl.item(rl.count()-2));
+	rl.remove(rl.count()-3);
+    }
+
+    r.x -= 1; r.y += r.height;
+    r.width += 1; r.height = h - r.height + 2;
+    rl.append(r);
+
+    XShapeCombineRectangles(display(), m_parent, ShapeBounding,
+			    0, 0, rl.array(0, rl.count()), rl.count(),
+			    visible ? ShapeUnion : ShapeSubtract, YXSorted);
+    rl.remove_all();
+
+    // Clip rectangles
+
+    r.x = m_tabWidth + w + 1; r.y = 1;
+    r.width = m_frameWidth; r.height = m_frameWidth - 1;
+    rl.append(r);
+
+    r.x = m_tabWidth + 2; r.y = m_tabTopHeight + 2;
+    r.width = w; r.height = m_frameWidth - m_tabTopHeight - 2;
+    rl.append(r);
+
+    // for button
+    r.width = r.height = m_tabWidth - m_tabTopHeight*2 - 6;
+    r.x = r.y = (m_tabWidth + 2 - r.width) / 2;
+    rl.append(r);
+
+    r.x = m_tabWidth + 2; r.y = m_frameWidth;
+    r.width = m_frameWidth - 2; r.height = h - m_frameWidth;
+    rl.append(r);
+
+    // swap last two if sorted wrong
+    if (rl.item(rl.count()-2).y > rl.item(rl.count()-1).y) {
+	rl.append(rl.item(rl.count()-2));
+	rl.remove(rl.count()-3);
+    }
+
+    r.x = m_tabWidth + 2; r.y = h;
+    r.width = m_frameWidth - 2; r.height = m_frameWidth + 1;
+    rl.append(r);
+
+    XShapeCombineRectangles(display(), m_parent, ShapeClip,
+			    0, 0, rl.array(0, rl.count()), rl.count(),
+			    visible ? ShapeUnion : ShapeSubtract, YXSorted);
+    rl.remove_all();
+
+    if (visible && !isFixedSize()) {
+	XMapRaised(display(), m_resize);
+    } else {
+	XUnmapWindow(display(), m_resize);
+    }
+
+//    XSync(display(), False);	// doesn't seem to help much
+}
+
+
+void Border::configure(int x, int y, int w, int h,
+		       unsigned long mask, int detail,
+		       Boolean force) // must reshape everything
+{
+    if (!m_parent || m_parent == root()) {
+
+	// create windows, then shape them afterwards
+
+	m_parent = XCreateSimpleWindow
+	    (display(), root(), 1, 1, 1, 1, 0,
+	     m_borderPixel, m_frameBackgroundPixel);
+
+	m_tab = XCreateSimpleWindow
+	    (display(), m_parent, 1, 1, 1, 1, 0,
+	     m_borderPixel, m_backgroundPixel);
+
+	m_button = XCreateSimpleWindow
+	    (display(), m_parent, 1, 1, 1, 1, 0,
+	     m_borderPixel, m_buttonBackgroundPixel);
+
+	m_resize = XCreateWindow
+	    (display(), m_child, 1, 1, m_frameWidth*2, m_frameWidth*2, 0,
+	     CopyFromParent, InputOutput, CopyFromParent, 0L, 0);
+
+	shapeResize();
+
+	XSelectInput(display(), m_parent,
+		     SubstructureRedirectMask | SubstructureNotifyMask |
+		     ButtonPressMask | ButtonReleaseMask);
+
+	if (!isTransient()) {
+	    XSelectInput(display(), m_tab,
+			 ExposureMask | ButtonPressMask | ButtonReleaseMask |
+			 EnterWindowMask | LeaveWindowMask);
+	}
+
+	XSelectInput(display(), m_button,
+		     ButtonPressMask | ButtonReleaseMask | LeaveWindowMask);
+	XSelectInput(display(), m_resize, ButtonPressMask | ButtonReleaseMask);
+	mask |= CWX | CWY | CWWidth | CWHeight | CWBorderWidth;
+    }
+
+    XWindowChanges wc;
+    wc.x = x - xIndent();
+    wc.y = y - yIndent();
+    wc.width  = w + xIndent() + 1;
+    wc.height = h + yIndent() + 1;
+    wc.border_width = 0;
+    wc.sibling = None;
+    wc.stack_mode = detail;
+    XConfigureWindow(display(), m_parent, mask, &wc);
+
+    unsigned long rmask = 0L;
+    if (mask & CWWidth)  rmask |= CWX;
+    if (mask & CWHeight) rmask |= CWY;
+    wc.x = w - m_frameWidth*2;
+    wc.y = h - m_frameWidth*2;
+    XConfigureWindow(display(), m_resize, rmask, &wc);
+    
+    if (force ||
+	(m_prevW < 0 || m_prevH < 0) ||
+	((mask & (CWWidth | CWHeight)) && (w != m_prevW || h != m_prevH))) {
+
+	int prevTabHeight = m_tabHeight;
+	if (isTransient()) m_tabHeight = 10; // arbitrary
+	else fixTabHeight(h);
+
+	shapeParent(w, h);
+	setFrameVisibility(m_client->isActive(), w, h);
+
+	if (force ||
+	    prevTabHeight != m_tabHeight || m_prevW < 0 || m_prevH < 0) {
+
+	    wc.x = 0;
+	    wc.y = 0;
+	    wc.width = w + xIndent();
+	    wc.height = m_tabHeight + 2 + m_tabWidth;
+	    XConfigureWindow(display(), m_tab, mask, &wc);
+	    shapeTab(w, h);
+	}
+
+	m_prevW = w;
+	m_prevH = h;
+
+    } else {
+
+	resizeTab(h);
+    }
+
+    wc.x = wc.y = m_tabTopHeight + 2;
+    wc.width = wc.height = m_tabWidth - m_tabTopHeight*2 - 4;
+    XConfigureWindow(display(), m_button, mask, &wc);
+}
+
+
+void Border::moveTo(int x, int y)
+{
+    XWindowChanges wc;
+    wc.x = x - xIndent();
+    wc.y = y - yIndent();
+    XConfigureWindow(display(), m_parent, CWX | CWY, &wc);
+}
+
+
+void Border::map()
+{
+    if (m_parent == root()) {
+	fprintf(stderr, "wm2: bad parent in Border::map()\n");
+    } else {
+	XMapWindow(display(), m_parent);
+
+	if (!isTransient()) {
+	    XMapWindow(display(), m_tab);
+	    XMapWindow(display(), m_button);
+	    if (!isFixedSize()) XMapWindow(display(), m_resize);
+	}
+    }
+}
+
+
+void Border::mapRaised()
+{
+    if (m_parent == root()) {
+	fprintf(stderr, "wm2: bad parent in Border::mapRaised()\n");
+    } else {
+	XMapRaised(display(), m_parent);
+
+	if (!isTransient()) {
+	    XMapWindow(display(), m_tab);
+	    XMapRaised(display(), m_button);
+	    if (!isFixedSize()) XMapRaised(display(), m_resize);
+	}
+    }
+}
+
+
+void Border::unmap()
+{
+    if (m_parent == root()) {
+	fprintf(stderr, "wm2: bad parent in Border::unmap()\n");
+    } else {
+	XUnmapWindow(display(), m_parent);
+
+	if (!isTransient()) {
+	    XUnmapWindow(display(), m_tab);
+	    XUnmapWindow(display(), m_button);
+//	    XUnmapWindow(display(), m_resize); // no, will unmap with parent
+	}
+    }
+}
+
+
+void Border::decorate(Boolean active, int w, int h)
+{
+    setFrameVisibility(active, w, h);
+}
+
+
+void Border::reparent()
+{
+    XReparentWindow(display(), m_child, m_parent, xIndent(), yIndent());
+}
+

          
A => Border.h +93 -0
@@ 0,0 1,93 @@ 
+
+#ifndef _BORDER_H_
+#define _BORDER_H_
+
+#include "General.h"
+#include "Rotated.h"
+
+class Client;
+class WindowManager;
+
+class Border {			// friend of client
+public:
+    Border(Client *const, Window child);
+    ~Border();
+
+
+    void map();
+    void unmap();
+    void mapRaised();
+    void decorate(Boolean active, int w, int h);
+    void reparent();
+    void configure(int x, int y, int w, int h, unsigned long mask, int detail,
+		   Boolean force = False);
+    void moveTo(int x, int y);
+
+    WindowManager *windowManager(); // calls into Client
+    Boolean isTransient();	// calls into Client
+    Boolean isFixedSize();	// calls into Client
+    Window parent() { return m_parent; }
+    Boolean hasWindow(Window w) {
+	return (w != root() && (w == m_parent || w == m_tab ||
+				w == m_button || w == m_resize));
+    }
+
+    Display *display();
+    Window root();
+
+    void expose(XExposeEvent *);
+    void eventButton(XButtonEvent *); // in Buttons.C
+
+    int yIndent() {
+	return isTransient() ? m_transientFrameWidth + 1 : m_frameWidth + 1;
+    }
+    int xIndent() {
+	return isTransient() ? m_transientFrameWidth + 1 :
+	    m_tabWidth + m_frameWidth + 1;
+    }
+    
+private:
+    Client *m_client;
+
+    Window m_parent;
+    Window m_tab;
+    Window m_child;
+    Window m_button;
+    Window m_resize;
+
+    void fatal(char *);
+
+    char *m_label;
+
+    void fixTabHeight(int);
+    void drawLabel();
+
+    void setFrameVisibility(Boolean, int, int);
+    void setTransientFrameVisibility(Boolean, int, int);
+    void shapeParent(int, int);
+    void shapeTransientParent(int, int);
+    void shapeTab(int, int);
+    void resizeTab(int);	// for rename without changing window size
+    void shapeResize();
+
+    int m_prevW;
+    int m_prevH;
+
+private:
+    int        m_tabHeight;	// depends on the label
+    static int m_tabWidth;	// depends on the label font
+    static int m_tabTopHeight;
+    static int m_frameWidth;	// NB frameTopHeight = frameHeight-tabTopHeight
+    static int m_transientFrameWidth;
+    static XRotFontStruct *m_tabFont;
+    static GC m_drawGC;
+    static unsigned long m_foregroundPixel;
+    static unsigned long m_backgroundPixel;
+    static unsigned long m_frameBackgroundPixel;
+    static unsigned long m_buttonBackgroundPixel;
+    static unsigned long m_borderPixel;
+};
+
+
+#endif
+

          
A => Buttons.C +681 -0
@@ 0,0 1,681 @@ 
+
+#include "Manager.h"
+#include "Client.h"
+#include <sys/time.h>
+
+#define AllButtonMask	( Button1Mask | Button2Mask | Button3Mask \
+			| Button4Mask | Button5Mask )
+#define ButtonMask	( ButtonPressMask | ButtonReleaseMask )
+#define DragMask        ( ButtonMask | ButtonMotionMask )
+#define MenuMask	( ButtonMask | ButtonMotionMask | ExposureMask )
+#define MenuGrabMask	( ButtonMask | ButtonMotionMask | StructureNotifyMask )
+
+
+void WindowManager::eventButton(XButtonEvent *e)
+{
+    Client *c = windowToClient(e->window);
+
+    if (e->window == e->root) {
+
+	if (e->button == Button1) menu(e);
+
+    } else if (c) {
+
+	c->eventButton(e);
+	return;
+    }
+}
+
+
+void Client::eventButton(XButtonEvent *e)
+{
+    if (e->type != ButtonPress) return;
+
+    mapRaised();
+
+    if (e->button == Button1) {
+	if (m_border->hasWindow(e->window)) {
+
+	    m_border->eventButton(e);
+	}
+    }
+
+    if (!isNormal() || isActive() || e->send_event) return;
+    activate();
+}
+
+
+static int nobuttons(XButtonEvent *e) // straight outta 9wm
+{
+    int state;
+    state = (e->state & AllButtonMask);
+    return (e->type == ButtonRelease) && (state & (state - 1)) == 0;
+}
+
+
+int WindowManager::attemptGrab(Window w, Window constrain, int mask, int t)
+{
+    int status;
+    if (t == 0) t = timestamp(False);
+    status = XGrabPointer(display(), w, False, mask, GrabModeAsync,
+			  GrabModeAsync, constrain, None, t);
+    return status;
+}
+
+
+void WindowManager::releaseGrab(XButtonEvent *e)
+{
+    XEvent ev;
+    if (!nobuttons(e)) {
+	for (;;) {
+	    XMaskEvent(display(), ButtonMask | ButtonMotionMask, &ev);
+	    if (ev.type == MotionNotify) continue;
+	    e = &ev.xbutton;
+	    if (nobuttons(e)) break;
+	}
+    }
+
+    XUngrabPointer(display(), e->time);
+    m_currentTime = e->time;
+}
+
+
+void WindowManager::menu(XButtonEvent *e)
+{
+    if (e->window == m_menuWindow) return;
+    
+    int i;
+    ClientList clients;
+
+#define MENU_LABEL(n) ((n)==0? m_menuCreateLabel : clients.item((n)-1)->label())
+
+    for (i = 0; i < m_hiddenClients.count(); ++i) {
+	clients.append(m_hiddenClients.item(i));
+    }
+    int nh = clients.count() + 1;
+
+    if (CONFIG_EVERYTHING_ON_ROOT_MENU) {
+	for (i = 0; i < m_clients.count(); ++i) {
+	    if (m_clients.item(i)->isNormal())
+		clients.append(m_clients.item(i));
+	}
+    }
+    int n = clients.count() + 1;
+
+    int width, maxWidth = 10;
+    for (i = 0; i < n; ++i) {
+	width = XTextWidth(m_menuFont, MENU_LABEL(i), strlen(MENU_LABEL(i)));
+	if (width > maxWidth) maxWidth = width;
+    }
+    maxWidth += 32;
+
+    int selecting = -1, prev = -1;
+    int entryHeight = m_menuFont->ascent + m_menuFont->descent + 4;
+    int totalHeight = entryHeight * n + 13; 
+    int x = e->x - maxWidth/2;
+    int y = e->y - 2;
+    int mx = DisplayWidth (display(), m_screenNumber) - 1;
+    int my = DisplayHeight(display(), m_screenNumber) - 1;
+    Boolean warp = False;
+
+    if (x < 0) {
+	e->x -= x; x = 0; warp = True;
+    } else if (x + maxWidth >= mx) {
+	e->x -= x + maxWidth - mx; x = mx - maxWidth; warp = True;
+    }
+
+    if (y < 0) {
+	e->y -= y; y = 0; warp = True;
+    } else if (y + totalHeight >= my) {
+	e->y -= y + totalHeight - my; y = my - totalHeight; warp = True;
+    }
+
+    if (warp) XWarpPointer(display(), None, root(),
+			   None, None, None, None, e->x, e->y);
+
+    XMoveResizeWindow(display(), m_menuWindow, x, y, maxWidth, totalHeight);
+    XSelectInput(display(), m_menuWindow, MenuMask);
+    XMapRaised(display(), m_menuWindow);
+
+    if (attemptGrab(m_menuWindow, None, MenuGrabMask, e->time) != GrabSuccess){
+	XUnmapWindow(display(), m_menuWindow);
+	return;
+    }
+
+    Boolean done = False;
+    Boolean drawn = False;
+    XEvent event;
+
+    while (!done) {
+	XMaskEvent(display(), MenuMask, &event);
+	
+	switch (event.type) {
+
+	default:
+	    fprintf(stderr, "wm2: unknown event type %d\n", event.type);
+	    break;
+
+	case ButtonPress:
+	    break;
+
+	case ButtonRelease:
+	    if (drawn) {
+
+		if (event.xbutton.button != e->button) break;
+		x = event.xbutton.x;
+		y = event.xbutton.y - 11;
+		i = y / entryHeight;
+
+		if (selecting >= 0 && y >= selecting * entryHeight - 3 &&
+		    y <= (selecting+1) * entryHeight - 3) i = selecting;
+
+		if (x < 0 || x > maxWidth || y < -3) i = -1;
+		else if (i < 0 || i >= n) i = -1;
+
+	    } else {
+		selecting = -1;
+	    }
+
+	    if (!nobuttons(&event.xbutton)) i = -1;
+	    releaseGrab(&event.xbutton);
+	    XUnmapWindow(display(), m_menuWindow);
+	    selecting = i;
+	    done = True;
+	    break;
+
+	case MotionNotify:
+	    if (!drawn) break;
+	    x = event.xbutton.x;
+	    y = event.xbutton.y - 11;
+	    prev = selecting;
+	    selecting = y / entryHeight;
+
+	    if (prev >= 0 && y >= prev * entryHeight - 3 &&
+		y <= (prev+1) * entryHeight - 3) selecting = prev;
+
+	    if (x < 0 || x > maxWidth || y < -3) selecting = -1;
+	    else if (selecting < 0 || selecting > n) selecting = -1;
+
+	    if (selecting == prev) break;
+
+	    if (prev >= 0 && prev < n) {
+		XFillRectangle(display(), m_menuWindow, m_menuGC,
+			       4, prev * entryHeight + 9,
+			       maxWidth - 8, entryHeight);
+	    }
+
+	    if (selecting >= 0 && selecting < n) {
+		XFillRectangle(display(), m_menuWindow, m_menuGC,
+			       4, selecting * entryHeight + 9,
+			       maxWidth - 8, entryHeight);
+	    }
+
+	    break;
+
+	case Expose:
+	    XClearWindow(display(), m_menuWindow);
+
+	    XDrawRectangle(display(), m_menuWindow, m_menuGC, 2, 7,
+			   maxWidth - 5, totalHeight - 10);
+
+	    for (i = 0; i < n; ++i) {
+
+		int dx = XTextWidth(m_menuFont, MENU_LABEL(i),
+				     strlen(MENU_LABEL(i)));
+		int dy = i * entryHeight + m_menuFont->ascent + 10;
+		
+		if (i >= nh) {		    
+		    XDrawString(display(), m_menuWindow, m_menuGC,
+				maxWidth - 8 - dx, dy,
+				MENU_LABEL(i), strlen(MENU_LABEL(i)));
+		} else {
+		    XDrawString(display(), m_menuWindow, m_menuGC, 8,
+				dy, MENU_LABEL(i), strlen(MENU_LABEL(i)));
+		}
+	    }
+
+	    if (selecting >= 0 && selecting < n) {
+		XFillRectangle(display(), m_menuWindow, m_menuGC,
+			       4, selecting * entryHeight + 9,
+			       maxWidth - 8, entryHeight);
+	    }
+
+	    drawn = True;
+	}
+    }
+
+    if (selecting >= 0) {
+	if (selecting == 0) {
+	    spawn();
+	} else if (selecting < nh) {
+	    clients.item(selecting - 1)->unhide(True);
+	} else if (selecting < n) {
+	    if (CONFIG_CLICK_TO_FOCUS) {
+		clients.item(selecting - 1)->activate();
+	    } else {
+		clients.item(selecting - 1)->mapRaised();
+	    }
+	    clients.item(selecting - 1)->ensureVisible();
+	}
+    }
+
+    clients.remove_all();
+    return;
+}
+
+
+void WindowManager::showGeometry(int x, int y)
+{
+    char string[20];
+    sprintf(string, "%d %d\n", x, y);
+    int width = XTextWidth(m_menuFont, string, strlen(string)) + 8;
+    int height = m_menuFont->ascent + m_menuFont->descent + 8;
+    int mx = DisplayWidth (display(), m_screenNumber) - 1;
+    int my = DisplayHeight(display(), m_screenNumber) - 1;
+  
+    XMoveResizeWindow(display(), m_menuWindow,
+		      (mx - width) / 2, (my - height) / 2, width, height);
+    XClearWindow(display(), m_menuWindow);
+    XMapRaised(display(), m_menuWindow);
+
+    XDrawString(display(), m_menuWindow, m_menuGC, 4, 4 + m_menuFont->ascent,
+		string, strlen(string));
+}
+
+
+void WindowManager::removeGeometry()
+{
+    XUnmapWindow(display(), m_menuWindow);
+}
+
+
+void Client::move(XButtonEvent *e)
+{
+    int x = -1, y = -1, xoff, yoff;
+    Boolean done = False;
+
+    if (m_windowManager->attemptGrab
+	(root(), None, DragMask, e->time) != GrabSuccess) {
+	return;
+    }
+
+    xoff = m_border->xIndent() - e->x;
+    yoff = m_border->yIndent() - e->y;
+
+    XEvent event;
+    Boolean found;
+    Boolean doSomething = False;
+    struct timeval sleepval;
+
+    while (!done) {
+
+	found = False;
+
+	while (XCheckMaskEvent(display(), DragMask | ExposureMask, &event)) {
+	    found = True;
+	    if (event.type != MotionNotify) break;
+	}
+
+	if (!found) {
+	    sleepval.tv_sec = 0;
+	    sleepval.tv_usec = 50000;
+	    select(0, 0, 0, 0, &sleepval);
+	    continue;
+	}
+	
+	switch (event.type) {
+
+	default:
+	    fprintf(stderr, "wm2: unknown event type %d\n", event.type);
+	    break;
+
+	case Expose:
+	    m_windowManager->eventExposure(&event.xexpose);
+	    break;
+
+	case ButtonPress:
+	    // don't like this
+	    XUngrabPointer(display(), event.xbutton.time);
+	    doSomething = False;
+	    done = True;
+	    break;
+
+	case ButtonRelease:
+
+	    x = event.xbutton.x; y = event.xbutton.y;
+	    if (!nobuttons(&event.xbutton)) doSomething = False;
+
+//	    XUngrabPointer(display(), event.xbutton.time);
+	    m_windowManager->releaseGrab(&event.xbutton);
+	    done = True;
+	    break;
+
+	case MotionNotify:
+	    x = event.xbutton.x; y = event.xbutton.y;
+	    if (x + xoff != m_x || y + yoff != m_y) {
+		windowManager()->showGeometry(x + xoff, y + yoff);
+		m_border->moveTo(x + xoff, y + yoff);
+		doSomething = True;
+	    }
+	    break;
+	}
+    }
+
+    windowManager()->removeGeometry();
+
+    if (x >= 0 && doSomething) {
+	m_x = x + xoff;
+	m_y = y + yoff;
+    }
+
+    if (CONFIG_CLICK_TO_FOCUS) activate();
+    m_border->moveTo(m_x, m_y);
+    sendConfigureNotify();
+}
+
+
+void Client::fixResizeDimensions(int &w, int &h, int &dw, int &dh)
+{
+    if (w < 50) w = 50;
+    if (h < 50) h = 50;
+    
+    if (m_sizeHints.flags & PResizeInc) {
+	w = m_minWidth  + (((w - m_minWidth) / m_sizeHints.width_inc) *
+			   m_sizeHints.width_inc);
+	h = m_minHeight + (((h - m_minHeight) / m_sizeHints.height_inc) *
+			   m_sizeHints.height_inc);
+
+	dw = (w - m_minWidth)  / m_sizeHints.width_inc;
+	dh = (h - m_minHeight) / m_sizeHints.height_inc;
+    } else {
+	dw = w; dh = h;
+    }
+
+    if (m_sizeHints.flags & PMaxSize) {
+	if (w > m_sizeHints.max_width)  w = m_sizeHints.max_width;
+	if (h > m_sizeHints.max_height) h = m_sizeHints.max_height;
+    }
+
+    if (w < m_minWidth)  w = m_minWidth;
+    if (h < m_minHeight) h = m_minHeight;
+}
+
+
+void Client::resize(XButtonEvent *e, Boolean horizontal, Boolean vertical)
+{
+    if (isFixedSize()) return;
+
+    if (m_windowManager->attemptGrab
+	(root(), None, DragMask, e->time) != GrabSuccess) {
+	return;
+    }
+
+    if (vertical && horizontal)
+	m_windowManager->installCursor(WindowManager::DownrightCursor);
+    else if (vertical)
+	m_windowManager->installCursor(WindowManager::DownCursor);
+    else
+	m_windowManager->installCursor(WindowManager::RightCursor);
+
+    Window dummy;
+    XTranslateCoordinates(display(), e->window, parent(),
+			  e->x, e->y, &e->x, &e->y, &dummy);
+
+    int xorig = e->x;
+    int yorig = e->y;
+    int x = xorig;
+    int y = yorig;
+    int w = m_w, h = m_h;
+    int prevW, prevH;
+    int dw, dh;
+
+    XEvent event;
+    Boolean found;
+    Boolean doSomething = False;
+    Boolean done = False;
+    struct timeval sleepval;
+
+    while (!done) {
+
+	found = False;
+
+	while (XCheckMaskEvent(display(), DragMask | ExposureMask, &event)) {
+	    found = True;
+	    if (event.type != MotionNotify) break;
+	}
+
+	if (!found) {
+	    sleepval.tv_sec = 0;
+	    sleepval.tv_usec = 50000;
+	    select(0, 0, 0, 0, &sleepval);
+	    continue;
+	}
+	
+	switch (event.type) {
+
+	default:
+	    fprintf(stderr, "wm2: unknown event type %d\n", event.type);
+	    break;
+
+	case Expose:
+	    m_windowManager->eventExposure(&event.xexpose);
+	    break;
+
+	case ButtonPress:
+	    // don't like this
+	    XUngrabPointer(display(), event.xbutton.time);
+	    done = True;
+	    break;
+
+	case ButtonRelease:
+
+	    x = event.xbutton.x; y = event.xbutton.y;
+
+	    if (!nobuttons(&event.xbutton)) x = -1;
+	    m_windowManager->releaseGrab(&event.xbutton);
+	    
+	    done = True;
+	    break;
+
+	case MotionNotify:
+	    x = event.xbutton.x; y = event.xbutton.y;
+
+	    if (vertical && horizontal) {
+		prevH = h; h = y - m_y;
+		prevW = w; w = x - m_x;
+		fixResizeDimensions(w, h, dw, dh);
+		if (h == prevH && w == prevW) break;
+		m_border->configure(m_x, m_y, w, h, CWWidth | CWHeight, 0);
+		windowManager()->showGeometry(dw, dh);
+		doSomething = True;
+
+	    } else if (vertical) {
+		prevH = h; h = y - m_y;
+		fixResizeDimensions(w, h, dw, dh);
+		if (h == prevH) break;
+		m_border->configure(m_x, m_y, w, h, CWHeight, 0);
+		windowManager()->showGeometry(dw, dh);
+		doSomething = True;
+
+	    } else {
+		prevW = w; w = x - m_x;
+		fixResizeDimensions(w, h, dw, dh);
+		if (w == prevW) break;
+		m_border->configure(m_x, m_y, w, h, CWWidth, 0);
+		windowManager()->showGeometry(dw, dh);
+		doSomething = True;
+	    }
+
+	    break;
+	}
+    }
+
+    if (doSomething) {
+
+	windowManager()->removeGeometry();
+
+	if (vertical && horizontal) {
+	    m_w = x - m_x;
+	    m_h = y - m_y;
+	    fixResizeDimensions(m_w, m_h, dw, dh);
+	    m_border->configure(m_x, m_y, m_w, m_h, CWWidth|CWHeight, 0, True);
+	} else if (vertical) {
+	    m_h = y - m_y;
+	    fixResizeDimensions(m_w, m_h, dw, dh);
+	    m_border->configure(m_x, m_y, m_w, m_h, CWHeight, 0, True);
+	} else {
+	    m_w = x - m_x;
+	    fixResizeDimensions(m_w, m_h, dw, dh);
+	    m_border->configure(m_x, m_y, m_w, m_h, CWWidth, 0, True);
+	}
+
+	XMoveResizeWindow(display(), m_window,
+			  m_border->xIndent(), m_border->yIndent(), m_w, m_h);
+
+	sendConfigureNotify();
+    }
+
+    m_windowManager->installCursor(WindowManager::NormalCursor);
+}
+
+
+void Client::moveOrResize(XButtonEvent *e)
+{
+    if (e->x < m_border->xIndent() && e->y > m_h) {
+	resize(e, False, True);
+    } else if (e->y < m_border->yIndent() &&
+	       e->x > m_w + m_border->xIndent() - m_border->yIndent()) { //hack
+	resize(e, True, False);
+    } else {
+	move(e);
+    }
+}
+
+
+void Border::eventButton(XButtonEvent *e)
+{
+    if (e->window == m_parent) {
+
+	if (!m_client->isActive()) return;
+	if (isTransient()) {
+	    if (e->x >= xIndent() && e->y >= yIndent()) {
+		return;
+	    } else {
+		m_client->move(e);
+		return;
+	    }
+	}
+
+	m_client->moveOrResize(e);
+	return;
+	
+    } else if (e->window == m_tab) {
+	m_client->move(e);
+	return;
+    }
+
+    if (e->window == m_resize) {
+	m_client->resize(e, True, True);
+	return;
+    }
+
+    if (e->window != m_button || e->type == ButtonRelease) return;
+
+    if (windowManager()->attemptGrab(m_button, None, MenuGrabMask, e->time)
+	!= GrabSuccess) {
+	return;
+    }
+
+    XEvent event;
+    Boolean found;
+    Boolean done = False;
+    struct timeval sleepval;
+    unsigned long tdiff = 0L;
+    int x = e->x;
+    int y = e->y;
+    int action = 1;
+    int buttonSize = m_tabWidth - m_tabTopHeight*2 - 4;
+
+    XFillRectangle(display(), m_button, m_drawGC, 0, 0, buttonSize, buttonSize);
+
+    while (!done) {
+
+	found = False;
+
+	if (tdiff > 1500L && action == 1) {
+	    windowManager()->installCursor(WindowManager::DeleteCursor);
+	    action = 2;
+	}
+
+	while (XCheckMaskEvent(display(), MenuMask, &event)) {
+	    found = True;
+	    if (event.type != MotionNotify) break;
+	}
+
+	if (!found) {
+	    sleepval.tv_sec = 0;
+	    sleepval.tv_usec = 50000;
+	    select(0, 0, 0, 0, &sleepval);
+	    tdiff += 50;
+	    continue;
+	}
+
+	switch (event.type) {
+
+	default:
+	    fprintf(stderr, "wm2: unknown event type %d\n", event.type);
+	    break;
+
+	case Expose:
+	    windowManager()->eventExposure(&event.xexpose);
+	    break;
+
+	case ButtonPress:
+	    break;
+
+	case ButtonRelease:
+
+	    if (!nobuttons(&event.xbutton)) {
+		action = 0;
+	    }
+
+	    if (x < 0 || y < 0 || x >= buttonSize || y >= buttonSize) {
+		action = 0;
+	    }
+
+	    windowManager()->releaseGrab(&event.xbutton);
+	    done = True;
+	    break;
+
+	case MotionNotify:
+	    tdiff = event.xmotion.time - e->time;
+	    if (tdiff > 5000L) tdiff = 5001L; // in case of overflow!
+
+	    x = event.xmotion.x;
+	    y = event.xmotion.y;
+
+	    if (action == 0 || action == 2) {
+		if (x < 0 || y < 0 || x >= buttonSize || y >= buttonSize) {
+		    windowManager()->installCursor(WindowManager::NormalCursor);
+		    action = 0;
+		} else {
+		    windowManager()->installCursor(WindowManager::DeleteCursor);
+		    action = 2;
+		}
+	    }
+
+	    break;
+	}
+    }
+
+    XClearWindow(display(), m_button);
+    windowManager()->installCursor(WindowManager::NormalCursor);
+
+    if (tdiff > 5000L) {	// do nothing, they dithered too long
+	return;
+    }
+
+    if (action == 1) m_client->hide();
+    else if (action == 2) m_client->kill();
+}
+

          
A => Client.C +714 -0
@@ 0,0 1,714 @@ 
+
+#include "Manager.h"
+#include "Client.h"
+
+#include <X11/Xutil.h>
+
+const char *const Client::m_defaultLabel = "incognito";
+
+
+
+Client::Client(WindowManager *const wm, Window w) :
+    m_window(w),
+    m_transient(None),
+    m_revert(0),
+    m_fixedSize(False),
+    m_state(WithdrawnState),
+    m_initialising(False),
+    m_reparenting(False),
+    m_colormap(None),
+    m_colormapWinCount(0),
+    m_colormapWindows(NULL),
+    m_windowColormaps(NULL),
+    m_windowManager(wm)
+{
+    XWindowAttributes attr;
+    XGetWindowAttributes(display(), m_window, &attr);
+
+    m_x = attr.x;
+    m_y = attr.y;
+    m_w = attr.width;
+    m_h = attr.height;
+    m_bw = attr.border_width;
+    m_name = m_iconName = 0;
+    m_sizeHints.flags = 0L;
+
+    m_label = NewString(m_defaultLabel);
+    m_border = new Border(this, w);
+
+    if (attr.map_state == IsViewable) manage(True);
+}
+
+
+Client::~Client()
+{
+    // empty
+}    
+
+
+void Client::release()
+{
+    // assume wm called for this, and will remove me from its list itself
+
+//    fprintf(stderr, "deleting client %p\n",this);
+
+    if (m_window == None) {
+	fprintf(stderr,
+		"wm2: invalid parent in Client::release (released twice?)\n");
+    }
+
+    windowManager()->skipInRevert(this, m_revert);
+
+//    fprintf(stderr, "deleting %lx\n",m_window);
+
+    if (isHidden()) unhide(False);
+
+    delete m_border;
+    m_window = None;
+
+    if (isActive()) {
+	if (CONFIG_CLICK_TO_FOCUS) {
+	    if (m_revert) {
+		windowManager()->setActiveClient(m_revert);
+		m_revert->activate();
+	    } else windowManager()->clearFocus();
+	} else {
+	    windowManager()->setActiveClient(0);
+	}
+    }
+
+    if (m_colormapWinCount > 0) {
+	XFree((char *)m_colormapWindows);
+	free((char *)m_windowColormaps); // not allocated through X
+    }
+
+    if (m_iconName) XFree(m_iconName);
+    if (m_name)     XFree(m_name);
+    if (m_label) free((void *)m_label);
+
+    delete this;
+}
+
+
+void Client::unreparent()
+{
+    XWindowChanges wc;
+
+    if (!isWithdrawn()) {
+	gravitate(True);
+	XReparentWindow(display(), m_window, root(), m_x, m_y);
+    }
+
+    wc.border_width = m_bw;
+    XConfigureWindow(display(), m_window, CWBorderWidth, &wc);
+
+    XSync(display(), True);
+}
+
+
+void Client::installColormap()
+{
+    Client *cc = 0;
+    int i, found;
+
+    if (m_colormapWinCount != 0) {
+
+	found = 0;
+
+	for (i = m_colormapWinCount - 1; i >= 0; --i) {
+	    windowManager()->installColormap(m_windowColormaps[i]);
+	    if (m_colormapWindows[i] == m_window) ++found;
+	}
+
+	if (found == 0) {
+	    windowManager()->installColormap(m_colormap);
+	}
+
+    } else if (m_transient != None &&
+	       (cc = windowManager()->windowToClient(m_transient))) {
+
+	cc->installColormap();
+    } else {
+	windowManager()->installColormap(m_colormap);
+    }
+}
+
+
+void Client::manage(Boolean mapped)
+{
+    Boolean shouldHide, reshape;
+    XWMHints *hints;
+    Display *d = display();
+    long mSize;
+    int state;
+
+    XSelectInput(d, m_window, ColormapChangeMask | EnterWindowMask |
+		 PropertyChangeMask | FocusChangeMask);
+
+    m_iconName = getProperty(XA_WM_ICON_NAME);
+    m_name = getProperty(XA_WM_NAME);
+    setLabel();
+
+    getColormaps();
+    getProtocols();
+    getTransient();
+
+    hints = XGetWMHints(d, m_window);
+
+    if (!getState(&state)) {
+	state = hints ? hints->initial_state : NormalState;
+    }
+
+    shouldHide = (state == IconicState);
+    if (hints) XFree(hints);
+
+    if (XGetWMNormalHints(d, m_window, &m_sizeHints, &mSize) == 0 ||
+	m_sizeHints.flags == 0) {
+	m_sizeHints.flags = PSize;
+    }
+
+    m_fixedSize = False;
+//    if ((m_sizeHints.flags & (USSize | PSize))) m_fixedSize = True;
+    if ((m_sizeHints.flags & (PMinSize | PMaxSize)) == (PMinSize | PMaxSize) &&
+	(m_sizeHints.min_width  == m_sizeHints.max_width &&
+	 m_sizeHints.min_height == m_sizeHints.max_height)) m_fixedSize = True;
+
+    reshape = !mapped;
+
+    if (m_fixedSize) {
+	if ((m_sizeHints.flags & USPosition)) reshape = False;
+	if ((m_sizeHints.flags & PPosition) && shouldHide) reshape = False;
+	if ((m_transient != None)) reshape = False;
+    }
+
+    if ((m_sizeHints.flags & PBaseSize)) {
+	m_minWidth  = m_sizeHints.base_width;
+	m_minHeight = m_sizeHints.base_height;
+    } else if ((m_sizeHints.flags & PMinSize)) {
+	m_minWidth  = m_sizeHints.min_width;
+	m_minHeight = m_sizeHints.min_height;
+    } else {
+	m_minWidth = m_minHeight = 50;
+    }
+
+    // act
+
+    gravitate(False);
+
+    if (m_w < m_minWidth) {
+	m_w = m_minWidth; m_fixedSize = False; reshape = True;
+    }
+    if (m_h < m_minHeight) {
+	m_h = m_minHeight; m_fixedSize = False; reshape = True;
+    }
+
+    // zeros are iffy, should be calling some Manager method
+
+    if (m_x > DisplayWidth(display(), 0) - m_border->xIndent()) {
+	m_x = DisplayWidth(display(), 0) - m_border->xIndent();
+    }
+
+    if (m_y > DisplayHeight(display(), 0) - m_border->yIndent()) {
+	m_y = DisplayHeight(display(), 0) - m_border->yIndent();
+    }
+
+    if (m_x < m_border->xIndent()) m_x = m_border->xIndent();
+    if (m_y < m_border->yIndent()) m_y = m_border->yIndent();
+    
+    m_border->configure(m_x, m_y, m_w, m_h, 0L, Above);
+
+    if (mapped) m_reparenting = True;
+    if (reshape && !m_fixedSize) XResizeWindow(d, m_window, m_w, m_h);
+    XSetWindowBorderWidth(d, m_window, 0);
+
+    m_border->reparent();
+
+    // (support for shaped windows absent)
+
+    XAddToSaveSet(d, m_window);
+
+    if (shouldHide) hide();
+    else {
+	XMapWindow(d, m_window);
+	m_border->map();
+
+	if (CONFIG_CLICK_TO_FOCUS ||
+	    (m_transient != None && activeClient() &&
+	    activeClient()->m_window == m_transient)) {
+	    activate();
+	} else {
+	    deactivate();
+	}
+
+	setState(NormalState);
+    }
+    
+    if (activeClient() && !isActive()) {
+	activeClient()->installColormap();
+    }
+
+    m_initialising = True;
+}
+
+
+void Client::decorate(Boolean active)
+{
+    m_border->decorate(active, m_w, m_h);
+}
+
+
+void Client::activate()
+{
+//    fprintf(stderr, "Client::activate (this = %p, window = %x, parent = %x)\n",
+//	    this, m_window, parent());
+
+    if (parent() == root()) {
+	fprintf(stderr, "wm2: warning: bad parent in Client::activate\n");
+	return;
+    }
+
+    if (isActive()) {
+	decorate(True);
+	if (CONFIG_RAISE_ON_FOCUS) mapRaised();
+	return;
+    }
+
+    if (activeClient()) {
+	activeClient()->deactivate();
+	// & some other-screen business
+    }
+
+    XUngrabButton(display(), AnyButton, AnyModifier, parent());
+
+    XSetInputFocus(display(), m_window, RevertToPointerRoot,
+		   windowManager()->timestamp(False));
+
+    if (m_protocol & PtakeFocus) {
+	sendMessage(Atoms::wm_protocols, Atoms::wm_takeFocus);
+    }
+
+    // now set revert of window that reverts to this one so as to
+    // revert to the window this one used to revert to (huh?)
+
+    windowManager()->skipInRevert(this, m_revert);
+
+    m_revert = activeClient();
+    while (m_revert && !m_revert->isNormal()) m_revert = m_revert->revertTo();
+
+    windowManager()->setActiveClient(this);
+    if (CONFIG_RAISE_ON_FOCUS) mapRaised();
+    decorate(True);
+
+    installColormap();		// new!
+}
+
+
+void Client::deactivate()	// called from wm?
+{
+//    fprintf(stderr, 
+//	    "Client::deactivate (this = %p, window = %x, parent = %x)\n",
+//	    this, m_window, parent());
+
+    if (parent() == root()) {
+	fprintf(stderr, "wm2: warning: bad parent in Client::deactivate\n");
+	return;
+    }
+
+    XGrabButton(display(), AnyButton, AnyModifier, parent(), False,
+		ButtonPressMask | ButtonReleaseMask,
+		GrabModeAsync, GrabModeSync, None, None);
+
+    decorate(False);
+}
+
+
+void Client::sendMessage(Atom a, long l)
+{
+    XEvent ev;
+    int status;
+    long mask;
+
+    memset(&ev, 0, sizeof(ev));
+    ev.xclient.type = ClientMessage;
+    ev.xclient.window = m_window;
+    ev.xclient.message_type = a;
+    ev.xclient.format = 32;
+    ev.xclient.data.l[0] = l;
+    ev.xclient.data.l[1] = windowManager()->timestamp(False);
+    mask = 0L;
+    status = XSendEvent(display(), m_window, False, mask, &ev);
+
+    if (status == 0) {
+	fprintf(stderr, "wm2: warning: Client::sendMessage failed\n");
+    }
+}
+
+
+static int getProperty_aux(Display *d, Window w, Atom a, Atom type, long len,
+			   unsigned char **p)
+{
+    Atom realType;
+    int format;
+    unsigned long n, extra;
+    int status;
+
+    status = XGetWindowProperty(d, w, a, 0L, len, False, type, &realType,
+				&format, &n, &extra, p);
+
+    if (status != Success || *p == 0) return -1;
+    if (n == 0) XFree((void *) *p);
+
+    return n;
+}
+
+
+char *Client::getProperty(Atom a)
+{
+    unsigned char *p;
+    if (getProperty_aux(display(), m_window, a, XA_STRING, 100L, &p) <= 0) {
+	return NULL;
+    }
+    return (char *)p;
+}
+
+
+int Client::getAtomProperty(Atom a, Atom type)
+{
+    char **p, *x;
+    if (getProperty_aux(display(), m_window, a, type, 1L,
+			(unsigned char **)&p) <= 0) {
+	return 0;
+    }
+
+    x = *p;
+    XFree((void *)p);
+    return (int)x;
+}
+
+
+int Client::getIntegerProperty(Atom a)
+{
+    return getAtomProperty(a, XA_INTEGER);
+}
+
+
+void Client::setState(int state)
+{
+    m_state = state;
+
+//    fprintf(stderr, "state set to %d\n",state);
+
+    long data[2];
+    data[0] = (long)state;
+    data[1] = (long)None;
+
+    XChangeProperty(display(), m_window, Atoms::wm_state, Atoms::wm_state,
+		    32, PropModeReplace, (unsigned char *)data, 2);
+}
+
+
+Boolean Client::getState(int *state)
+{
+    long *p = 0;
+
+    if (getProperty_aux(display(), m_window, Atoms::wm_state, Atoms::wm_state,
+			2L, (unsigned char **)&p) <= 0) {
+	return False;
+    }
+
+    *state = (int) *p;
+    XFree((char *)p);
+    return True;
+}
+
+
+void Client::getProtocols()
+{
+    long n;
+    Atom *p;
+
+    m_protocol = 0;
+    if ((n = getProperty_aux(display(), m_window, Atoms::wm_protocols, XA_ATOM,
+			     20L, (unsigned char **)&p)) <= 0) {
+	return;
+    }
+
+    for (int i = 0; i < n; ++i) {
+	if (p[i] == Atoms::wm_delete) {
+	    m_protocol |= Pdelete;
+	} else if (p[i] == Atoms::wm_takeFocus) {
+	    m_protocol |= PtakeFocus;
+	}
+    }
+
+    XFree((char *) p);
+}
+
+
+void Client::gravitate(Boolean invert)
+{
+    int gravity;
+    int w = 0, h = 0, xdelta, ydelta;
+
+    // possibly shouldn't work if we haven't been managed yet?
+
+    gravity = NorthWestGravity;
+    if (m_sizeHints.flags & PWinGravity) gravity = m_sizeHints.win_gravity;
+
+    xdelta = m_bw - m_border->xIndent();
+    ydelta = m_bw - m_border->yIndent();
+
+    // note that right and bottom borders have indents of 1
+
+    switch (gravity) {
+
+    case NorthWestGravity:
+	break;
+
+    case NorthGravity:
+	w = xdelta;
+	break;
+
+    case NorthEastGravity:
+	w = xdelta + m_bw-1;
+	break;
+
+    case WestGravity:
+	h = ydelta;
+	break;
+
+    case CenterGravity:
+    case StaticGravity:
+	w = xdelta;
+	h = ydelta;
+	break;
+
+    case EastGravity:
+	w = xdelta + m_bw-1;
+	h = ydelta;
+	break;
+
+    case SouthWestGravity:
+	h = ydelta + m_bw-1;
+	break;
+
+    case SouthGravity:
+	w = xdelta;
+	h = ydelta + m_bw-1;
+	break;
+
+    case SouthEastGravity:
+	w = xdelta + m_bw-1;
+	h = ydelta + m_bw-1;
+	break;
+
+    default:
+	fprintf(stderr, "wm2: bad window gravity %d for window 0x%lx\n",
+		gravity, m_window);
+	return;
+    }
+
+    w += m_border->xIndent();
+    h += m_border->yIndent();
+
+    if (invert) { w = -w; h = -h; }
+
+    m_x += w;
+    m_y += h;
+}
+
+
+Boolean Client::setLabel(void)
+{
+    const char *newLabel;
+
+    if (m_name) newLabel = m_name;
+    else if (m_iconName) newLabel = m_iconName;
+    else newLabel = m_defaultLabel;
+
+    if (!m_label) {
+
+	m_label = NewString(newLabel);
+	return True;
+
+    } else if (strcmp(m_label, newLabel)) {
+
+	free((void *)m_label);
+	m_label = NewString(newLabel);
+	return True;
+
+    } else return True;//False;// dammit!
+}
+
+
+void Client::getColormaps(void)
+{
+    int i, n;
+    Window *cw;
+    XWindowAttributes attr;
+
+    if (!m_initialising) {
+	XGetWindowAttributes(display(), m_window, &attr);
+	m_colormap = attr.colormap;
+
+//	fprintf(stderr, "colormap for %s is %p\n",m_label, (void *)m_colormap);
+    }
+
+    n = getProperty_aux(display(), m_window, Atoms::wm_colormaps, XA_WINDOW,
+			100L, (unsigned char **)&cw);
+
+    if (m_colormapWinCount != 0) {
+	XFree((char *)m_colormapWindows);
+	free((char *)m_windowColormaps);
+    }
+
+    if (n <= 0) {
+	m_colormapWinCount = 0;
+	return;
+    }
+    
+    m_colormapWinCount = n;
+    m_colormapWindows = cw;
+
+    m_windowColormaps = (Colormap *)malloc(n * sizeof(Colormap));
+
+    for (i = 0; i < n; ++i) {
+	if (cw[i] == m_window) {
+	    m_windowColormaps[i] = m_colormap;
+	} else {
+	    XSelectInput(display(), cw[i], ColormapChangeMask);
+	    XGetWindowAttributes(display(), cw[i], &attr);
+	    m_windowColormaps[i] = attr.colormap;
+	}
+    }
+}
+
+
+void Client::getTransient()
+{
+    Window t = None;
+
+    if (XGetTransientForHint(display(), m_window, &t) != 0) {
+	m_transient = t;
+    } else {
+	m_transient = None;
+    }
+}
+
+
+void Client::hide()
+{
+    if (isHidden()) {
+	fprintf(stderr, "wm2: Client already hidden in Client::hide\n");
+	return;
+    }
+
+    m_border->unmap();
+    XUnmapWindow(display(), m_window);
+
+    if (activeClient() == this) windowManager()->clearFocus();
+
+    setState(IconicState);
+    windowManager()->addToHiddenList(this);
+}
+
+
+void Client::unhide(Boolean map)
+{
+    if (!isHidden()) {
+	fprintf(stderr, "wm2: Client not hidden in Client::unhide\n");
+	return;
+    }
+
+    windowManager()->removeFromHiddenList(this);
+
+    if (map) {
+	setState(NormalState);
+	XMapWindow(display(), m_window);
+	mapRaised();
+
+	if (CONFIG_CLICK_TO_FOCUS) activate();
+    }
+}
+
+
+void Client::sendConfigureNotify()
+{
+    XConfigureEvent ce;
+
+    ce.type   = ConfigureNotify;
+    ce.event  = m_window;
+    ce.window = m_window;
+
+    ce.x = m_x;
+    ce.y = m_y;
+    ce.width  = m_w;
+    ce.height = m_h;
+    ce.border_width = m_bw;
+    ce.above = None;
+    ce.override_redirect = 0;
+
+    XSendEvent(display(), m_window, False, StructureNotifyMask, (XEvent*)&ce);
+}
+
+
+void Client::withdraw()
+{
+    m_border->unmap();
+
+    gravitate(True);
+    XReparentWindow(display(), m_window, root(), m_x, m_y);
+
+//    delete m_border;
+//    m_border = new Border(this, m_window);
+//    m_parent = root();
+
+    gravitate(False);
+
+    XRemoveFromSaveSet(display(), m_window);
+    setState(WithdrawnState);
+
+    ignoreBadWindowErrors = True;
+    XSync(display(), False);
+    ignoreBadWindowErrors = False;
+}
+
+
+void Client::rename()
+{
+    m_border->configure(0, 0, m_w, m_h, CWWidth | CWHeight, Above);
+}
+
+
+void Client::mapRaised()
+{
+    m_border->mapRaised();
+    windowManager()->raiseTransients(this);
+}
+
+
+void Client::kill()
+{
+    if (m_protocol & Pdelete) {
+	sendMessage(Atoms::wm_protocols, Atoms::wm_delete);
+    } else {
+	XKillClient(display(), m_window);
+    }
+}
+
+
+void Client::ensureVisible()
+{
+    int mx = DisplayWidth(display(), 0) - 1; // hack
+    int my = DisplayHeight(display(), 0) - 1;
+    int px = m_x;
+    int py = m_y;
+    
+    if (m_x + m_w > mx) m_x = mx - m_w;
+    if (m_y + m_h > my) m_y = my - m_h;
+    if (m_x < 0) m_x = 0;
+    if (m_y < 0) m_y = 0;
+
+    if (m_x != px || m_y != py) m_border->moveTo(m_x, m_y);
+}
+

          
A => Client.h +146 -0
@@ 0,0 1,146 @@ 
+
+#ifndef _CLIENT_H_
+#define _CLIENT_H_
+
+#include "General.h"
+#include "Manager.h"
+#include "Border.h"
+
+
+class Client {
+public:
+    Client(WindowManager *const, Window);
+    void release();
+
+    /* for call from WindowManager: */
+
+    void activate();		/* active() */
+    void deactivate();		/* setactive(0) */
+    void gravitate(Boolean invert);
+    void installColormap();
+    void unreparent();
+    void withdraw();
+    void hide();
+    void unhide(Boolean map);
+    void rename();
+    void kill();
+    void mapRaised();		// without activating
+
+    void move(XButtonEvent *);		// event for grab timestamp & coords
+    void resize(XButtonEvent *, Boolean, Boolean);
+    void moveOrResize(XButtonEvent *);
+    void ensureVisible();	// make sure x, y are on-screen
+
+    void manage(Boolean mapped);
+    Boolean hasWindow(Window w) {
+	return ((m_window == w) || m_border->hasWindow(w));
+    }
+
+    Client *revertTo() { return m_revert; }
+    void setRevertTo(Client *c) { m_revert = c; }
+
+    Boolean isHidden()    { return (m_state == IconicState);    }
+    Boolean isWithdrawn() { return (m_state == WithdrawnState); }
+    Boolean isNormal()    { return (m_state == NormalState);    }
+    Boolean isTransient() { return (m_transient != None);       }
+    Window  transientFor() { return m_transient; }
+    Boolean isFixedSize()  { return m_fixedSize; }
+
+    Boolean isInitialising() { return m_initialising; }
+    Boolean isReparenting()  { return m_reparenting;  }
+
+    const char *label()    { return m_label;    }
+    const char *name()     { return m_name;     }
+    const char *iconName() { return m_iconName; }
+
+    void sendMessage(Atom, long);
+    void sendConfigureNotify();
+
+    /* for call from within: */
+
+    void fatal(char *m)    { m_windowManager->fatal(m);              }
+    Display *display()     { return m_windowManager->display();      }
+    Window parent()        { return m_border->parent();              }
+    Window root()          { return m_windowManager->root();         }
+    Client *activeClient() { return m_windowManager->activeClient(); }
+    Boolean isActive()     { return (activeClient() == this);        }
+
+    WindowManager *windowManager() { return m_windowManager; }
+
+    // for call from equivalent wm functions in Events.C:
+
+    void eventButton(XButtonEvent *);
+    void eventMapRequest(XMapRequestEvent *);
+    void eventConfigureRequest(XConfigureRequestEvent *);
+    void eventUnmap(XUnmapEvent *);
+    void eventCreate(XCreateWindowEvent *);
+    void eventColormap(XColormapEvent *);
+    void eventProperty(XPropertyEvent *);
+    void eventEnter(XCrossingEvent *);
+    void eventFocusIn(XFocusInEvent *);
+//    void eventShapeNotify(XShapeEvent *);
+    void eventExposure(XExposeEvent *);
+
+private:
+    // gcc says: class Client only defines a private destructor and
+    // has no friends.  I don't think that's very nice of gcc.  My
+    // code always has plenty of friends.
+    ~Client();
+
+    Window m_window;
+    Window m_transient;
+    Border *m_border;
+
+    Client *m_revert;
+
+    int m_x;
+    int m_y;
+    int m_w;
+    int m_h;
+    int m_bw;
+
+    XSizeHints m_sizeHints;
+    Boolean m_fixedSize;
+    int m_minWidth;
+    int m_minHeight;
+    void fixResizeDimensions(int &, int &, int &, int &);
+
+    int m_state;
+    int m_protocol;
+    Boolean m_initialising;
+    Boolean m_reparenting;
+
+    char *m_name;
+    char *m_iconName;
+    const char *m_label;	// alias: one of (instance,class,name,iconName)
+    static const char *const m_defaultLabel;
+
+    Colormap m_colormap;
+    int m_colormapWinCount;
+    Window *m_colormapWindows;
+    Colormap *m_windowColormaps;
+
+    WindowManager *const m_windowManager;
+
+    char *getProperty(Atom);
+    int getAtomProperty(Atom, Atom);
+    int getIntegerProperty(Atom);
+
+    // accessors 
+    Boolean getState(int *);
+    void setState(int);
+
+    // internal instantiation requests
+    Boolean setLabel(void);	// returns True if changed
+    void getColormaps(void);
+    void getProtocols(void);
+    void getTransient(void);
+
+    void decorate(Boolean active);
+};
+
+#define Pdelete    1
+#define PtakeFocus 2
+
+#endif
+

          
A => Config.h +31 -0
@@ 0,0 1,31 @@ 
+
+#ifndef _CONFIG_H_
+#define _CONFIG_H_
+
+#define CONFIG_NICE_FONT	  "-*-lucida-bold-r-*-*-14-*-75-75-*-*-*-*"
+#define CONFIG_NICE_MENU_FONT	  "-*-lucida-medium-r-*-*-14-*-75-75-*-*-*-*"
+#define CONFIG_NASTY_FONT	  "fixed"
+
+#define CONFIG_EXEC_USING_SHELL   False
+#define CONFIG_NEW_WINDOW_COMMAND "xterm"
+
+// You can't have CLICK_TO_FOCUS True with RAISE_ON_FOCUS False, but
+// the other three combinations seem to work
+
+#define CONFIG_CLICK_TO_FOCUS     False
+#define CONFIG_RAISE_ON_FOCUS     False
+
+#define CONFIG_TAB_FOREGROUND	  "black"
+#define CONFIG_TAB_BACKGROUND     "gray80"
+#define CONFIG_FRAME_BACKGROUND   "white"
+#define CONFIG_BUTTON_BACKGROUND  "white"
+#define CONFIG_BORDERS            "black"
+
+#define CONFIG_MENU_FOREGROUND    "black"
+#define CONFIG_MENU_BACKGROUND    "gray80"
+#define CONFIG_MENU_BORDERS       "black"
+
+#define CONFIG_EVERYTHING_ON_ROOT_MENU    True
+
+#endif
+

          
A => Cursors.h +85 -0
@@ 0,0 1,85 @@ 
+
+#ifndef _WM2_CURSORS_H_
+#define _WM2_CURSORS_H_
+
+#define cursor_width 16
+#define cursor_height 16
+#define cursor_x_hot 1
+#define cursor_y_hot 1
+static unsigned char cursor_bits[] = {
+   0x00, 0x00, 0x06, 0x00, 0x1e, 0x00, 0x7c, 0x00, 0xfc, 0x01, 0xf8, 0x07,
+   0xf8, 0x1f, 0xf0, 0x07, 0xf0, 0x03, 0xe0, 0x07, 0xe0, 0x0e, 0x40, 0x1c,
+   0x40, 0x38, 0x00, 0x70, 0x00, 0x20, 0x00, 0x00};
+
+#define cursor_mask_width 16
+#define cursor_mask_height 16
+static unsigned char cursor_mask_bits[] = {
+   0x07, 0x00, 0x1f, 0x00, 0x7f, 0x00, 0xfe, 0x01, 0xfe, 0x07, 0xfc, 0x1f,
+   0xfc, 0x3f, 0xf8, 0x1f, 0xf8, 0x07, 0xf0, 0x0f, 0xf0, 0x1f, 0xe0, 0x3e,
+   0xe0, 0x7c, 0x40, 0xf8, 0x00, 0x70, 0x00, 0x20};
+
+#define ninja_cross_width 16
+#define ninja_cross_height 16
+#define ninja_cross_x_hot 7
+#define ninja_cross_y_hot 7
+static unsigned char ninja_cross_bits[] = {
+   0x00, 0x00, 0x00, 0x00, 0x18, 0x10, 0x3c, 0x18, 0x38, 0x0c, 0x70, 0x06,
+   0xe0, 0x07, 0xc0, 0x03, 0xc0, 0x01, 0xe0, 0x03, 0x70, 0x06, 0x38, 0x06,
+   0x1c, 0x0c, 0x08, 0x18, 0x00, 0x10, 0x00, 0x00};
+
+#define ninja_cross_mask_width 16
+#define ninja_cross_mask_height 16
+static unsigned char ninja_cross_mask_bits[] = {
+   0x00, 0x00, 0x18, 0x10, 0x3c, 0x38, 0x7e, 0x3c, 0x7c, 0x1e, 0xf8, 0x0f,
+   0xf0, 0x0f, 0xe0, 0x07, 0xe0, 0x03, 0xf0, 0x07, 0xf8, 0x0f, 0x7c, 0x0f,
+   0x3e, 0x1e, 0x1c, 0x3c, 0x08, 0x38, 0x00, 0x10};
+
+#define cursor_right_width 16
+#define cursor_right_height 16
+#define cursor_right_x_hot 13
+#define cursor_right_y_hot 7
+static unsigned char cursor_right_bits[] = {
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x80, 0x03,
+   0x80, 0x0f, 0xfe, 0x3f, 0xfe, 0x3f, 0x80, 0x0f, 0x80, 0x03, 0xc0, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+
+#define cursor_right_mask_width 16
+#define cursor_right_mask_height 16
+static unsigned char cursor_right_mask_bits[] = {
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0xe0, 0x03, 0xc0, 0x0f,
+   0xfe, 0x3f, 0xff, 0x7f, 0xff, 0x7f, 0xfe, 0x3f, 0xc0, 0x0f, 0xe0, 0x03,
+   0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+
+#define cursor_down_width 16
+#define cursor_down_height 16
+#define cursor_down_x_hot 7
+#define cursor_down_y_hot 13
+static unsigned char cursor_down_bits[] = {
+   0x00, 0x00, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01,
+   0x90, 0x09, 0xf0, 0x0f, 0xe0, 0x07, 0xe0, 0x07, 0xc0, 0x03, 0xc0, 0x03,
+   0x80, 0x01, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00};
+
+#define cursor_down_mask_width 16
+#define cursor_down_mask_height 16
+static unsigned char cursor_down_mask_bits[] = {
+   0x80, 0x01, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xd0, 0x0b,
+   0xf8, 0x1f, 0xf8, 0x1f, 0xf0, 0x0f, 0xf0, 0x0f, 0xe0, 0x07, 0xe0, 0x07,
+   0xc0, 0x03, 0xc0, 0x03, 0x80, 0x01, 0x00, 0x00};
+
+#define cursor_down_right_width 16
+#define cursor_down_right_height 16
+#define cursor_down_right_x_hot 13
+#define cursor_down_right_y_hot 13
+static unsigned char cursor_down_right_bits[] = {
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x70, 0x00,
+   0xe0, 0x04, 0xc0, 0x05, 0x80, 0x0f, 0x00, 0x0f, 0xc0, 0x1f, 0x00, 0x1f,
+   0x00, 0x3c, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00};
+
+#define cursor_down_right_mask_width 16
+#define cursor_down_right_mask_height 16
+static unsigned char cursor_down_right_mask_bits[] = {
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x70, 0x00, 0xf8, 0x04,
+   0xf0, 0x0f, 0xe0, 0x0f, 0xc0, 0x1f, 0xc0, 0x1f, 0xe0, 0x3f, 0xc0, 0x3f,
+   0x00, 0x7f, 0x00, 0x7c, 0x00, 0x70, 0x00, 0x00};
+
+#endif

          
A => Events.C +537 -0
@@ 0,0 1,537 @@ 
+
+#include "Manager.h"
+#include "Client.h"
+
+
+// hack:
+static unsigned long swallowNextEnterEvent = 0L;
+
+
+int WindowManager::loop()
+{
+    XEvent ev;
+    m_looping = True;
+
+    while (m_looping) {
+
+	nextEvent(&ev);
+	m_currentTime = CurrentTime;
+
+	switch (ev.type) {
+
+	case ButtonPress:
+	    eventButton(&ev.xbutton);
+	    break;
+
+	case ButtonRelease:
+	    break;
+
+	case MapRequest:
+	    eventMapRequest(&ev.xmaprequest);
+	    break;
+
+	case ConfigureRequest:
+	    eventConfigureRequest(&ev.xconfigurerequest);
+	    break;
+
+	case UnmapNotify:
+	    eventUnmap(&ev.xunmap);
+	    break;
+
+	case CreateNotify:
+	    eventCreate(&ev.xcreatewindow);
+	    break;
+
+	case DestroyNotify:
+	    eventDestroy(&ev.xdestroywindow);
+	    break;
+
+	case ClientMessage:
+	    eventClient(&ev.xclient);
+	    break;
+
+	case ColormapNotify:
+	    eventColormap(&ev.xcolormap);
+	    break;
+	    
+	case PropertyNotify:
+	    eventProperty(&ev.xproperty);
+	    break;
+	    
+	case SelectionClear:
+	    fprintf(stderr, "wm2: SelectionClear (this should not happen)\n");
+	    break;
+
+	case SelectionNotify:
+	    fprintf(stderr, "wm2: SelectionNotify (this should not happen)\n");
+	    break;
+
+	case SelectionRequest:
+	    fprintf(stderr, "wm2: SelectionRequest (this should not happen)\n");
+	    break;
+
+	case EnterNotify:
+	case LeaveNotify:
+	    eventEnter(&ev.xcrossing);
+	    break;
+
+	case ReparentNotify:
+	    eventReparent(&ev.xreparent);
+	    break;
+
+	case FocusIn:
+	    eventFocusIn(&ev.xfocus);
+	    break;
+
+	case Expose:		// might be wm tab
+	    eventExposure(&ev.xexpose);
+	    break;
+
+	case MotionNotify:
+	case FocusOut:
+	case ConfigureNotify:
+	case MapNotify:
+	case MappingNotify:
+
+#ifdef DEBUG_EV
+	    trace("ignore", 0, &ev);
+#endif
+	    break;
+
+	default:
+	    if (ev.type == m_shapeEvent) eventShapeNotify((XShapeEvent *)&ev);
+	    else fprintf(stderr, "wm2: unsupported event type %d\n", ev.type);
+	    break;
+	}
+    }
+    
+    release();
+    return m_returnCode;
+}
+
+
+void WindowManager::nextEvent(XEvent *e)
+{
+    int fd;
+    fd_set rfds;
+    struct timeval t;
+
+    if (!m_signalled) {
+
+	if (QLength(m_display) > 0) {
+	    XNextEvent(m_display, e);
+	    return;
+	}
+
+	fd = ConnectionNumber(m_display);
+	memset((void *)&rfds, 0, sizeof(fd_set)); // SGI's FD_ZERO is fucked
+	FD_SET(fd, &rfds);
+	t.tv_sec = t.tv_usec = 0;
+
+	if (select(fd + 1, &rfds, NULL, NULL, &t) == 1) {
+	    XNextEvent(m_display, e);
+	    return;
+	}
+
+	XFlush(m_display);
+	FD_SET(fd, &rfds);
+
+	if (select(fd + 1, &rfds, NULL, NULL, NULL) == 1) {
+	    XNextEvent(m_display, e);
+	    return;
+	}
+
+	if (errno != EINTR || !m_signalled) {
+	    perror("wm2: select failed");
+	    m_looping = False;
+	}
+    }
+
+    fprintf(stderr, "wm2: signal caught, exiting\n");
+    m_looping = False;
+    m_returnCode = 0;
+}
+
+
+void WindowManager::eventConfigureRequest(XConfigureRequestEvent *e)
+{
+    XWindowChanges wc;
+    Client *c = windowToClient(e->window);
+
+    e->value_mask &= ~CWSibling;
+    if (c) c->eventConfigureRequest(e);
+    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(display(), e->window, e->value_mask, &wc);
+    }
+}
+
+
+void Client::eventConfigureRequest(XConfigureRequestEvent *e)
+{
+    XWindowChanges wc;
+    Boolean raise = False;
+
+    e->value_mask &= ~CWSibling;
+
+    gravitate(True);
+
+    if (e->value_mask & CWX)      m_x = e->x;
+    if (e->value_mask & CWY)      m_y = e->y;
+    if (e->value_mask & CWWidth)  m_w = e->width;
+    if (e->value_mask & CWHeight) m_h = e->height;
+    if (e->value_mask & CWBorderWidth) m_bw = e->border_width;
+
+    gravitate(False);
+
+    if (e->value_mask & CWStackMode) {
+	if (e->detail == Above) raise = True;
+	e->value_mask &= ~CWStackMode;
+    }
+
+    if (parent() != root() && m_window == e->window) {
+	m_border->configure(m_x, m_y, m_w, m_h, e->value_mask, e->detail);
+	sendConfigureNotify();
+    }
+
+    if (m_initialising) {
+	wc.x = m_border->xIndent();
+	wc.y = m_border->yIndent();
+    } 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(display(), e->window, e->value_mask, &wc);
+
+    // if parent==root, it's not managed yet -- & it'll be raised when it is
+    if (raise && parent() != root()) {
+	mapRaised();
+	if (CONFIG_CLICK_TO_FOCUS) activate();
+    }
+}
+
+
+void WindowManager::eventMapRequest(XMapRequestEvent *e)
+{
+    Client *c = windowToClient(e->window);
+
+    // some stuff for multi-screen fuckups here, omitted
+
+    if (c) c->eventMapRequest(e);
+    else {
+	fprintf(stderr, "wm2: bad map request for window %lx\n", e->window);
+    }
+}
+
+
+void Client::eventMapRequest(XMapRequestEvent *)
+{
+    switch(m_state) {
+
+    case WithdrawnState:
+	if (parent() == root()) {
+	    manage(False);
+	    return;
+	}
+
+	m_border->reparent();
+
+	XAddToSaveSet(display(), m_window);
+	// fall through
+
+    case NormalState:
+	XMapWindow(display(), m_window);
+	mapRaised();
+	setState(NormalState);
+
+	if (CONFIG_CLICK_TO_FOCUS)/* ||
+	    (m_transient != None && activeClient() &&
+	     m_transient == activeClient()->m_window))*/ {
+	    activate();
+	}
+	break;
+
+    case IconicState:
+	unhide(True);
+	break;
+    }
+}
+
+
+void WindowManager::eventUnmap(XUnmapEvent *e)
+{
+    Client *c = windowToClient(e->window);
+    if (c) c->eventUnmap(e);
+}
+
+
+void Client::eventUnmap(XUnmapEvent *e)
+{
+    swallowNextEnterEvent = 0L;
+
+    switch (m_state) {
+
+    case IconicState:
+	if (e->send_event) {
+	    unhide(False);
+	    withdraw();
+	}
+	break;
+
+    case NormalState:
+	if (isActive()) m_windowManager->clearFocus();
+	if (!m_reparenting) withdraw();
+	break;
+    }
+
+    m_reparenting = False;
+}
+
+
+void WindowManager::eventCreate(XCreateWindowEvent *e)
+{
+    if (e->override_redirect) return;
+    Client *c = windowToClient(e->window, True);
+    if (c) c->eventCreate(e);
+}
+
+
+void Client::eventCreate(XCreateWindowEvent *)
+{
+    if (!CONFIG_CLICK_TO_FOCUS) {
+	Window r, ch;
+	int x = -1, y = -1, wx, wy;
+	unsigned int k;
+	XQueryPointer(display(), root(), &r, &ch, &x, &y, &wx, &wy, &k);
+	if (x > m_x && y > m_y && x < m_x + m_w && y < m_y + m_h) {
+	    activate();
+	}
+    }
+}
+
+
+void WindowManager::eventDestroy(XDestroyWindowEvent *e)
+{
+    Client *c = windowToClient(e->window);
+
+    if (c) {
+	swallowNextEnterEvent = 0L;
+
+	for (int i = m_clients.count()-1; i >= 0; --i) {
+	    if (m_clients.item(i) == c) {
+		m_clients.remove(i);
+		break;
+	    }
+	}
+
+	c->release();
+
+	ignoreBadWindowErrors = True;
+	XSync(display(), False);
+	ignoreBadWindowErrors = False;
+    }
+}
+
+
+void WindowManager::eventClient(XClientMessageEvent *e)
+{
+    Client *c = windowToClient(e->window);
+
+    if (e->message_type == Atoms::wm_changeState) {
+	if (c && e->format == 32 && e->data.l[0] == IconicState && c != 0) {
+	    if (c->isNormal()) c->hide();
+	    return;
+	}
+    }
+
+    fprintf(stderr, "wm2: unexpected XClientMessageEvent, type 0x%lx, "
+	    "window 0x%lx\n", e->message_type, e->window);
+}
+
+
+void WindowManager::eventColormap(XColormapEvent *e)
+{
+    Client *c = windowToClient(e->window);
+    int i;
+
+    if (e->c_new) {  // this field is called "new" in the old C++-unaware Xlib
+
+	if (c) c->eventColormap(e);
+	else {
+	    for (i = 0; i < m_clients.count(); ++i) {
+		m_clients.item(i)->eventColormap(e);
+	    }
+	}
+    }
+}
+
+
+void Client::eventColormap(XColormapEvent *e)
+{
+    if (e->window == m_window || e->window == parent()) {
+
+	m_colormap = e->colormap;
+	if (isActive()) installColormap();
+
+    } else {
+
+	for (int i = 0; i < m_colormapWinCount; ++i) {
+	    if (m_colormapWindows[i] == e->window) {
+		m_windowColormaps[i] = e->colormap;
+		if (isActive()) installColormap();
+		return;
+	    }
+	}
+    }
+}
+
+
+void WindowManager::eventProperty(XPropertyEvent *e)
+{
+    Client *c = windowToClient(e->window);
+    if (c) c->eventProperty(e);
+}
+
+
+void Client::eventProperty(XPropertyEvent *e)
+{
+    Atom a = e->atom;
+    Boolean shouldDelete = (e->state == PropertyDelete);
+
+    switch (a) {
+
+    case XA_WM_ICON_NAME:
+	if (m_iconName) XFree((char *)m_iconName);
+	m_iconName = shouldDelete ? 0 : getProperty(a);
+	if (setLabel()) rename();
+	return;
+
+    case XA_WM_NAME:
+	if (m_name) XFree((char *)m_name);
+	m_name = shouldDelete ? 0 : getProperty(a);
+	if (setLabel()) rename();
+	return;
+
+    case XA_WM_TRANSIENT_FOR:
+	getTransient();
+	return;
+    }
+
+    if (a == Atoms::wm_colormaps) {
+	getColormaps();
+	if (isActive()) installColormap();
+    }
+}
+
+
+void WindowManager::eventReparent(XReparentEvent *e)
+{
+    if (e->override_redirect) return;
+    (void)windowToClient(e->window, True); // create if absent
+
+    // odd screen complications, omitted
+}
+
+
+void WindowManager::eventEnter(XCrossingEvent *e)
+{
+//    if (e->detail == NotifyVirtual || e->detail == NotifyNonlinearVirtual) {
+//	return;
+//    }
+
+    m_currentTime = e->time;	// not CurrentTime
+
+    Client *c = windowToClient(e->window);
+    if (c) c->eventEnter(e);
+}
+
+
+void Client::eventEnter(XCrossingEvent *e)
+{
+    long s = swallowNextEnterEvent;
+
+    if (s == 0L && e->type == LeaveNotify) {
+
+	if (!CONFIG_CLICK_TO_FOCUS &&
+	    e->window != m_window && e->window != parent() &&
+	    // must be tab or button
+	    ((e->x > 1 && e->x < m_border->xIndent() &&
+	      e->y > 1 && e->y < m_border->xIndent()) ||
+	     (e->x > m_border->xIndent() - m_border->yIndent() &&
+	      e->x < m_border->xIndent() + m_w &&
+	      e->y > 1 && e->y < m_border->yIndent()))) {
+
+	    swallowNextEnterEvent = e->time; // so you can reach the button!
+	}
+	return;
+
+    } else if (s != 0L) {
+
+	swallowNextEnterEvent = 0L;
+
+	if (e->time <= s || e->time - s < 500L) {
+	    return;
+	}
+    }
+
+    if (e->type == EnterNotify && !isActive() && !CONFIG_CLICK_TO_FOCUS) {
+	activate();
+    }
+}
+
+
+void WindowManager::eventFocusIn(XFocusInEvent *e)
+{
+    if (e->detail != NotifyNonlinearVirtual) return;
+    Client *c = windowToClient(e->window);
+
+    if (c) c->eventFocusIn(e);
+}
+
+
+void Client::eventFocusIn(XFocusInEvent *e)
+{
+    if (m_window == e->window && !isActive()) {
+	activate();
+    }
+}
+
+
+void WindowManager::eventExposure(XExposeEvent *e)
+{
+    if (e->count != 0) return;
+    Client *c = windowToClient(e->window);
+    if (c) c->eventExposure(e);
+}
+
+
+void Client::eventExposure(XExposeEvent *e)
+{
+    if (m_border->hasWindow(e->window)) {
+	m_border->expose(e);
+    }
+}
+
+
+
+// don't handle these (yet?)
+void WindowManager::eventShapeNotify(XShapeEvent *) { }

          
A => General.h +58 -0
@@ 0,0 1,58 @@ 
+
+#ifndef _GENERAL_H_
+#define _GENERAL_H_
+
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/time.h>
+
+#define _POSIX_SOURCE 1
+
+#include <stdio.h>
+#include <signal.h>
+#include <errno.h>
+#include <stdlib.h>
+
+#include <X11/X.h>
+#include <X11/Xlib.h>
+#include <X11/Xos.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/Xatom.h>
+
+#include <X11/extensions/shape.h>
+
+// True and False are defined in Xlib.h
+typedef char Boolean;
+
+#define NewString(x) (strcpy((char *)malloc(strlen(x)+1),(x)))
+
+#ifndef SIGNAL_CALLBACK_TYPE
+#define SIGNAL_CALLBACK_TYPE (void (*)(...))
+#endif
+
+#define signal(x,y)     \
+  do { \
+    struct sigaction sAct; \
+    (void)sigemptyset(&sAct.sa_mask); \
+    sAct.sa_flags = 0; \
+    sAct.sa_handler = (SIGNAL_CALLBACK_TYPE(y)); \
+    (void)sigaction((x), &sAct, NULL); \
+  } while (0)
+
+#include "Config.h"
+
+class Atoms {
+public:
+    static Atom wm_state;
+    static Atom wm_changeState;
+    static Atom wm_protocols;
+    static Atom wm_delete;
+    static Atom wm_takeFocus;
+    static Atom wm_colormaps;
+    static Atom wm2_running;
+};
+
+extern Boolean ignoreBadWindowErrors; // tidiness hack
+
+#endif

          
A => Main.C +15 -0
@@ 0,0 1,15 @@ 
+
+#include "Manager.h"
+
+int main(int argc, char **argv)
+{
+    if (argc > 1) {
+	for (int i = strlen(argv[0])-1; i > 0 && argv[0][i] != '/'; --i);
+	fprintf(stderr, "usage: %s\n", argv[0] + (i > 0) + i);
+	exit(2);
+    }
+
+    WindowManager manager;
+    return 0;
+}
+

          
A => Makefile +111 -0
@@ 0,0 1,111 @@ 
+
+LIBS	= -lXext -lX11 -lXmu -lm
+CC	= ncc
+CCC	= NCC
+CFLAGS	= -O2
+OBJECTS	= Border.o Buttons.o Client.o Events.o Main.o Manager.o Rotated.o listimpl.o
+
+.c.o:
+	$(CC) -c $(CFLAGS) $<
+
+.C.o:
+	$(CCC) -c $(CFLAGS) $<
+
+wm2:	$(OBJECTS)
+	$(CCC) -o wm2 $(OBJECTS) $(LIBS)
+
+depend:
+	makedepend -- $(CFLAGS) -- *.C
+
+clean:
+	rm -f *.o wm2 core
+
+# DO NOT DELETE
+
+Border.o: Border.h General.h /usr/include/unistd.h /usr/include/sys/types.h
+Border.o: /usr/include/sgidefs.h /usr/include/sys/bsd_types.h
+Border.o: /usr/include/sys/select.h /usr/include/sys/time.h
+Border.o: /usr/include/stdio.h /usr/include/signal.h
+Border.o: /usr/include/sys/signal.h /usr/include/sys/siginfo.h
+Border.o: /usr/include/errno.h /usr/include/sys/errno.h /usr/include/stdlib.h
+Border.o: /usr/include/X11/X.h /usr/include/X11/Xlib.h
+Border.o: /usr/include/X11/Xfuncproto.h /usr/include/X11/Xosdefs.h
+Border.o: /usr/include/stddef.h /usr/include/X11/Xos.h /usr/include/string.h
+Border.o: /usr/include/fcntl.h /usr/include/sys/fcntl.h
+Border.o: /usr/include/X11/Xutil.h /usr/include/X11/Xatom.h
+Border.o: /usr/include/X11/extensions/shape.h Config.h Rotated.h Client.h
+Border.o: Manager.h listmacro.h
+Buttons.o: Manager.h General.h /usr/include/unistd.h /usr/include/sys/types.h
+Buttons.o: /usr/include/sgidefs.h /usr/include/sys/bsd_types.h
+Buttons.o: /usr/include/sys/select.h /usr/include/sys/time.h
+Buttons.o: /usr/include/stdio.h /usr/include/signal.h
+Buttons.o: /usr/include/sys/signal.h /usr/include/sys/siginfo.h
+Buttons.o: /usr/include/errno.h /usr/include/sys/errno.h
+Buttons.o: /usr/include/stdlib.h /usr/include/X11/X.h /usr/include/X11/Xlib.h
+Buttons.o: /usr/include/X11/Xfuncproto.h /usr/include/X11/Xosdefs.h
+Buttons.o: /usr/include/stddef.h /usr/include/X11/Xos.h /usr/include/string.h
+Buttons.o: /usr/include/fcntl.h /usr/include/sys/fcntl.h
+Buttons.o: /usr/include/X11/Xutil.h /usr/include/X11/Xatom.h
+Buttons.o: /usr/include/X11/extensions/shape.h Config.h listmacro.h Client.h
+Buttons.o: Border.h Rotated.h
+Client.o: Manager.h General.h /usr/include/unistd.h /usr/include/sys/types.h
+Client.o: /usr/include/sgidefs.h /usr/include/sys/bsd_types.h
+Client.o: /usr/include/sys/select.h /usr/include/sys/time.h
+Client.o: /usr/include/stdio.h /usr/include/signal.h
+Client.o: /usr/include/sys/signal.h /usr/include/sys/siginfo.h
+Client.o: /usr/include/errno.h /usr/include/sys/errno.h /usr/include/stdlib.h
+Client.o: /usr/include/X11/X.h /usr/include/X11/Xlib.h
+Client.o: /usr/include/X11/Xfuncproto.h /usr/include/X11/Xosdefs.h
+Client.o: /usr/include/stddef.h /usr/include/X11/Xos.h /usr/include/string.h
+Client.o: /usr/include/fcntl.h /usr/include/sys/fcntl.h
+Client.o: /usr/include/X11/Xutil.h /usr/include/X11/Xatom.h
+Client.o: /usr/include/X11/extensions/shape.h Config.h listmacro.h Client.h
+Client.o: Border.h Rotated.h
+Events.o: Manager.h General.h /usr/include/unistd.h /usr/include/sys/types.h
+Events.o: /usr/include/sgidefs.h /usr/include/sys/bsd_types.h
+Events.o: /usr/include/sys/select.h /usr/include/sys/time.h
+Events.o: /usr/include/stdio.h /usr/include/signal.h
+Events.o: /usr/include/sys/signal.h /usr/include/sys/siginfo.h
+Events.o: /usr/include/errno.h /usr/include/sys/errno.h /usr/include/stdlib.h
+Events.o: /usr/include/X11/X.h /usr/include/X11/Xlib.h
+Events.o: /usr/include/X11/Xfuncproto.h /usr/include/X11/Xosdefs.h
+Events.o: /usr/include/stddef.h /usr/include/X11/Xos.h /usr/include/string.h
+Events.o: /usr/include/fcntl.h /usr/include/sys/fcntl.h
+Events.o: /usr/include/X11/Xutil.h /usr/include/X11/Xatom.h
+Events.o: /usr/include/X11/extensions/shape.h Config.h listmacro.h Client.h
+Events.o: Border.h Rotated.h
+Main.o: Manager.h General.h /usr/include/unistd.h /usr/include/sys/types.h
+Main.o: /usr/include/sgidefs.h /usr/include/sys/bsd_types.h
+Main.o: /usr/include/sys/select.h /usr/include/sys/time.h
+Main.o: /usr/include/stdio.h /usr/include/signal.h /usr/include/sys/signal.h
+Main.o: /usr/include/sys/siginfo.h /usr/include/errno.h
+Main.o: /usr/include/sys/errno.h /usr/include/stdlib.h /usr/include/X11/X.h
+Main.o: /usr/include/X11/Xlib.h /usr/include/X11/Xfuncproto.h
+Main.o: /usr/include/X11/Xosdefs.h /usr/include/stddef.h
+Main.o: /usr/include/X11/Xos.h /usr/include/string.h /usr/include/fcntl.h
+Main.o: /usr/include/sys/fcntl.h /usr/include/X11/Xutil.h
+Main.o: /usr/include/X11/Xatom.h /usr/include/X11/extensions/shape.h Config.h
+Main.o: listmacro.h
+Manager.o: Manager.h General.h /usr/include/unistd.h /usr/include/sys/types.h
+Manager.o: /usr/include/sgidefs.h /usr/include/sys/bsd_types.h
+Manager.o: /usr/include/sys/select.h /usr/include/sys/time.h
+Manager.o: /usr/include/stdio.h /usr/include/signal.h
+Manager.o: /usr/include/sys/signal.h /usr/include/sys/siginfo.h
+Manager.o: /usr/include/errno.h /usr/include/sys/errno.h
+Manager.o: /usr/include/stdlib.h /usr/include/X11/X.h /usr/include/X11/Xlib.h
+Manager.o: /usr/include/X11/Xfuncproto.h /usr/include/X11/Xosdefs.h
+Manager.o: /usr/include/stddef.h /usr/include/X11/Xos.h /usr/include/string.h
+Manager.o: /usr/include/fcntl.h /usr/include/sys/fcntl.h
+Manager.o: /usr/include/X11/Xutil.h /usr/include/X11/Xatom.h
+Manager.o: /usr/include/X11/extensions/shape.h Config.h listmacro.h Client.h
+Manager.o: Border.h Rotated.h /usr/include/X11/Xproto.h
+Manager.o: /usr/include/X11/Xmd.h /usr/include/X11/Xprotostr.h
+Manager.o: /usr/include/sys/wait.h Cursors.h
+Rotated.o: /usr/include/X11/Xlib.h /usr/include/sys/types.h
+Rotated.o: /usr/include/sgidefs.h /usr/include/sys/bsd_types.h
+Rotated.o: /usr/include/sys/select.h /usr/include/X11/X.h
+Rotated.o: /usr/include/X11/Xfuncproto.h /usr/include/X11/Xosdefs.h
+Rotated.o: /usr/include/stddef.h /usr/include/X11/Xutil.h
+Rotated.o: /usr/include/stdlib.h /usr/include/string.h /usr/include/stdio.h
+Rotated.o: Rotated.h
+listimpl.o: /usr/include/stdio.h /usr/include/stdlib.h /usr/include/sgidefs.h

          
A => Manager.C +534 -0
@@ 0,0 1,534 @@ 
+
+#include "Manager.h"
+#include "Client.h"
+#include <string.h>
+#include <X11/Xproto.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include "Cursors.h"
+
+Atom    Atoms::wm_state;
+Atom    Atoms::wm_changeState;
+Atom    Atoms::wm_protocols;
+Atom    Atoms::wm_delete;
+Atom    Atoms::wm_takeFocus;
+Atom    Atoms::wm_colormaps;
+Atom    Atoms::wm2_running;
+
+int     WindowManager::m_signalled = False;
+Boolean WindowManager::m_initialising = False;
+Boolean ignoreBadWindowErrors;
+
+const char *const WindowManager::m_menuCreateLabel = "New";
+
+implementPList(ClientList, Client);
+
+
+WindowManager::WindowManager() :
+    m_menuGC(0), m_menuWindow(0), m_menuFont(0)
+{
+    fprintf(stderr, "wm2: Copyright (c) 1996 Chris Cannam\n"
+	    "     Parts derived from 9wm Copyright (c) 1994-96 David Hogan\n"
+	    "     %s\n     Copying and redistribution encouraged.  "
+	    "No warranty\n\n", XV_COPYRIGHT);
+
+    m_display = XOpenDisplay(NULL);
+    if (!m_display) fatal("can't open display");
+
+    m_shell = (char *)getenv("SHELL");
+    if (!m_shell) m_shell = NewString("/bin/sh");
+
+    m_initialising = True;
+    XSetErrorHandler(errorHandler);
+    ignoreBadWindowErrors = False;
+
+    // 9wm does more, I think for nohup
+    signal(SIGTERM, sigHandler);
+    signal(SIGINT,  sigHandler);
+    signal(SIGHUP,  sigHandler);
+
+    m_currentTime = -1;
+    m_activeClient = 0;
+
+    Atoms::wm_state      = XInternAtom(m_display, "WM_STATE",            False);
+    Atoms::wm_changeState= XInternAtom(m_display, "WM_CHANGE_STATE",     False);
+    Atoms::wm_protocols  = XInternAtom(m_display, "WM_PROTOCOLS",        False);
+    Atoms::wm_delete     = XInternAtom(m_display, "WM_DELETE_WINDOW",    False);
+    Atoms::wm_takeFocus  = XInternAtom(m_display, "WM_TAKE_FOCUS",       False);
+    Atoms::wm_colormaps  = XInternAtom(m_display, "WM_COLORMAP_WINDOWS", False);
+    Atoms::wm2_running   = XInternAtom(m_display, "_WM2_RUNNING",        False);
+
+    int dummy;
+    if (!XShapeQueryExtension(m_display, &m_shapeEvent, &dummy))
+	fatal("no shape extension, can't run without it");
+
+    // we only cope with one screen!
+    initialiseScreen();
+
+    XSetSelectionOwner(m_display, Atoms::wm2_running,
+		       m_menuWindow, timestamp(True));
+    XSync(m_display, False);
+    m_initialising = False;
+    m_returnCode = 0;
+
+    clearFocus();
+    scanInitialWindows();
+
+    loop();
+}
+    
+
+WindowManager::~WindowManager()
+{
+    // empty
+}
+
+
+void WindowManager::release()
+{
+    if (m_returnCode != 0) return; // hasty exit
+
+    ClientList normalList, unparentList;
+    Client *c;
+    int i;
+
+    for (i = 0; i < m_clients.count(); ++i) {
+	c = m_clients.item(i);
+//    fprintf(stderr, "client %d is %p\n", i, c);
+
+	if (c->isNormal()) normalList.append(c);
+	else unparentList.append(c);
+    }
+
+    for (i = normalList.count()-1; i >= 0; --i) {
+	unparentList.append(normalList.item(i));
+    }
+
+    m_clients.remove_all();
+    
+    for (i = 0; i < unparentList.count(); ++i) {
+//	fprintf(stderr, "unparenting client %p\n",unparentList.item(i));
+	unparentList.item(i)->unreparent();
+	unparentList.item(i)->release();
+	unparentList.item(i) = 0;
+    }
+
+    XSetInputFocus(m_display, PointerRoot, RevertToPointerRoot,
+		   timestamp(False));
+    installColormap(None);
+
+    XFreeCursor(m_display, m_cursor);
+    XFreeCursor(m_display, m_xCursor);
+    XFreeCursor(m_display, m_vCursor);
+    XFreeCursor(m_display, m_hCursor);
+    XFreeCursor(m_display, m_vhCursor);
+
+    XFreeFont(m_display, m_menuFont);
+    XFreeGC(m_display, m_menuGC);
+
+    XCloseDisplay(m_display);
+}
+
+
+void WindowManager::fatal(const char *message)
+{
+    fprintf(stderr, "wm2: ");
+    perror(message);
+    fprintf(stderr, "\n");
+    exit(1);
+}
+
+
+int WindowManager::errorHandler(Display *d, XErrorEvent *e)
+{
+    if (m_initialising && (e->request_code == X_ChangeWindowAttributes) &&
+	e->error_code == BadAccess) {
+	fprintf(stderr, "wm2: another window manager running?\n");
+	exit(1);
+    }
+
+    // ugh
+    if (ignoreBadWindowErrors == True && e->error_code == BadWindow) return 0;
+
+    char msg[100], number[30], request[100];
+    XGetErrorText(d, e->error_code, msg, 100);
+    sprintf(number, "%d", e->request_code);
+    XGetErrorDatabaseText(d, "XRequest", number, "", request, 100);
+
+    if (request[0] == '\0') sprintf(request, "<request-code-%d>",
+				    e->request_code);
+
+    fprintf(stderr, "wm2: %s (0x%lx): %s\n", request, e->resourceid, msg);
+
+    if (m_initialising) {
+	fprintf(stderr, "wm2: failure during initialisation, abandoning\n");
+	exit(1);
+    }
+
+    return 0;
+}    
+
+
+static Cursor makeCursor(Display *d, Window w,
+			 unsigned char *bits, unsigned char *mask_bits,
+			 int width, int height, int xhot, int yhot,
+			 XColor *fg, XColor *bg)
+{
+    Pixmap pixmap =
+ 	XCreateBitmapFromData(d, w, (const char *)bits, width, height);
+
+    Pixmap mask =
+ 	XCreateBitmapFromData(d, w, (const char *)mask_bits, width, height);
+
+    Cursor cursor = XCreatePixmapCursor(d, pixmap, mask, fg, bg, xhot, yhot);
+    XFreePixmap(d, pixmap);
+    XFreePixmap(d, mask);
+
+    return cursor;
+}
+
+
+void WindowManager::initialiseScreen()
+{
+    int i = 0;
+    m_screenNumber = i;
+
+    m_root = RootWindow(m_display, i);
+    m_defaultColormap = DefaultColormap(m_display, i);
+    m_minimumColormaps = MinCmapsOfScreen(ScreenOfDisplay(m_display, i));
+
+    unsigned long blackPixel = BlackPixel(m_display, i);
+    unsigned long whitePixel = WhitePixel(m_display, i);
+
+    XColor black = { blackPixel, 0, 0, 0, 7, ' ' };
+    XColor white = { whitePixel, 0xffff, 0xffff, 0xffff, 7, ' ' };
+
+    m_cursor = makeCursor
+	(m_display, m_root, cursor_bits, cursor_mask_bits,
+	 cursor_width, cursor_height, cursor_x_hot,
+	 cursor_y_hot, &black, &white);
+
+    m_xCursor = makeCursor
+	(m_display, m_root, ninja_cross_bits, ninja_cross_mask_bits,
+	 ninja_cross_width, ninja_cross_height, ninja_cross_x_hot,
+	 ninja_cross_y_hot, &black, &white);
+
+    m_hCursor = makeCursor
+	(m_display, m_root, cursor_right_bits, cursor_right_mask_bits,
+	 cursor_right_width, cursor_right_height, cursor_right_x_hot,
+	 cursor_right_y_hot, &black, &white);
+
+    m_vCursor = makeCursor
+	(m_display, m_root, cursor_down_bits, cursor_down_mask_bits,
+	 cursor_down_width, cursor_down_height, cursor_down_x_hot,
+	 cursor_down_y_hot, &black, &white);
+
+    m_vhCursor = makeCursor
+	(m_display, m_root, cursor_down_right_bits, cursor_down_right_mask_bits,
+	 cursor_down_right_width, cursor_down_right_height,
+	 cursor_down_right_x_hot, cursor_down_right_y_hot, &black, &white);
+
+    XSetWindowAttributes attr;
+    attr.cursor = m_cursor;
+    attr.event_mask = SubstructureRedirectMask | SubstructureNotifyMask |
+	ColormapChangeMask | ButtonPressMask | ButtonReleaseMask | 
+	PropertyChangeMask;
+    XChangeWindowAttributes(m_display, m_root, CWCursor | CWEventMask, &attr);
+    XSync(m_display, False);
+
+    XColor nearest, ideal;
+
+    if (!XAllocNamedColor(m_display, m_defaultColormap,
+			  CONFIG_MENU_FOREGROUND, &nearest, &ideal)) {
+	fatal("couldn't load menu foreground colour");
+    } else m_menuForegroundPixel = nearest.pixel;
+    
+    if (!XAllocNamedColor(m_display, m_defaultColormap,
+			  CONFIG_MENU_BACKGROUND, &nearest, &ideal)) {
+	fatal("couldn't load menu background colour");
+    } else m_menuBackgroundPixel = nearest.pixel;
+    
+    if (!XAllocNamedColor(m_display, m_defaultColormap,
+			  CONFIG_MENU_BORDERS, &nearest, &ideal)) {
+	fatal("couldn't load menu border colour");
+    } else m_menuBorderPixel = nearest.pixel;
+
+    m_menuWindow = XCreateSimpleWindow
+	(m_display, m_root, 0, 0, 1, 1, 1,
+	 m_menuBorderPixel, m_menuBackgroundPixel);
+
+    if (DoesSaveUnders(ScreenOfDisplay(m_display, m_screenNumber))) {
+	XSetWindowAttributes attr;
+	attr.save_under = True;
+	XChangeWindowAttributes(m_display, m_menuWindow, CWSaveUnder, &attr);
+    }
+
+    XGCValues values;
+    values.background = m_menuBackgroundPixel;
+    values.foreground = m_menuForegroundPixel ^ m_menuBackgroundPixel;
+    values.function = GXxor;
+    values.line_width = 0;
+    values.subwindow_mode = IncludeInferiors;
+
+    m_menuFont = XLoadQueryFont(display(), CONFIG_NICE_MENU_FONT);
+    if (!m_menuFont) m_menuFont = XLoadQueryFont(display(),
+						 CONFIG_NASTY_FONT);
+    if (!m_menuFont) fatal("couldn't load default menu font\n");
+    
+    values.font = m_menuFont->fid;
+    m_menuGC = XCreateGC
+	(display(), root(), GCForeground | GCBackground |
+	 GCFunction | GCLineWidth | GCSubwindowMode | GCFont, &values);
+}
+
+
+void WindowManager::installCursor(RootCursor c)
+{
+    installCursorOnWindow(c, m_root);
+}
+
+
+void WindowManager::installCursorOnWindow(RootCursor c, Window w)
+{
+    XSetWindowAttributes attr;
+
+    switch (c) {
+    case DeleteCursor:    attr.cursor = m_xCursor;  break;
+    case DownCursor:      attr.cursor = m_vCursor;  break;
+    case RightCursor:     attr.cursor = m_hCursor;  break;
+    case DownrightCursor: attr.cursor = m_vhCursor; break;
+    case NormalCursor:    attr.cursor = m_cursor;   break;
+    }
+
+    XChangeWindowAttributes(m_display, w, CWCursor, &attr);
+}
+	
+
+int WindowManager::timestamp(Boolean reset)
+{
+    if (reset) m_currentTime = CurrentTime;
+
+    if (m_currentTime == CurrentTime) {
+
+	XEvent event;
+	XChangeProperty(m_display, m_root, Atoms::wm2_running,
+			Atoms::wm2_running, 8, PropModeAppend,
+			(unsigned char *)"", 0);
+	XMaskEvent(m_display, PropertyChangeMask, &event);
+
+	m_currentTime = event.xproperty.time;
+    }
+
+    return m_currentTime;
+}
+
+void WindowManager::sigHandler()
+{
+    m_signalled = True;
+}
+
+void WindowManager::scanInitialWindows()
+{
+    unsigned int i, n;
+    Window w1, w2, *wins;
+    XWindowAttributes attr;
+
+    XQueryTree(m_display, m_root, &w1, &w2, &wins, &n);
+
+    for (i = 0; i < n; ++i) {
+
+	XGetWindowAttributes(m_display, wins[i], &attr);
+	if (attr.override_redirect || wins[i] == m_menuWindow) continue;
+
+	(void)windowToClient(wins[i], True);
+    }
+
+    XFree((void *)wins);
+}
+
+Client *WindowManager::windowToClient(Window w, Boolean create)
+{
+    if (w == 0) return 0;
+
+    for (int i = m_clients.count()-1; i >= 0; --i) {
+
+	if (m_clients.item(i)->hasWindow(w)) {
+
+// fprintf(stderr, "WindowManager::windowToClient: located %p (\"%s\")\n",
+//	    m_clients.item(i), m_clients.item(i)->label());
+
+	    return m_clients.item(i);
+	}
+    }
+
+    if (!create) return 0;
+    else {
+	Client *newC = new Client(this, w);
+	m_clients.append(newC);
+
+//	fprintf(stderr, "I now have %d clients\n", (int)m_clients.count());
+
+	return newC;
+    }
+}
+
+void WindowManager::installColormap(Colormap cmap)
+{
+    if (cmap == None) {
+	XInstallColormap(m_display, m_defaultColormap);
+    } else {
+	XInstallColormap(m_display, cmap);
+    }
+}
+
+void WindowManager::clearColormapFocus()
+{
+    installColormap(None);
+}
+
+void WindowManager::clearFocus()
+{
+    static Window w = 0;
+    Client *active = activeClient();
+
+    if (!CONFIG_CLICK_TO_FOCUS) return;
+
+    if (active) {
+
+	setActiveClient(0);
+	active->deactivate();
+
+	for (Client *c = active->revertTo(); c; c = c->revertTo()) {
+	    if (c->isNormal()) {
+		c->activate();
+		return;
+	    }
+	}
+
+	clearColormapFocus();
+    }
+
+    if (w == 0) {
+
+	XSetWindowAttributes attr;
+	int mask = CWOverrideRedirect;
+	attr.override_redirect = 1;
+
+	w = XCreateWindow(display(), root(), 0, 0, 1, 1, 0,
+			  CopyFromParent, InputOnly, CopyFromParent,
+			  mask, &attr);
+
+	XMapWindow(display(), w);
+    }
+
+    XSetInputFocus(display(), w, RevertToPointerRoot, timestamp(False));
+}
+
+
+void WindowManager::skipInRevert(Client *c, Client *myRevert)
+{
+    for (int i = 0; i < m_clients.count(); ++i) {
+	if (m_clients.item(i) != c &&
+	    m_clients.item(i)->revertTo() == c) {
+	    m_clients.item(i)->setRevertTo(myRevert);
+	}
+    }
+}
+
+
+void WindowManager::addToHiddenList(Client *c)
+{
+    for (int i = 0; i < m_hiddenClients.count(); ++i) {
+	if (m_hiddenClients.item(i) == c) return;
+    }
+
+    m_hiddenClients.append(c);
+}
+
+
+void WindowManager::removeFromHiddenList(Client *c)
+{
+    for (int i = 0; i < m_hiddenClients.count(); ++i) {
+	if (m_hiddenClients.item(i) == c) {
+	    m_hiddenClients.remove(i);
+	    return;
+	}
+    }
+}
+
+
+Boolean WindowManager::raiseTransients(Client *c)
+{
+    Client *first = 0;
+
+    if (!c->isNormal()) return False;
+
+    for (int i = 0; i < m_clients.count(); ++i) {
+
+	if (m_clients.item(i)->isNormal() &&
+	    m_clients.item(i)->isTransient()) {
+
+	    if (c->hasWindow(m_clients.item(i)->transientFor())) {
+
+		if (!first) first = m_clients.item(i);
+		else m_clients.item(i)->mapRaised();
+	    }
+	}
+    }
+
+    if (first) {
+	first->mapRaised();
+	return True;
+    } else {
+	return False;
+    }
+}
+
+#ifdef sgi
+extern "C" {
+extern int putenv(char *);	/* not POSIX */
+}
+#endif
+
+void WindowManager::spawn()
+{
+    // strange code thieved from 9wm to avoid leaving zombies
+
+    char *displayName = DisplayString(m_display);
+
+    if (fork() == 0) {
+	if (fork() == 0) {
+
+	    close(ConnectionNumber(m_display));
+
+	    // if you don't have putenv, miss out this next
+	    // conditional and its contents
+
+	    if (displayName && (displayName[0] != '\0')) {
+
+		char *pstring = (char *)malloc(strlen(displayName) + 10);
+		sprintf(pstring, "DISPLAY=%s", displayName);
+		putenv(pstring);
+		free(pstring);
+	    }
+
+	    if (CONFIG_EXEC_USING_SHELL) {
+		execl(m_shell, m_shell, "-c", CONFIG_NEW_WINDOW_COMMAND, 0);
+		fprintf(stderr, "wm2: exec %s", m_shell);
+		perror(" failed");
+	    }
+
+	    execlp(CONFIG_NEW_WINDOW_COMMAND, CONFIG_NEW_WINDOW_COMMAND, 0);
+	    fprintf(stderr, "wm2: exec %s", CONFIG_NEW_WINDOW_COMMAND);
+	    perror(" failed");
+
+	    execlp("xterm", "xterm", "-ut", 0);
+	    perror("wm2: exec xterm failed");
+	    exit(1);
+	}
+	exit(0);
+    }
+    wait((int *) 0);
+}
+
+

          
A => Manager.h +124 -0
@@ 0,0 1,124 @@ 
+
+#ifndef _MANAGER_H_
+#define _MANAGER_H_
+
+#include "General.h"
+#include "listmacro.h"
+
+class Client;
+declarePList(ClientList, Client);
+
+
+class WindowManager {
+public:
+    WindowManager();
+    ~WindowManager();
+
+    void clearFocus();
+    void clearColormapFocus();
+
+    void fatal(const char *);
+
+    int timestamp(Boolean reset);
+    Boolean initialising() { return m_initialising; }
+
+    // for call from Client and within:
+
+    Client *windowToClient(Window, Boolean create = False);
+    Client *selectClient(Boolean needRelease, Boolean *shift);
+    Client *activeClient() { return m_activeClient; }
+    Boolean raiseTransients(Client *); // true if raised any
+
+    void setActiveClient(Client *const c) { m_activeClient = c; }
+
+    void addToHiddenList(Client *);
+    void removeFromHiddenList(Client *);
+    void skipInRevert(Client *, Client *);
+
+    Display *display() { return m_display; }
+    Window root() { return m_root; }
+
+    enum RootCursor {
+	NormalCursor, DeleteCursor, DownCursor, RightCursor, DownrightCursor
+    };
+
+    void installCursor(RootCursor);
+    void installCursorOnWindow(RootCursor, Window);
+    void installColormap(Colormap);
+
+    // shouldn't really be public
+    int attemptGrab(Window, Window, int, int);
+    void releaseGrab(XButtonEvent *);
+    void eventExposure(XExposeEvent *);	// for exposures during client grab
+    void showGeometry(int, int);
+    void removeGeometry();
+
+private:
+    int loop();
+    void release();
+
+    Display *m_display;
+    int m_screenNumber;
+
+    Window m_root;
+
+    Colormap m_defaultColormap;
+    int m_minimumColormaps;
+
+    Cursor m_cursor;
+    Cursor m_xCursor;
+    Cursor m_vCursor;
+    Cursor m_hCursor;
+    Cursor m_vhCursor;
+    
+    char *m_terminal;
+    char *m_shell;
+
+    ClientList m_clients;
+    ClientList m_hiddenClients;
+    Client *m_activeClient;
+
+    int m_shapeEvent;
+    int m_currentTime;
+
+    Boolean m_looping;
+    int m_returnCode;
+
+    static Boolean m_initialising;
+    static int errorHandler(Display *, XErrorEvent *);
+    static void sigHandler();
+    static int m_signalled;
+
+    void initialiseScreen();
+    void scanInitialWindows();
+
+    GC m_menuGC;
+    Window m_menuWindow;
+    XFontStruct *m_menuFont;
+    unsigned long m_menuForegroundPixel;
+    unsigned long m_menuBackgroundPixel;
+    unsigned long m_menuBorderPixel;
+    static const char *const m_menuCreateLabel;
+    const char *const menuLabel(int);
+    void menu(XButtonEvent *);
+    void spawn();
+
+    void nextEvent(XEvent *);	// return
+
+    void eventButton(XButtonEvent *);
+    void eventMapRequest(XMapRequestEvent *);
+    void eventConfigureRequest(XConfigureRequestEvent *);
+    void eventUnmap(XUnmapEvent *);
+    void eventCreate(XCreateWindowEvent *);
+    void eventDestroy(XDestroyWindowEvent *);
+    void eventClient(XClientMessageEvent *);
+    void eventColormap(XColormapEvent *);
+    void eventProperty(XPropertyEvent *);
+    void eventEnter(XCrossingEvent *);
+    void eventReparent(XReparentEvent *);
+    void eventFocusIn(XFocusInEvent *);
+    void eventShapeNotify(XShapeEvent *);
+};
+
+#endif
+

          
A => README +132 -0
@@ 0,0 1,132 @@ 
+
+wm2 -- a window manager
+=======================
+
+wm2 is a new window manager for X.  It provides an unusual style of
+window decoration and as little functionality as I feel comfortable
+with in a window manager.  wm2 is not configurable, except by editing
+the source and recompiling the code, and is really intended for people
+who don't particularly want their window manager to be too friendly.
+
+wm2 provides:
+
+  -- Decorative frames for your windows.
+
+  -- The ability to move, resize, hide and restore windows.
+
+  -- No icons.
+
+  -- No configurable root menus, buttons or mouse or keyboard bindings.
+
+  -- No virtual desktop, toolbars or integrated applications.
+
+
+Building wm2
+============
+
+You will need a Unix machine, X libraries and a C++ compiler such as
+gcc.  You will also need a mouse, with a button.  Your X server and
+libraries must be R4 or newer and must support the Shape extension.
+wm2 does NOT support multi-screen displays, because I don't have
+anything to test multi-screen code on.
+
+wm2 makes relatively heavy demands on the performance of your X
+server, because of the use of shaped windows, but it shouldn't place
+too much of a load on other aspects of your system.
+
+Inspect the file Config.h.  If any of the settings (fonts, colours and
+"xterm" application name) in this file are unlikely to work with your
+system, change them.  Ensure that CONFIG_NASTY_FONT is set to
+something which is unlikely not to be found.
+
+Edit the Makefile to suit the requirements of your system, and run
+"make depend" followed by "make".  This should build wm2.  On certain
+systems you may need to edit the source to overcome problems with the
+location of non-POSIX functions such as putenv() and select().
+
+
+Using wm2
+=========
+
+To run wm2, make sure you're not already running a window manager,
+make sure the DISPLAY variable is correctly set, and then execute the
+file "wm2".  There are no command-line options or X resources, and
+there is no start-up file.  If your X server doesn't support the Shape
+extension, wm2 will exit (and will never work on your server); if it
+can't find the required fonts or allocate the required colours, it
+will also exit (but you should be able to fix this by changing the
+definitions in Config.h and recompiling).
+
+Available window manipulations are:
+
+ -- To focus a window: depends on the focus policy you selected
+    in Config.h before compiling.  The default distribution has
+    focus-follows-pointer; if you prefer click-to-focus, edit
+    and rebuild.
+
+ -- To raise a window: click on its tab or frame.  (Depends on
+    your Config.h, though -- you can have raise-on-focus.)
+
+ -- To move a window: make sure it's in focus, then click and drag
+    on its tab.
+
+ -- To hide a window: make sure it's in focus, then click on the
+    button at the top of its tab.
+
+ -- To recover a hidden window: click left button on the root
+    window for the root menu, and choose the window you want.
+
+ -- To start a new xterm: use the first item on root menu ("New").
+
+ -- To delete a window: make sure it's in focus, click on the
+    button on the tab, hold the mouse button for at least a
+    second and a half until the cursor changes to a cross, then
+    release.  (I know, it's not very easy.  On the other hand,
+    things like Windows-95 tend to obscure the fact that most
+    windows already have a perfectly good Close option.)
+
+ -- To resize a window: make sure it's in focus, then click and
+    drag on its bottom-right corner.  For a constrained resize,
+    click and drag on the bottom-left or top-right corner of
+    the enclosing window frame.
+
+All move and resize operations are opaque.  There is no way to
+lower a window (apart from by raising others over it).
+
+
+Credits
+=======
+
+wm2 was written by Chris Cannam, recycling a lot of code and structure
+from "9wm" by David Hogan (see http://www.cs.su.oz.au/~dhog/ ).  9wm
+is written in C, so very little of the code is used verbatim, but the
+intention was to reuse and a lot of the resulting code is
+recognisable.  (Also 9wm's minimalism was rather inspiring.)  I've
+made enough changes to make it very probable that any bugs you find
+will be my fault rather than David's.
+
+wm2 also uses version 2.0 of Alan Richardson's "xvertext"
+font-rotation routines.
+
+The sideways tabs on the window frames were Andy Green's idea.
+
+
+Bugs &c
+=======
+
+If you find a bug, please report it to me (preferably with a fix).  If
+you want a new feature added, you can try asking but I'm unlikely to
+do it (unless it's something really original and very easy to do --
+which seems improbable).  If you've already written the code to add
+the feature, I'd probably quite like to see it but I'm unlikely to
+include it in the main distribution unless I really like it (or it's
+something "essential", like multi-headed display support).
+
+If you want to hack the code into something else for your own
+amusement, please go ahead.  Feel free to modify and redistribute, as
+long as you retain the original copyrights as appropriate.
+
+
+
+Chris Cannam, cannam@zands.demon.co.uk
+October 1996

          
A => Rotated.C +621 -0
@@ 0,0 1,621 @@ 
+/* ********************************************************************** */
+
+/* xvertext, Copyright (c) 1992 Alan Richardson (mppa3@uk.ac.sussex.syma)
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose and without fee is hereby granted, provided
+ * that the above copyright notice appear in all copies and that both the
+ * copyright notice and this permission notice appear in supporting
+ * documentation.  All work developed as a consequence of the use of
+ * this program should duly acknowledge such use. No representations are
+ * made about the suitability of this software for any purpose.  It is
+ * provided "as is" without express or implied warranty.
+ */
+
+/* ********************************************************************** */
+
+
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include "Rotated.h"
+
+
+/* ---------------------------------------------------------------------- */
+
+
+int xv_errno;
+
+static char *my_strdup(char *);
+static char *my_strtok(char *, char *);
+
+
+/* ---------------------------------------------------------------------- */  
+
+
+/* *** Routine to mimic `strdup()' (some machines don't have it) *** */
+
+static char *my_strdup(char *str)
+{
+  char *s;
+
+  if (str == NULL) return NULL;
+
+  s = (char *)malloc((unsigned)(strlen(str)+1));
+  /* this error is highly unlikely ... */
+  if (s == NULL) {
+    fprintf(stderr, "Fatal error: my_strdup(): Couldn't do malloc (gulp!)\n");
+    exit(1); 
+  }
+
+  strcpy(s, str);
+  return s;
+}
+
+
+/* ---------------------------------------------------------------------- */
+
+
+/* *** Routine to replace `strtok' : this one returns a zero
+       length string if it encounters two consecutive delimiters *** */
+
+static char *my_strtok(char *str1, char *str2)
+{
+  char *ret;
+  int i, j, stop;
+  static int start, len;
+  static char *stext;
+
+  /* this error should never occur ... */
+  if (str2 == NULL) {
+    fprintf(stderr,
+	    "Fatal error: my_strtok(): recieved null delimiter string\n");
+    exit(1);
+  }
+
+  /* initialise if str1 not NULL ... */
+  if (str1 != NULL) {
+    start = 0;
+    stext = str1;
+    len = strlen(str1);
+  }
+
+  /* run out of tokens ? ... */
+  if (start >= len) return NULL;
+
+  /* loop through characters ... */
+  for (i = start; i < len; i++) {
+
+    /* loop through delimiters ... */
+    stop = 0;
+    for (j = 0; j < strlen(str2); j++)
+      if (stext[i] == str2[j]) stop = 1;
+ 
+    if (stop) break;
+  }
+
+  stext[i] = '\0';
+  ret = stext + start;
+  start = i+1;
+
+  return ret;
+}
+
+
+/* ---------------------------------------------------------------------- */
+  
+
+/* *** Routine to return version/copyright information *** */
+
+float XRotVersion(char *str, int n)
+{
+  if (str != NULL) strncpy(str, XV_COPYRIGHT, n);
+  return XV_VERSION;
+}
+
+
+/* ---------------------------------------------------------------------- */
+
+
+/* *** Load the rotated version of a given font *** */
+ 
+XRotFontStruct *XRotLoadFont(Display *dpy, char *fontname, float angle)
+{
+  char val;
+  XImage *I1, *I2;
+  Pixmap canvas;
+  Window root;
+  int screen;
+  GC font_gc;
+  char text[3];/*, errstr[300];*/
+  XFontStruct *fontstruct;
+  XRotFontStruct *rotfont;
+  int ichar, i, j, index, boxlen = 60, dir;
+  int vert_w, vert_h, vert_len, bit_w, bit_h, bit_len;
+  int min_char, max_char;
+  unsigned char *vertdata, *bitdata;
+  int ascent, descent, lbearing, rbearing;
+  int on = 1, off = 0;
+
+  /* make angle positive ... */
+  if (angle < 0) do angle += 360; while (angle < 0);
+
+  /* get nearest vertical or horizontal direction ... */
+  dir = (int)((angle+45.)/90.)%4;
+
+  /* useful macros ... */
+  screen = DefaultScreen(dpy);
+  root = DefaultRootWindow(dpy);
+
+  /* create the depth 1 canvas bitmap ... */
+  canvas = XCreatePixmap(dpy, root, boxlen, boxlen, 1);
+ 
+  /* create a GC ... */
+  font_gc = XCreateGC(dpy, canvas, 0, 0);
+  XSetBackground(dpy, font_gc, off);
+
+  /* load the font ... */
+  fontstruct = XLoadQueryFont(dpy, fontname);
+  if (fontstruct == NULL) {
+    xv_errno = XV_NOFONT;
+    return NULL;
+  }
+ 
+  XSetFont(dpy, font_gc, fontstruct->fid);
+
+  /* allocate space for rotated font ... */
+  rotfont = (XRotFontStruct *)malloc((unsigned)sizeof(XRotFontStruct));
+  if (rotfont == NULL) {
+    xv_errno = XV_NOMEM;
+    return NULL;
+  }
+   
+  /* determine which characters are defined in font ... */
+  min_char = fontstruct->min_char_or_byte2; 
+  max_char = fontstruct->max_char_or_byte2;
+ 
+  /* we only want printing characters ... */
+  if (min_char<32)  min_char = 32;
+  if (max_char>126) max_char = 126;
+     
+  /* some overall font data ... */
+  rotfont->name = my_strdup(fontname);
+  rotfont->dir = dir;
+  rotfont->min_char = min_char;
+  rotfont->max_char = max_char;
+  rotfont->max_ascent = fontstruct->max_bounds.ascent;
+  rotfont->max_descent = fontstruct->max_bounds.descent;   
+  rotfont->height = rotfont->max_ascent+rotfont->max_descent;
+
+  /* remember xfontstruct for `normal' text ... */
+  if (dir == 0) rotfont->xfontstruct = fontstruct;
+
+  else {
+    /* font needs rotation ... */
+    /* loop through each character ... */
+    for (ichar = min_char; ichar <= max_char; ichar++) {
+
+      index = ichar-fontstruct->min_char_or_byte2;
+ 
+      /* per char dimensions ... */
+      ascent =   rotfont->per_char[ichar-32].ascent = 
+	fontstruct->per_char[index].ascent;
+      descent =  rotfont->per_char[ichar-32].descent = 
+	fontstruct->per_char[index].descent;
+      lbearing = rotfont->per_char[ichar-32].lbearing = 
+	fontstruct->per_char[index].lbearing;
+      rbearing = rotfont->per_char[ichar-32].rbearing = 
+	fontstruct->per_char[index].rbearing;
+      rotfont->per_char[ichar-32].width = 
+	fontstruct->per_char[index].width;
+
+      /* some space chars have zero body, but a bitmap can't have ... */
+      if (!ascent && !descent)   
+	ascent =   rotfont->per_char[ichar-32].ascent =   1;
+      if (!lbearing && !rbearing) 
+	rbearing = rotfont->per_char[ichar-32].rbearing = 1;
+
+      /* glyph width and height when vertical ... */
+      vert_w = rbearing-lbearing;
+      vert_h = ascent+descent;
+
+      /* width in bytes ... */
+      vert_len = (vert_w-1)/8+1;   
+ 
+      XSetForeground(dpy, font_gc, off);
+      XFillRectangle(dpy, canvas, font_gc, 0, 0, boxlen, boxlen);
+
+      /* draw the character centre top right on canvas ... */
+      sprintf(text, "%c", ichar);
+      XSetForeground(dpy, font_gc, on);
+      XDrawImageString(dpy, canvas, font_gc, boxlen/2 - lbearing,
+		       boxlen/2 - descent, text, 1);
+
+      /* reserve memory for first XImage ... */
+      vertdata = (unsigned char *) malloc((unsigned)(vert_len*vert_h));
+      if (vertdata == NULL) {
+	xv_errno = XV_NOMEM;
+	return NULL;
+      }
+  
+      /* create the XImage ... */
+      I1 = XCreateImage(dpy, DefaultVisual(dpy, screen), 1, XYBitmap,
+			0, (char *)vertdata, vert_w, vert_h, 8, 0);
+
+      if (I1 == NULL) {
+	xv_errno = XV_NOXIMAGE;
+	return NULL;
+      }
+  
+      I1->byte_order = I1->bitmap_bit_order = MSBFirst;
+   
+      /* extract character from canvas ... */
+      XGetSubImage(dpy, canvas, boxlen/2, boxlen/2-vert_h,
+		   vert_w, vert_h, 1, XYPixmap, I1, 0, 0);
+      I1->format = XYBitmap; 
+ 
+      /* width, height of rotated character ... */
+      if (dir == 2) { 
+	bit_w = vert_w;
+	bit_h = vert_h; 
+      } else {
+	bit_w = vert_h;
+	bit_h = vert_w; 
+      }
+
+      /* width in bytes ... */
+      bit_len = (bit_w-1)/8 + 1;
+
+      rotfont->per_char[ichar-32].glyph.bit_w = bit_w;
+      rotfont->per_char[ichar-32].glyph.bit_h = bit_h;
+
+      /* reserve memory for the rotated image ... */
+      bitdata = (unsigned char *)calloc((unsigned)(bit_h*bit_len), 1);
+      if (bitdata == NULL) {
+	xv_errno = XV_NOMEM;
+	return NULL;
+      }
+
+      /* create the image ... */
+      I2 = XCreateImage(dpy, DefaultVisual(dpy, screen), 1, XYBitmap, 0,
+			(char *)bitdata, bit_w, bit_h, 8, 0); 
+ 
+      if (I2 == NULL) {
+	xv_errno = XV_NOXIMAGE;
+	return NULL;
+      }
+
+      I2->byte_order = I2->bitmap_bit_order = MSBFirst;
+ 
+      /* map vertical data to rotated character ... */
+      for (j = 0; j < bit_h; j++) {
+	for (i = 0; i < bit_w; i++) {
+	  /* map bits ... */
+	  if (dir == 1)
+	    val = vertdata[i*vert_len + (vert_w-j-1)/8] &
+	      (128>>((vert_w-j-1)%8));
+   
+	  else if (dir == 2)
+	    val = vertdata[(vert_h-j-1)*vert_len + (vert_w-i-1)/8] &
+	      (128>>((vert_w-i-1)%8));
+                    
+	  else 
+	    val = vertdata[(vert_h-i-1)*vert_len + j/8] & 
+	      (128>>(j%8));
+        
+	  if (val) 
+	    bitdata[j*bit_len + i/8] = bitdata[j*bit_len + i/8] |
+	      (128>>(i%8));
+	}
+      }
+   
+      /* create this character's bitmap ... */
+      rotfont->per_char[ichar-32].glyph.bm = 
+	XCreatePixmap(dpy, root, bit_w, bit_h, 1);
+     
+      /* put the image into the bitmap ... */
+      XPutImage(dpy, rotfont->per_char[ichar-32].glyph.bm, 
+		font_gc, I2, 0, 0, 0, 0, bit_w, bit_h);
+  
+      /* free the image and data ... */
+      XDestroyImage(I1);
+      XDestroyImage(I2);
+      /*      free((char *)bitdata);  -- XDestroyImage does this
+	      free((char *)vertdata);*/
+    }
+
+    XFreeFont(dpy, fontstruct);
+  }
+
+  /* free pixmap and GC ... */
+  XFreePixmap(dpy, canvas);
+  XFreeGC(dpy, font_gc);
+
+  return rotfont;
+}
+
+
+/* ---------------------------------------------------------------------- */
+
+
+/* *** Free the resources associated with a rotated font *** */
+
+void XRotUnloadFont(Display *dpy, XRotFontStruct *rotfont)
+{
+  int ichar;
+
+  if (rotfont->dir == 0) XFreeFont(dpy, rotfont->xfontstruct);
+
+  else
+    /* loop through each character, freeing its pixmap ... */
+    for (ichar = rotfont->min_char-32; ichar <= rotfont->max_char-32; ichar++)
+      XFreePixmap(dpy, rotfont->per_char[ichar].glyph.bm);
+
+  /* rotfont should never be referenced again ... */
+  free((char *)rotfont->name);
+  free((char *)rotfont);
+}
+
+
+/* ---------------------------------------------------------------------- */
+   
+
+/* *** Return the width of a string *** */
+
+int XRotTextWidth(XRotFontStruct *rotfont, char *str, int len)
+{
+  int i, width = 0, ichar;
+
+  if (str == NULL) return 0;
+
+  if (rotfont->dir == 0)
+    width = XTextWidth(rotfont->xfontstruct, str, strlen(str));
+
+  else
+    for (i = 0; i<len; i++) {
+      ichar = str[i]-32;
+  
+      /* make sure it's a printing character ... */
+      if (ichar >= 0 && ichar<95) 
+	width += rotfont->per_char[ichar].width;
+    }
+
+  return width;
+}
+
+
+/* ---------------------------------------------------------------------- */
+
+
+/* *** A front end to XRotPaintString : mimics XDrawString *** */
+
+void XRotDrawString(Display *dpy, XRotFontStruct *rotfont, Drawable drawable,
+		    GC gc, int x, int y, char *str, int len)
+{            
+  static GC my_gc = 0;
+  int i, xp, yp, dir, ichar;
+
+  if (str == NULL || len<1) return;
+
+  dir = rotfont->dir;
+  if (my_gc == 0) my_gc = XCreateGC(dpy, drawable, 0, 0);
+
+  XCopyGC(dpy, gc, GCForeground|GCBackground, my_gc);
+
+  /* a horizontal string is easy ... */
+  if (dir == 0) {
+    XSetFillStyle(dpy, my_gc, FillSolid);
+    XSetFont(dpy, my_gc, rotfont->xfontstruct->fid);
+    XDrawString(dpy, drawable, my_gc, x, y, str, len);
+    return;
+  }
+
+  /* vertical or upside down ... */
+
+  XSetFillStyle(dpy, my_gc, FillStippled);
+
+  /* loop through each character in string ... */
+  for (i = 0; i<len; i++) {
+    ichar = str[i]-32;
+
+    /* make sure it's a printing character ... */
+    if (ichar >= 0 && ichar<95) {
+      /* suitable offset ... */
+      if (dir == 1) {
+	xp = x-rotfont->per_char[ichar].ascent;
+	yp = y-rotfont->per_char[ichar].rbearing; 
+      }
+      else if (dir == 2) {
+	xp = x-rotfont->per_char[ichar].rbearing;
+	yp = y-rotfont->per_char[ichar].descent+1; 
+      }
+      else {
+	xp = x-rotfont->per_char[ichar].descent+1;  
+	yp = y+rotfont->per_char[ichar].lbearing; 
+      }
+                   
+      /* draw the glyph ... */
+      XSetStipple(dpy, my_gc, rotfont->per_char[ichar].glyph.bm);
+    
+      XSetTSOrigin(dpy, my_gc, xp, yp);
+      
+      XFillRectangle(dpy, drawable, my_gc, xp, yp,
+		     rotfont->per_char[ichar].glyph.bit_w,
+		     rotfont->per_char[ichar].glyph.bit_h);
+    
+      /* advance position ... */
+      if (dir == 1)
+	y -= rotfont->per_char[ichar].width;
+      else if (dir == 2)
+	x -= rotfont->per_char[ichar].width;
+      else 
+	y += rotfont->per_char[ichar].width;
+    }
+  }
+}
+
+  
+    
+/* ---------------------------------------------------------------------- */
+
+
+/* *** A front end to XRotPaintAlignedString : uses XRotDrawString *** */
+
+void XRotDrawAlignedString(Display *dpy, XRotFontStruct *rotfont,
+			   Drawable drawable, GC gc, int x, int y,
+			   char *text, int align)
+{  
+  int xp = 0, yp = 0, dir;
+  int i, nl = 1, max_width = 0, this_width;
+  char *str1, *str2 = "\n\0", *str3;
+
+  if (text == NULL) 
+    return;
+  
+  dir = rotfont->dir;
+
+  /* count number of sections in string ... */
+  for (i = 0; i<strlen(text); i++) 
+    if (text[i] == '\n') 
+      nl++;
+
+  /* find width of longest section ... */
+  str1 = my_strdup(text);
+  str3 = my_strtok(str1, str2);
+  max_width = XRotTextWidth(rotfont, str3, strlen(str3));
+
+  do {
+    str3 = my_strtok((char *)NULL, str2);
+    if (str3 != NULL)
+      if (XRotTextWidth(rotfont, str3, strlen(str3))>max_width)
+	max_width = XRotTextWidth(rotfont, str3, strlen(str3));
+  }
+  while (str3 != NULL);
+ 
+  /* calculate vertical starting point according to alignment policy and
+     rotation angle ... */
+  if (dir == 0) {
+    if (align == TLEFT || align == TCENTRE || align == TRIGHT)
+      yp = y+rotfont->max_ascent;
+
+    else if (align == BLEFT || align == BCENTRE || align == BRIGHT)
+      yp = y-(nl-1)*rotfont->height - rotfont->max_descent;
+
+    else 
+      yp = y-(nl-1)/2*rotfont->height + rotfont->max_ascent -
+	rotfont->height/2 - ((nl%2 == 0)?rotfont->height/2:0); 
+  }
+
+  else if (dir == 1) {
+    if (align == TLEFT || align == TCENTRE || align == TRIGHT)
+      xp = x+rotfont->max_ascent;
+
+    else if (align == BLEFT || align == BCENTRE || align == BRIGHT)
+      xp = x-(nl-1)*rotfont->height - rotfont->max_descent;
+
+    else 
+      xp = x-(nl-1)/2*rotfont->height + rotfont->max_ascent -
+	rotfont->height/2 - ((nl%2 == 0)?rotfont->height/2:0); 
+  }
+
+  else if (dir == 2) {
+    if (align == TLEFT || align == TCENTRE || align == TRIGHT)
+      yp = y-rotfont->max_ascent;
+     
+    else if (align == BLEFT || align == BCENTRE || align == BRIGHT)
+      yp = y+(nl-1)*rotfont->height + rotfont->max_descent;
+     
+    else 
+      yp = y+(nl-1)/2*rotfont->height - rotfont->max_ascent +
+	rotfont->height/2 + ((nl%2 == 0)?rotfont->height/2:0); 
+  }
+
+  else {
+    if (align == TLEFT || align == TCENTRE || align == TRIGHT)
+      xp = x-rotfont->max_ascent;
+    
+    else if (align == BLEFT || align == BCENTRE || align == BRIGHT)
+      xp = x+(nl-1)*rotfont->height + rotfont->max_descent;
+  
+    else 
+      xp = x+(nl-1)/2*rotfont->height - rotfont->max_ascent +
+	rotfont->height/2 + ((nl%2 == 0)?rotfont->height/2:0); 
+  }
+
+  free(str1);
+  str1 = my_strdup(text);
+  str3 = my_strtok(str1, str2);
+  
+  /* loop through each section in the string ... */
+  do {
+    /* width of this section ... */
+    this_width = XRotTextWidth(rotfont, str3, strlen(str3));
+
+    /* horizontal alignment ... */
+    if (dir == 0) {
+      if (align == TLEFT || align == MLEFT || align == BLEFT)
+	xp = x;
+  
+      else if (align == TCENTRE || align == MCENTRE || align == BCENTRE)
+	xp = x-this_width/2;
+ 
+      else 
+	xp = x-max_width; 
+    }   
+
+    else if (dir == 1) {
+      if (align == TLEFT || align == MLEFT || align == BLEFT)
+	yp = y;
+
+      else if (align == TCENTRE || align == MCENTRE || align == BCENTRE)
+	yp = y+this_width/2;
+
+      else 
+	yp = y+max_width; 
+    }
+
+    else if (dir == 2) {
+      if (align == TLEFT || align == MLEFT || align == BLEFT)
+	xp = x;
+  
+      else if (align == TCENTRE || align == MCENTRE || align == BCENTRE)
+	xp = x+this_width/2;
+ 
+      else 
+	xp = x+max_width; 
+    }
+
+    else {
+      if (align == TLEFT || align == MLEFT || align == BLEFT)  
+	yp = y;
+     
+      else if (align == TCENTRE || align == MCENTRE || align == BCENTRE)
+	yp = y-this_width/2;
+     
+      else 
+	yp = y-max_width; 
+    }
+
+    /* draw the section ... */
+    XRotDrawString(dpy, rotfont, drawable, gc, xp, yp,
+		   str3, strlen(str3));
+
+    str3 = my_strtok((char *)NULL, str2);
+
+    /* advance position ... */
+    if (dir == 0)
+      yp += rotfont->height;
+    else if (dir == 1)
+      xp += rotfont->height;
+    else if (dir == 2)
+      yp -= rotfont->height;
+    else 
+      xp -= rotfont->height;
+  }
+  while (str3 != NULL);
+
+  free(str1);
+}
+

          
A => Rotated.h +101 -0
@@ 0,0 1,101 @@ 
+/* ************************************************************************ */
+
+
+/* Header file for the `xvertext' routines.
+
+   Copyright (c) 1992 Alan Richardson (mppa3@uk.ac.sussex.syma) */
+
+
+/* ************************************************************************ */
+
+
+#ifndef _XVERTEXT_INCLUDED_ 
+#define _XVERTEXT_INCLUDED_
+
+
+#define XV_VERSION      2.0
+#define XV_COPYRIGHT    "xvertext routines Copyright (c) 1992 Alan Richardson"
+
+
+/* ---------------------------------------------------------------------- */
+
+
+/* *** The font structures *** */
+
+struct BitmapStruct {
+    int			 bit_w;
+    int			 bit_h;
+
+    Pixmap bm;
+};
+
+struct XRotCharStruct {
+    int			 ascent;
+    int			 descent;
+    int			 lbearing;
+    int			 rbearing;
+    int			 width;
+
+    BitmapStruct	 glyph;
+};
+
+struct XRotFontStruct {
+    int			 dir;
+    int			 height;
+    int			 max_ascent;
+    int			 max_descent;
+    int			 max_char;
+    int			 min_char;
+    char 		*name;
+
+    XFontStruct		*xfontstruct;
+
+    XRotCharStruct	 per_char[95];
+};
+
+
+/* ---------------------------------------------------------------------- */
+
+
+extern float XRotVersion(char *, int);
+extern XRotFontStruct *XRotLoadFont(Display *, char *, float);
+extern void XRotUnloadFont(Display *, XRotFontStruct *);
+extern int XRotTextWidth(XRotFontStruct *, char *, int);
+extern void XRotDrawString(Display *, XRotFontStruct *, Drawable, GC,
+			   int, int, char *, int);
+extern void XRotDrawAlignedString(Display *, XRotFontStruct *, Drawable, GC,
+				  int, int, char *, int);
+
+
+/* ---------------------------------------------------------------------- */
+
+
+#define TLEFT	 	 1
+#define TCENTRE	 	 2
+#define TRIGHT	 	 3
+#define MLEFT	 	 4
+#define MCENTRE	 	 5
+#define MRIGHT	 	 6
+#define BLEFT	 	 7
+#define BCENTRE	 	 8
+#define BRIGHT	 	 9
+
+
+/* ---------------------------------------------------------------------- */
+
+
+extern int		 xv_errno;
+
+#define XV_NOFONT	 1  /* no such font on X server */
+#define XV_NOMEM	 2  /* couldn't do malloc */
+#define XV_NOXIMAGE	 3  /* couldn't create an XImage */
+
+
+/* ---------------------------------------------------------------------- */
+ 
+
+#else
+
+extern int		 xv_errno;
+
+#endif 

          
A => listimpl.C +23 -0
@@ 0,0 1,23 @@ 
+
+#include <stdio.h>
+#include <stdlib.h>
+
+static long ListImpl_best_new_sizes[] = {
+    48, 112, 240, 496, 1008, 2032, 4080, 8176,
+    16368, 32752, 65520, 131056, 262128, 524272, 1048560,
+    2097136, 4194288, 8388592, 16777200, 33554416, 67108848
+};
+
+long ListImpl_best_new_count(long count, unsigned int size) {
+    for (int i = 0; i < sizeof(ListImpl_best_new_sizes)/sizeof(int); ++i) {
+        if (count * size < ListImpl_best_new_sizes[i]) {
+            return ListImpl_best_new_sizes[i] / size;
+        }
+    }
+    return count;
+}
+
+void ListImpl_range_error(long i) {
+    fprintf(stderr, "internal error: list index %ld out of range\n", i);
+    abort();
+}

          
A => listmacro.h +168 -0
@@ 0,0 1,168 @@ 
+
+#ifndef _LISTMACRO_H_
+#define _LISTMACRO_H_
+
+#define delete_list_items() delete [] items_
+
+extern void ListImpl_range_error(long index);
+extern long ListImpl_best_new_count(long count, unsigned size);
+
+
+#define declarePList(List,T)\
+\
+  typedef T *T##_pointer;\
+\
+  declareList(List,T##_pointer);
+
+#define implementPList(List,T)\
+\
+  typedef T *T##_pointer;\
+\
+  implementList(List,T##_pointer);
+
+
+#define declareList(List,T)\
+class List { \
+public: \
+    List(long size = 0); \
+    List(const List &); \
+    ~List(); \
+\
+    long count() const; \
+    T& item(long index) const; \
+    T* array(long index, long count); \
+\
+    void prepend(const T&); \
+    void append(const T&); \
+    void insert(long index,const T&); \
+    void remove(long index); \
+    void remove_all(); \
+    List &operator=(const List &); \
+private: \
+    T* items_; \
+    long size_; \
+    long count_; \
+    long free_; \
+}; \
+\
+inline long List::count() const { return count_; } \
+\
+inline T& List::item(long index) const { \
+    if (index < 0 || index >= count_) { \
+	ListImpl_range_error(index); \
+    } \
+    long i = index < free_ ? index : index + size_ - count_; \
+    return items_[i]; \
+} \
+\
+inline void List::append(const T& item) { insert(count_, item); } \
+inline void List::prepend(const T& item) { insert(0, item); }
+
+
+#define implementList(List,T) \
+List::List(long size) { \
+    if (size > 0) { \
+        size_ = ListImpl_best_new_count(size, sizeof(T)); \
+        items_ = new T[size_]; \
+    } else { \
+        size_ = 0; \
+        items_ = 0; \
+    } \
+    count_ = 0; \
+    free_ = 0; \
+} \
+\
+List::List(const List& list) : items_(0), size_(0), count_(0), free_(0) { \
+    for (long i = 0; i < list.count(); ++i) { \
+	append(list.item(i)); \
+    } \
+} \
+\
+List::~List() { \
+    delete_list_items(); \
+} \
+\
+T* List::array(long index, long count) { \
+    if (index + count <= free_) { \
+        return items_ + index; \
+    } else if (index >= free_) { \
+        return items_ + index + size_ - count_; \
+    } else { \
+        long i; \
+        for (i = 0; i < index + count - free_; ++i) { \
+            items_[free_ + i] = items_[free_ + size_ - count_ + i]; \
+        } \
+        free_ = index + count; \
+    } \
+    return items_ + index; \
+} \
+\
+void List::insert(long index, const T& item) { \
+    if (count_ == size_) { \
+        long size = ListImpl_best_new_count(size_ + 1, sizeof(T)); \
+        T* items = new T[size]; \
+        if (items_ != 0) { \
+            register long i; \
+            for (i = 0; i < free_; ++i) { \
+                items[i] = items_[i]; \
+            } \
+            for (i = 0; i < count_ - free_; ++i) { \
+                items[free_ + size - count_ + i] = \
+                    items_[free_ + size_ - count_ + i]; \
+            } \
+            delete_list_items(); \
+        } \
+        items_ = items; \
+        size_ = size; \
+    } \
+    if (index >= 0 && index <= count_) { \
+	if (index < free_) { \
+            for (register long i = free_ - index - 1; i >= 0; --i) { \
+                items_[index + size_ - count_ + i] = items_[index + i]; \
+            } \
+        } else if (index > free_) { \
+            for (register long i = 0; i < index - free_; ++i) { \
+                items_[free_ + i] = items_[free_ + size_ - count_ + i]; \
+            } \
+        } \
+        free_ = index + 1; \
+        count_ += 1; \
+        items_[index] = (T)item; \
+    } \
+} \
+\
+void List::remove(long index) { \
+    if (index >= 0 && index <= count_) { \
+        if (index < free_) { \
+            for (register long i = free_ - index - 2; i >= 0; --i) { \
+                items_[size_ - count_ + index + 1 + i] = \
+		    items_[index + 1 + i]; \
+            } \
+        } else if (index > free_) { \
+            for (register long i = 0; i < index - free_; ++i) { \
+                items_[free_ + i] = items_[free_ + size_ - count_ + i]; \
+            } \
+        } \
+        free_ = index; \
+        count_ -= 1; \
+    } \
+} \
+\
+void List::remove_all() { \
+    count_ = 0; \
+    free_ = 0; \
+} \
+\
+List & List::operator=(const List& list) { \
+    if (this != &list) { \
+	remove_all(); \
+	for (long i = 0; i < list.count(); ++i) { \
+	    append(list.item(i)); \
+	} \
+    } \
+    return *this; \
+}
+
+
+#endif
+