xenocara/app/wm2/Manager.C

566 lines
14 KiB
C++
Raw Normal View History

#include "Manager.h"
#include "Client.h"
#include <string.h>
#include <X11/Xproto.h>
#include <sys/types.h>
#include <sys/wait.h>
#include "Cursors.h"
Atom Atoms::wm_state;
Atom Atoms::wm_changeState;
Atom Atoms::wm_protocols;
Atom Atoms::wm_delete;
Atom Atoms::wm_takeFocus;
Atom Atoms::wm_colormaps;
Atom Atoms::wm2_running;
int WindowManager::m_signalled = False;
Boolean WindowManager::m_initialising = False;
Boolean ignoreBadWindowErrors;
const char *const WindowManager::m_menuCreateLabel = "New";
implementPList(ClientList, Client);
WindowManager::WindowManager() :
m_menuGC(0), m_menuWindow(0), m_menuFont(0), 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),
"<request-code-%d>", e->request_code);
fprintf(stderr, "wm2: %s (0x%lx): %s\n", request, e->resourceid, msg);
if (m_initialising) {
fprintf(stderr, "wm2: failure during initialisation, abandoning\n");
exit(1);
}
return 0;
}
static Cursor makeCursor(Display *d, Window w,
unsigned char *bits, unsigned char *mask_bits,
int width, int height, int xhot, int yhot,
XColor *fg, XColor *bg)
{
Pixmap pixmap =
XCreateBitmapFromData(d, w, (const char *)bits, width, height);
Pixmap mask =
XCreateBitmapFromData(d, w, (const char *)mask_bits, width, height);
Cursor cursor = XCreatePixmapCursor(d, pixmap, mask, fg, bg, xhot, yhot);
XFreePixmap(d, pixmap);
XFreePixmap(d, mask);
return cursor;
}
void WindowManager::initialiseScreen()
{
int i = 0;
m_screenNumber = i;
m_root = RootWindow(m_display, i);
m_defaultColormap = DefaultColormap(m_display, i);
m_minimumColormaps = MinCmapsOfScreen(ScreenOfDisplay(m_display, i));
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);
}