# HG changeset patch # User cannam # Date 1060692192 0 # Tue Aug 12 12:43:12 2003 +0000 # Branch wm2 # Node ID 6475aaec9445e84ec75281d7c0c9c53d57a0517a # Parent 0000000000000000000000000000000000000000 Importing wm2-1 diff --git a/Border.C b/Border.C new file mode 100644 --- /dev/null +++ b/Border.C @@ -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()); +} + diff --git a/Border.h b/Border.h new file mode 100644 --- /dev/null +++ b/Border.h @@ -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 + diff --git a/Buttons.C b/Buttons.C new file mode 100644 --- /dev/null +++ b/Buttons.C @@ -0,0 +1,681 @@ + +#include "Manager.h" +#include "Client.h" +#include + +#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(); +} + diff --git a/Client.C b/Client.C new file mode 100644 --- /dev/null +++ b/Client.C @@ -0,0 +1,714 @@ + +#include "Manager.h" +#include "Client.h" + +#include + +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); +} + diff --git a/Client.h b/Client.h new file mode 100644 --- /dev/null +++ b/Client.h @@ -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 + diff --git a/Config.h b/Config.h new file mode 100644 --- /dev/null +++ b/Config.h @@ -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 + diff --git a/Cursors.h b/Cursors.h new file mode 100644 --- /dev/null +++ b/Cursors.h @@ -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 diff --git a/Events.C b/Events.C new file mode 100644 --- /dev/null +++ b/Events.C @@ -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 *) { } diff --git a/General.h b/General.h new file mode 100644 --- /dev/null +++ b/General.h @@ -0,0 +1,58 @@ + +#ifndef _GENERAL_H_ +#define _GENERAL_H_ + +#include +#include +#include + +#define _POSIX_SOURCE 1 + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +// 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 diff --git a/Main.C b/Main.C new file mode 100644 --- /dev/null +++ b/Main.C @@ -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; +} + diff --git a/Makefile b/Makefile new file mode 100644 --- /dev/null +++ b/Makefile @@ -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 diff --git a/Manager.C b/Manager.C new file mode 100644 --- /dev/null +++ b/Manager.C @@ -0,0 +1,534 @@ + +#include "Manager.h" +#include "Client.h" +#include +#include +#include +#include +#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, "", + 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); +} + + diff --git a/Manager.h b/Manager.h new file mode 100644 --- /dev/null +++ b/Manager.h @@ -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 + diff --git a/README b/README new file mode 100644 --- /dev/null +++ b/README @@ -0,0 +1,132 @@ + +wm2 -- a window manager +======================= + +wm2 is a new window manager for X. It provides an unusual style of +window decoration and as little functionality as I feel comfortable +with in a window manager. wm2 is not configurable, except by editing +the source and recompiling the code, and is really intended for people +who don't particularly want their window manager to be too friendly. + +wm2 provides: + + -- Decorative frames for your windows. + + -- The ability to move, resize, hide and restore windows. + + -- No icons. + + -- No configurable root menus, buttons or mouse or keyboard bindings. + + -- No virtual desktop, toolbars or integrated applications. + + +Building wm2 +============ + +You will need a Unix machine, X libraries and a C++ compiler such as +gcc. You will also need a mouse, with a button. Your X server and +libraries must be R4 or newer and must support the Shape extension. +wm2 does NOT support multi-screen displays, because I don't have +anything to test multi-screen code on. + +wm2 makes relatively heavy demands on the performance of your X +server, because of the use of shaped windows, but it shouldn't place +too much of a load on other aspects of your system. + +Inspect the file Config.h. If any of the settings (fonts, colours and +"xterm" application name) in this file are unlikely to work with your +system, change them. Ensure that CONFIG_NASTY_FONT is set to +something which is unlikely not to be found. + +Edit the Makefile to suit the requirements of your system, and run +"make depend" followed by "make". This should build wm2. On certain +systems you may need to edit the source to overcome problems with the +location of non-POSIX functions such as putenv() and select(). + + +Using wm2 +========= + +To run wm2, make sure you're not already running a window manager, +make sure the DISPLAY variable is correctly set, and then execute the +file "wm2". There are no command-line options or X resources, and +there is no start-up file. If your X server doesn't support the Shape +extension, wm2 will exit (and will never work on your server); if it +can't find the required fonts or allocate the required colours, it +will also exit (but you should be able to fix this by changing the +definitions in Config.h and recompiling). + +Available window manipulations are: + + -- To focus a window: depends on the focus policy you selected + in Config.h before compiling. The default distribution has + focus-follows-pointer; if you prefer click-to-focus, edit + and rebuild. + + -- To raise a window: click on its tab or frame. (Depends on + your Config.h, though -- you can have raise-on-focus.) + + -- To move a window: make sure it's in focus, then click and drag + on its tab. + + -- To hide a window: make sure it's in focus, then click on the + button at the top of its tab. + + -- To recover a hidden window: click left button on the root + window for the root menu, and choose the window you want. + + -- To start a new xterm: use the first item on root menu ("New"). + + -- To delete a window: make sure it's in focus, click on the + button on the tab, hold the mouse button for at least a + second and a half until the cursor changes to a cross, then + release. (I know, it's not very easy. On the other hand, + things like Windows-95 tend to obscure the fact that most + windows already have a perfectly good Close option.) + + -- To resize a window: make sure it's in focus, then click and + drag on its bottom-right corner. For a constrained resize, + click and drag on the bottom-left or top-right corner of + the enclosing window frame. + +All move and resize operations are opaque. There is no way to +lower a window (apart from by raising others over it). + + +Credits +======= + +wm2 was written by Chris Cannam, recycling a lot of code and structure +from "9wm" by David Hogan (see http://www.cs.su.oz.au/~dhog/ ). 9wm +is written in C, so very little of the code is used verbatim, but the +intention was to reuse and a lot of the resulting code is +recognisable. (Also 9wm's minimalism was rather inspiring.) I've +made enough changes to make it very probable that any bugs you find +will be my fault rather than David's. + +wm2 also uses version 2.0 of Alan Richardson's "xvertext" +font-rotation routines. + +The sideways tabs on the window frames were Andy Green's idea. + + +Bugs &c +======= + +If you find a bug, please report it to me (preferably with a fix). If +you want a new feature added, you can try asking but I'm unlikely to +do it (unless it's something really original and very easy to do -- +which seems improbable). If you've already written the code to add +the feature, I'd probably quite like to see it but I'm unlikely to +include it in the main distribution unless I really like it (or it's +something "essential", like multi-headed display support). + +If you want to hack the code into something else for your own +amusement, please go ahead. Feel free to modify and redistribute, as +long as you retain the original copyrights as appropriate. + + + +Chris Cannam, cannam@zands.demon.co.uk +October 1996 diff --git a/Rotated.C b/Rotated.C new file mode 100644 --- /dev/null +++ b/Rotated.C @@ -0,0 +1,621 @@ +/* ********************************************************************** */ + +/* xvertext, Copyright (c) 1992 Alan Richardson (mppa3@uk.ac.sussex.syma) + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose and without fee is hereby granted, provided + * that the above copyright notice appear in all copies and that both the + * copyright notice and this permission notice appear in supporting + * documentation. All work developed as a consequence of the use of + * this program should duly acknowledge such use. No representations are + * made about the suitability of this software for any purpose. It is + * provided "as is" without express or implied warranty. + */ + +/* ********************************************************************** */ + + +#include +#include +#include +#include +#include +#include "Rotated.h" + + +/* ---------------------------------------------------------------------- */ + + +int xv_errno; + +static char *my_strdup(char *); +static char *my_strtok(char *, char *); + + +/* ---------------------------------------------------------------------- */ + + +/* *** Routine to mimic `strdup()' (some machines don't have it) *** */ + +static char *my_strdup(char *str) +{ + char *s; + + if (str == NULL) return NULL; + + s = (char *)malloc((unsigned)(strlen(str)+1)); + /* this error is highly unlikely ... */ + if (s == NULL) { + fprintf(stderr, "Fatal error: my_strdup(): Couldn't do malloc (gulp!)\n"); + exit(1); + } + + strcpy(s, str); + return s; +} + + +/* ---------------------------------------------------------------------- */ + + +/* *** Routine to replace `strtok' : this one returns a zero + length string if it encounters two consecutive delimiters *** */ + +static char *my_strtok(char *str1, char *str2) +{ + char *ret; + int i, j, stop; + static int start, len; + static char *stext; + + /* this error should never occur ... */ + if (str2 == NULL) { + fprintf(stderr, + "Fatal error: my_strtok(): recieved null delimiter string\n"); + exit(1); + } + + /* initialise if str1 not NULL ... */ + if (str1 != NULL) { + start = 0; + stext = str1; + len = strlen(str1); + } + + /* run out of tokens ? ... */ + if (start >= len) return NULL; + + /* loop through characters ... */ + for (i = start; i < len; i++) { + + /* loop through delimiters ... */ + stop = 0; + for (j = 0; j < strlen(str2); j++) + if (stext[i] == str2[j]) stop = 1; + + if (stop) break; + } + + stext[i] = '\0'; + ret = stext + start; + start = i+1; + + return ret; +} + + +/* ---------------------------------------------------------------------- */ + + +/* *** Routine to return version/copyright information *** */ + +float XRotVersion(char *str, int n) +{ + if (str != NULL) strncpy(str, XV_COPYRIGHT, n); + return XV_VERSION; +} + + +/* ---------------------------------------------------------------------- */ + + +/* *** Load the rotated version of a given font *** */ + +XRotFontStruct *XRotLoadFont(Display *dpy, char *fontname, float angle) +{ + char val; + XImage *I1, *I2; + Pixmap canvas; + Window root; + int screen; + GC font_gc; + char text[3];/*, errstr[300];*/ + XFontStruct *fontstruct; + XRotFontStruct *rotfont; + int ichar, i, j, index, boxlen = 60, dir; + int vert_w, vert_h, vert_len, bit_w, bit_h, bit_len; + int min_char, max_char; + unsigned char *vertdata, *bitdata; + int ascent, descent, lbearing, rbearing; + int on = 1, off = 0; + + /* make angle positive ... */ + if (angle < 0) do angle += 360; while (angle < 0); + + /* get nearest vertical or horizontal direction ... */ + dir = (int)((angle+45.)/90.)%4; + + /* useful macros ... */ + screen = DefaultScreen(dpy); + root = DefaultRootWindow(dpy); + + /* create the depth 1 canvas bitmap ... */ + canvas = XCreatePixmap(dpy, root, boxlen, boxlen, 1); + + /* create a GC ... */ + font_gc = XCreateGC(dpy, canvas, 0, 0); + XSetBackground(dpy, font_gc, off); + + /* load the font ... */ + fontstruct = XLoadQueryFont(dpy, fontname); + if (fontstruct == NULL) { + xv_errno = XV_NOFONT; + return NULL; + } + + XSetFont(dpy, font_gc, fontstruct->fid); + + /* allocate space for rotated font ... */ + rotfont = (XRotFontStruct *)malloc((unsigned)sizeof(XRotFontStruct)); + if (rotfont == NULL) { + xv_errno = XV_NOMEM; + return NULL; + } + + /* determine which characters are defined in font ... */ + min_char = fontstruct->min_char_or_byte2; + max_char = fontstruct->max_char_or_byte2; + + /* we only want printing characters ... */ + if (min_char<32) min_char = 32; + if (max_char>126) max_char = 126; + + /* some overall font data ... */ + rotfont->name = my_strdup(fontname); + rotfont->dir = dir; + rotfont->min_char = min_char; + rotfont->max_char = max_char; + rotfont->max_ascent = fontstruct->max_bounds.ascent; + rotfont->max_descent = fontstruct->max_bounds.descent; + rotfont->height = rotfont->max_ascent+rotfont->max_descent; + + /* remember xfontstruct for `normal' text ... */ + if (dir == 0) rotfont->xfontstruct = fontstruct; + + else { + /* font needs rotation ... */ + /* loop through each character ... */ + for (ichar = min_char; ichar <= max_char; ichar++) { + + index = ichar-fontstruct->min_char_or_byte2; + + /* per char dimensions ... */ + ascent = rotfont->per_char[ichar-32].ascent = + fontstruct->per_char[index].ascent; + descent = rotfont->per_char[ichar-32].descent = + fontstruct->per_char[index].descent; + lbearing = rotfont->per_char[ichar-32].lbearing = + fontstruct->per_char[index].lbearing; + rbearing = rotfont->per_char[ichar-32].rbearing = + fontstruct->per_char[index].rbearing; + rotfont->per_char[ichar-32].width = + fontstruct->per_char[index].width; + + /* some space chars have zero body, but a bitmap can't have ... */ + if (!ascent && !descent) + ascent = rotfont->per_char[ichar-32].ascent = 1; + if (!lbearing && !rbearing) + rbearing = rotfont->per_char[ichar-32].rbearing = 1; + + /* glyph width and height when vertical ... */ + vert_w = rbearing-lbearing; + vert_h = ascent+descent; + + /* width in bytes ... */ + vert_len = (vert_w-1)/8+1; + + XSetForeground(dpy, font_gc, off); + XFillRectangle(dpy, canvas, font_gc, 0, 0, boxlen, boxlen); + + /* draw the character centre top right on canvas ... */ + sprintf(text, "%c", ichar); + XSetForeground(dpy, font_gc, on); + XDrawImageString(dpy, canvas, font_gc, boxlen/2 - lbearing, + boxlen/2 - descent, text, 1); + + /* reserve memory for first XImage ... */ + vertdata = (unsigned char *) malloc((unsigned)(vert_len*vert_h)); + if (vertdata == NULL) { + xv_errno = XV_NOMEM; + return NULL; + } + + /* create the XImage ... */ + I1 = XCreateImage(dpy, DefaultVisual(dpy, screen), 1, XYBitmap, + 0, (char *)vertdata, vert_w, vert_h, 8, 0); + + if (I1 == NULL) { + xv_errno = XV_NOXIMAGE; + return NULL; + } + + I1->byte_order = I1->bitmap_bit_order = MSBFirst; + + /* extract character from canvas ... */ + XGetSubImage(dpy, canvas, boxlen/2, boxlen/2-vert_h, + vert_w, vert_h, 1, XYPixmap, I1, 0, 0); + I1->format = XYBitmap; + + /* width, height of rotated character ... */ + if (dir == 2) { + bit_w = vert_w; + bit_h = vert_h; + } else { + bit_w = vert_h; + bit_h = vert_w; + } + + /* width in bytes ... */ + bit_len = (bit_w-1)/8 + 1; + + rotfont->per_char[ichar-32].glyph.bit_w = bit_w; + rotfont->per_char[ichar-32].glyph.bit_h = bit_h; + + /* reserve memory for the rotated image ... */ + bitdata = (unsigned char *)calloc((unsigned)(bit_h*bit_len), 1); + if (bitdata == NULL) { + xv_errno = XV_NOMEM; + return NULL; + } + + /* create the image ... */ + I2 = XCreateImage(dpy, DefaultVisual(dpy, screen), 1, XYBitmap, 0, + (char *)bitdata, bit_w, bit_h, 8, 0); + + if (I2 == NULL) { + xv_errno = XV_NOXIMAGE; + return NULL; + } + + I2->byte_order = I2->bitmap_bit_order = MSBFirst; + + /* map vertical data to rotated character ... */ + for (j = 0; j < bit_h; j++) { + for (i = 0; i < bit_w; i++) { + /* map bits ... */ + if (dir == 1) + val = vertdata[i*vert_len + (vert_w-j-1)/8] & + (128>>((vert_w-j-1)%8)); + + else if (dir == 2) + val = vertdata[(vert_h-j-1)*vert_len + (vert_w-i-1)/8] & + (128>>((vert_w-i-1)%8)); + + else + val = vertdata[(vert_h-i-1)*vert_len + j/8] & + (128>>(j%8)); + + if (val) + bitdata[j*bit_len + i/8] = bitdata[j*bit_len + i/8] | + (128>>(i%8)); + } + } + + /* create this character's bitmap ... */ + rotfont->per_char[ichar-32].glyph.bm = + XCreatePixmap(dpy, root, bit_w, bit_h, 1); + + /* put the image into the bitmap ... */ + XPutImage(dpy, rotfont->per_char[ichar-32].glyph.bm, + font_gc, I2, 0, 0, 0, 0, bit_w, bit_h); + + /* free the image and data ... */ + XDestroyImage(I1); + XDestroyImage(I2); + /* free((char *)bitdata); -- XDestroyImage does this + free((char *)vertdata);*/ + } + + XFreeFont(dpy, fontstruct); + } + + /* free pixmap and GC ... */ + XFreePixmap(dpy, canvas); + XFreeGC(dpy, font_gc); + + return rotfont; +} + + +/* ---------------------------------------------------------------------- */ + + +/* *** Free the resources associated with a rotated font *** */ + +void XRotUnloadFont(Display *dpy, XRotFontStruct *rotfont) +{ + int ichar; + + if (rotfont->dir == 0) XFreeFont(dpy, rotfont->xfontstruct); + + else + /* loop through each character, freeing its pixmap ... */ + for (ichar = rotfont->min_char-32; ichar <= rotfont->max_char-32; ichar++) + XFreePixmap(dpy, rotfont->per_char[ichar].glyph.bm); + + /* rotfont should never be referenced again ... */ + free((char *)rotfont->name); + free((char *)rotfont); +} + + +/* ---------------------------------------------------------------------- */ + + +/* *** Return the width of a string *** */ + +int XRotTextWidth(XRotFontStruct *rotfont, char *str, int len) +{ + int i, width = 0, ichar; + + if (str == NULL) return 0; + + if (rotfont->dir == 0) + width = XTextWidth(rotfont->xfontstruct, str, strlen(str)); + + else + for (i = 0; i= 0 && ichar<95) + width += rotfont->per_char[ichar].width; + } + + return width; +} + + +/* ---------------------------------------------------------------------- */ + + +/* *** A front end to XRotPaintString : mimics XDrawString *** */ + +void XRotDrawString(Display *dpy, XRotFontStruct *rotfont, Drawable drawable, + GC gc, int x, int y, char *str, int len) +{ + static GC my_gc = 0; + int i, xp, yp, dir, ichar; + + if (str == NULL || len<1) return; + + dir = rotfont->dir; + if (my_gc == 0) my_gc = XCreateGC(dpy, drawable, 0, 0); + + XCopyGC(dpy, gc, GCForeground|GCBackground, my_gc); + + /* a horizontal string is easy ... */ + if (dir == 0) { + XSetFillStyle(dpy, my_gc, FillSolid); + XSetFont(dpy, my_gc, rotfont->xfontstruct->fid); + XDrawString(dpy, drawable, my_gc, x, y, str, len); + return; + } + + /* vertical or upside down ... */ + + XSetFillStyle(dpy, my_gc, FillStippled); + + /* loop through each character in string ... */ + for (i = 0; i= 0 && ichar<95) { + /* suitable offset ... */ + if (dir == 1) { + xp = x-rotfont->per_char[ichar].ascent; + yp = y-rotfont->per_char[ichar].rbearing; + } + else if (dir == 2) { + xp = x-rotfont->per_char[ichar].rbearing; + yp = y-rotfont->per_char[ichar].descent+1; + } + else { + xp = x-rotfont->per_char[ichar].descent+1; + yp = y+rotfont->per_char[ichar].lbearing; + } + + /* draw the glyph ... */ + XSetStipple(dpy, my_gc, rotfont->per_char[ichar].glyph.bm); + + XSetTSOrigin(dpy, my_gc, xp, yp); + + XFillRectangle(dpy, drawable, my_gc, xp, yp, + rotfont->per_char[ichar].glyph.bit_w, + rotfont->per_char[ichar].glyph.bit_h); + + /* advance position ... */ + if (dir == 1) + y -= rotfont->per_char[ichar].width; + else if (dir == 2) + x -= rotfont->per_char[ichar].width; + else + y += rotfont->per_char[ichar].width; + } + } +} + + + +/* ---------------------------------------------------------------------- */ + + +/* *** A front end to XRotPaintAlignedString : uses XRotDrawString *** */ + +void XRotDrawAlignedString(Display *dpy, XRotFontStruct *rotfont, + Drawable drawable, GC gc, int x, int y, + char *text, int align) +{ + int xp = 0, yp = 0, dir; + int i, nl = 1, max_width = 0, this_width; + char *str1, *str2 = "\n\0", *str3; + + if (text == NULL) + return; + + dir = rotfont->dir; + + /* count number of sections in string ... */ + for (i = 0; imax_width) + max_width = XRotTextWidth(rotfont, str3, strlen(str3)); + } + while (str3 != NULL); + + /* calculate vertical starting point according to alignment policy and + rotation angle ... */ + if (dir == 0) { + if (align == TLEFT || align == TCENTRE || align == TRIGHT) + yp = y+rotfont->max_ascent; + + else if (align == BLEFT || align == BCENTRE || align == BRIGHT) + yp = y-(nl-1)*rotfont->height - rotfont->max_descent; + + else + yp = y-(nl-1)/2*rotfont->height + rotfont->max_ascent - + rotfont->height/2 - ((nl%2 == 0)?rotfont->height/2:0); + } + + else if (dir == 1) { + if (align == TLEFT || align == TCENTRE || align == TRIGHT) + xp = x+rotfont->max_ascent; + + else if (align == BLEFT || align == BCENTRE || align == BRIGHT) + xp = x-(nl-1)*rotfont->height - rotfont->max_descent; + + else + xp = x-(nl-1)/2*rotfont->height + rotfont->max_ascent - + rotfont->height/2 - ((nl%2 == 0)?rotfont->height/2:0); + } + + else if (dir == 2) { + if (align == TLEFT || align == TCENTRE || align == TRIGHT) + yp = y-rotfont->max_ascent; + + else if (align == BLEFT || align == BCENTRE || align == BRIGHT) + yp = y+(nl-1)*rotfont->height + rotfont->max_descent; + + else + yp = y+(nl-1)/2*rotfont->height - rotfont->max_ascent + + rotfont->height/2 + ((nl%2 == 0)?rotfont->height/2:0); + } + + else { + if (align == TLEFT || align == TCENTRE || align == TRIGHT) + xp = x-rotfont->max_ascent; + + else if (align == BLEFT || align == BCENTRE || align == BRIGHT) + xp = x+(nl-1)*rotfont->height + rotfont->max_descent; + + else + xp = x+(nl-1)/2*rotfont->height - rotfont->max_ascent + + rotfont->height/2 + ((nl%2 == 0)?rotfont->height/2:0); + } + + free(str1); + str1 = my_strdup(text); + str3 = my_strtok(str1, str2); + + /* loop through each section in the string ... */ + do { + /* width of this section ... */ + this_width = XRotTextWidth(rotfont, str3, strlen(str3)); + + /* horizontal alignment ... */ + if (dir == 0) { + if (align == TLEFT || align == MLEFT || align == BLEFT) + xp = x; + + else if (align == TCENTRE || align == MCENTRE || align == BCENTRE) + xp = x-this_width/2; + + else + xp = x-max_width; + } + + else if (dir == 1) { + if (align == TLEFT || align == MLEFT || align == BLEFT) + yp = y; + + else if (align == TCENTRE || align == MCENTRE || align == BCENTRE) + yp = y+this_width/2; + + else + yp = y+max_width; + } + + else if (dir == 2) { + if (align == TLEFT || align == MLEFT || align == BLEFT) + xp = x; + + else if (align == TCENTRE || align == MCENTRE || align == BCENTRE) + xp = x+this_width/2; + + else + xp = x+max_width; + } + + else { + if (align == TLEFT || align == MLEFT || align == BLEFT) + yp = y; + + else if (align == TCENTRE || align == MCENTRE || align == BCENTRE) + yp = y-this_width/2; + + else + yp = y-max_width; + } + + /* draw the section ... */ + XRotDrawString(dpy, rotfont, drawable, gc, xp, yp, + str3, strlen(str3)); + + str3 = my_strtok((char *)NULL, str2); + + /* advance position ... */ + if (dir == 0) + yp += rotfont->height; + else if (dir == 1) + xp += rotfont->height; + else if (dir == 2) + yp -= rotfont->height; + else + xp -= rotfont->height; + } + while (str3 != NULL); + + free(str1); +} + diff --git a/Rotated.h b/Rotated.h new file mode 100644 --- /dev/null +++ b/Rotated.h @@ -0,0 +1,101 @@ +/* ************************************************************************ */ + + +/* Header file for the `xvertext' routines. + + Copyright (c) 1992 Alan Richardson (mppa3@uk.ac.sussex.syma) */ + + +/* ************************************************************************ */ + + +#ifndef _XVERTEXT_INCLUDED_ +#define _XVERTEXT_INCLUDED_ + + +#define XV_VERSION 2.0 +#define XV_COPYRIGHT "xvertext routines Copyright (c) 1992 Alan Richardson" + + +/* ---------------------------------------------------------------------- */ + + +/* *** The font structures *** */ + +struct BitmapStruct { + int bit_w; + int bit_h; + + Pixmap bm; +}; + +struct XRotCharStruct { + int ascent; + int descent; + int lbearing; + int rbearing; + int width; + + BitmapStruct glyph; +}; + +struct XRotFontStruct { + int dir; + int height; + int max_ascent; + int max_descent; + int max_char; + int min_char; + char *name; + + XFontStruct *xfontstruct; + + XRotCharStruct per_char[95]; +}; + + +/* ---------------------------------------------------------------------- */ + + +extern float XRotVersion(char *, int); +extern XRotFontStruct *XRotLoadFont(Display *, char *, float); +extern void XRotUnloadFont(Display *, XRotFontStruct *); +extern int XRotTextWidth(XRotFontStruct *, char *, int); +extern void XRotDrawString(Display *, XRotFontStruct *, Drawable, GC, + int, int, char *, int); +extern void XRotDrawAlignedString(Display *, XRotFontStruct *, Drawable, GC, + int, int, char *, int); + + +/* ---------------------------------------------------------------------- */ + + +#define TLEFT 1 +#define TCENTRE 2 +#define TRIGHT 3 +#define MLEFT 4 +#define MCENTRE 5 +#define MRIGHT 6 +#define BLEFT 7 +#define BCENTRE 8 +#define BRIGHT 9 + + +/* ---------------------------------------------------------------------- */ + + +extern int xv_errno; + +#define XV_NOFONT 1 /* no such font on X server */ +#define XV_NOMEM 2 /* couldn't do malloc */ +#define XV_NOXIMAGE 3 /* couldn't create an XImage */ + + +/* ---------------------------------------------------------------------- */ + + +#else + +extern int xv_errno; + +#endif diff --git a/listimpl.C b/listimpl.C new file mode 100644 --- /dev/null +++ b/listimpl.C @@ -0,0 +1,23 @@ + +#include +#include + +static long ListImpl_best_new_sizes[] = { + 48, 112, 240, 496, 1008, 2032, 4080, 8176, + 16368, 32752, 65520, 131056, 262128, 524272, 1048560, + 2097136, 4194288, 8388592, 16777200, 33554416, 67108848 +}; + +long ListImpl_best_new_count(long count, unsigned int size) { + for (int i = 0; i < sizeof(ListImpl_best_new_sizes)/sizeof(int); ++i) { + if (count * size < ListImpl_best_new_sizes[i]) { + return ListImpl_best_new_sizes[i] / size; + } + } + return count; +} + +void ListImpl_range_error(long i) { + fprintf(stderr, "internal error: list index %ld out of range\n", i); + abort(); +} diff --git a/listmacro.h b/listmacro.h new file mode 100644 --- /dev/null +++ b/listmacro.h @@ -0,0 +1,168 @@ + +#ifndef _LISTMACRO_H_ +#define _LISTMACRO_H_ + +#define delete_list_items() delete [] items_ + +extern void ListImpl_range_error(long index); +extern long ListImpl_best_new_count(long count, unsigned size); + + +#define declarePList(List,T)\ +\ + typedef T *T##_pointer;\ +\ + declareList(List,T##_pointer); + +#define implementPList(List,T)\ +\ + typedef T *T##_pointer;\ +\ + implementList(List,T##_pointer); + + +#define declareList(List,T)\ +class List { \ +public: \ + List(long size = 0); \ + List(const List &); \ + ~List(); \ +\ + long count() const; \ + T& item(long index) const; \ + T* array(long index, long count); \ +\ + void prepend(const T&); \ + void append(const T&); \ + void insert(long index,const T&); \ + void remove(long index); \ + void remove_all(); \ + List &operator=(const List &); \ +private: \ + T* items_; \ + long size_; \ + long count_; \ + long free_; \ +}; \ +\ +inline long List::count() const { return count_; } \ +\ +inline T& List::item(long index) const { \ + if (index < 0 || index >= count_) { \ + ListImpl_range_error(index); \ + } \ + long i = index < free_ ? index : index + size_ - count_; \ + return items_[i]; \ +} \ +\ +inline void List::append(const T& item) { insert(count_, item); } \ +inline void List::prepend(const T& item) { insert(0, item); } + + +#define implementList(List,T) \ +List::List(long size) { \ + if (size > 0) { \ + size_ = ListImpl_best_new_count(size, sizeof(T)); \ + items_ = new T[size_]; \ + } else { \ + size_ = 0; \ + items_ = 0; \ + } \ + count_ = 0; \ + free_ = 0; \ +} \ +\ +List::List(const List& list) : items_(0), size_(0), count_(0), free_(0) { \ + for (long i = 0; i < list.count(); ++i) { \ + append(list.item(i)); \ + } \ +} \ +\ +List::~List() { \ + delete_list_items(); \ +} \ +\ +T* List::array(long index, long count) { \ + if (index + count <= free_) { \ + return items_ + index; \ + } else if (index >= free_) { \ + return items_ + index + size_ - count_; \ + } else { \ + long i; \ + for (i = 0; i < index + count - free_; ++i) { \ + items_[free_ + i] = items_[free_ + size_ - count_ + i]; \ + } \ + free_ = index + count; \ + } \ + return items_ + index; \ +} \ +\ +void List::insert(long index, const T& item) { \ + if (count_ == size_) { \ + long size = ListImpl_best_new_count(size_ + 1, sizeof(T)); \ + T* items = new T[size]; \ + if (items_ != 0) { \ + register long i; \ + for (i = 0; i < free_; ++i) { \ + items[i] = items_[i]; \ + } \ + for (i = 0; i < count_ - free_; ++i) { \ + items[free_ + size - count_ + i] = \ + items_[free_ + size_ - count_ + i]; \ + } \ + delete_list_items(); \ + } \ + items_ = items; \ + size_ = size; \ + } \ + if (index >= 0 && index <= count_) { \ + if (index < free_) { \ + for (register long i = free_ - index - 1; i >= 0; --i) { \ + items_[index + size_ - count_ + i] = items_[index + i]; \ + } \ + } else if (index > free_) { \ + for (register long i = 0; i < index - free_; ++i) { \ + items_[free_ + i] = items_[free_ + size_ - count_ + i]; \ + } \ + } \ + free_ = index + 1; \ + count_ += 1; \ + items_[index] = (T)item; \ + } \ +} \ +\ +void List::remove(long index) { \ + if (index >= 0 && index <= count_) { \ + if (index < free_) { \ + for (register long i = free_ - index - 2; i >= 0; --i) { \ + items_[size_ - count_ + index + 1 + i] = \ + items_[index + 1 + i]; \ + } \ + } else if (index > free_) { \ + for (register long i = 0; i < index - free_; ++i) { \ + items_[free_ + i] = items_[free_ + size_ - count_ + i]; \ + } \ + } \ + free_ = index; \ + count_ -= 1; \ + } \ +} \ +\ +void List::remove_all() { \ + count_ = 0; \ + free_ = 0; \ +} \ +\ +List & List::operator=(const List& list) { \ + if (this != &list) { \ + remove_all(); \ + for (long i = 0; i < list.count(); ++i) { \ + append(list.item(i)); \ + } \ + } \ + return *this; \ +} + + +#endif +