#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), m_focusChanging(False) { fprintf(stderr, "\nwm2: Copyright (c) 1996-7 Chris Cannam." " Fourth release, March 1997\n" " Parts derived from 9wm Copyright (c) 1994-96 David Hogan\n" " %s\n Copying and redistribution encouraged. " "No warranty.\n\n", XV_COPYRIGHT); if (CONFIG_AUTO_RAISE) { if (CONFIG_CLICK_TO_FOCUS) { fatal("can't have auto-raise-with-delay with click-to-focus"); } else if (CONFIG_RAISE_ON_FOCUS) { fatal("can't have raise-on-focus AND auto-raise-with-delay"); } else { fprintf(stderr, " Focus follows, auto-raise with delay. "); } } else { if (CONFIG_CLICK_TO_FOCUS) { if (CONFIG_RAISE_ON_FOCUS) { fprintf(stderr, " Click to focus. "); } else { fatal("can't have click-to-focus without raise-on-focus"); } } else { if (CONFIG_RAISE_ON_FOCUS) { fprintf(stderr, " Focus follows, auto-raise. "); } else { fprintf(stderr, " Focus follows pointer. "); } } } if (CONFIG_EVERYTHING_ON_ROOT_MENU) { fprintf(stderr, "All clients on menu.\n"); } else { fprintf(stderr, "Hidden clients only on menu.\n"); } if (CONFIG_PROD_SHAPE) { fprintf(stderr, " Shape prodding on. "); } else { fprintf(stderr, " Shape prodding off. "); } fprintf(stderr, "\n (To reconfigure, simply edit and recompile.)\n\n"); 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); snprintf(number, sizeof(number), "%d", e->request_code); XGetErrorDatabaseText(d, "XRequest", number, "", request, 100); if (request[0] == '\0') snprintf(request, sizeof(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)); XColor black, white, temp; if (!XAllocNamedColor(m_display, m_defaultColormap, "black", &black, &temp)) fatal("couldn't load colour \"black\"!"); if (!XAllocNamedColor(m_display, m_defaultColormap, "white", &white, &temp)) fatal("couldn't load colour \"white\"!"); 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); m_menuForegroundPixel = allocateColour(CONFIG_MENU_FOREGROUND, "menu foreground"); m_menuBackgroundPixel = allocateColour(CONFIG_MENU_BACKGROUND, "menu background"); m_menuBorderPixel = allocateColour(CONFIG_MENU_BORDERS, "menu border"); 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); } unsigned long WindowManager::allocateColour(char *name, char *desc) { XColor nearest, ideal; if (!XAllocNamedColor (display(), DefaultColormap(display(), m_screenNumber), name, &nearest, &ideal)) { char error[100]; snprintf(error, sizeof(error), "couldn't load %s colour", desc); fatal(error); } else return nearest.pixel; } 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); } Time 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(int) { 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)) { return m_clients.item(i); } } if (!create) return 0; else { Client *newC = new Client(this, w); m_clients.append(newC); return newC; } } void WindowManager::installColormap(Colormap cmap) { if (cmap == None) { XInstallColormap(m_display, m_defaultColormap); } else { XInstallColormap(m_display, cmap); } } void WindowManager::clearFocus() { static Window w = 0; Client *active = activeClient(); if (CONFIG_AUTO_RAISE || !CONFIG_CLICK_TO_FOCUS) { setActiveClient(0); return; } if (active) { setActiveClient(0); active->deactivate(); for (Client *c = active->revertTo(); c; c = c->revertTo()) { if (c->isNormal()) { c->activate(); return; } } installColormap(None); } 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; } } 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; asprintf(&pstring, "DISPLAY=%s", displayName); putenv(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); }