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