1 
2 #include "Manager.h"
3 #include "Client.h"
4 #include <string.h>
5 #include <X11/Xproto.h>
6 #include <sys/types.h>
7 #include <sys/wait.h>
8 #include "Cursors.h"
9 
10 Atom    Atoms::wm_state;
11 Atom    Atoms::wm_changeState;
12 Atom    Atoms::wm_protocols;
13 Atom    Atoms::wm_delete;
14 Atom    Atoms::wm_takeFocus;
15 Atom    Atoms::wm_colormaps;
16 Atom    Atoms::wm2_running;
17 
18 int     WindowManager::m_signalled = False;
19 Boolean WindowManager::m_initialising = False;
20 Boolean ignoreBadWindowErrors;
21 
22 const char *const WindowManager::m_menuCreateLabel = "New";
23 
24 implementPList(ClientList, Client);
25 
26 
WindowManager()27 WindowManager::WindowManager() :
28     m_menuGC(0), m_menuWindow(0), m_menuFont(0), m_focusChanging(False)
29 {
30     fprintf(stderr, "\nwm2: Copyright (c) 1996-7 Chris Cannam."
31 	    "  Fourth release, March 1997\n"
32 	    "     Parts derived from 9wm Copyright (c) 1994-96 David Hogan\n"
33 	    "     %s\n     Copying and redistribution encouraged.  "
34 	    "No warranty.\n\n", XV_COPYRIGHT);
35 
36     if (CONFIG_AUTO_RAISE) {
37 	if (CONFIG_CLICK_TO_FOCUS) {
38 	    fatal("can't have auto-raise-with-delay with click-to-focus");
39 	} else if (CONFIG_RAISE_ON_FOCUS) {
40 	    fatal("can't have raise-on-focus AND auto-raise-with-delay");
41 	} else {
42 	    fprintf(stderr, "     Focus follows, auto-raise with delay.  ");
43 	}
44 
45     } else {
46 	if (CONFIG_CLICK_TO_FOCUS) {
47 	    if (CONFIG_RAISE_ON_FOCUS) {
48 		fprintf(stderr, "     Click to focus.  ");
49 	    } else {
50 		fatal("can't have click-to-focus without raise-on-focus");
51 	    }
52 	} else {
53 	    if (CONFIG_RAISE_ON_FOCUS) {
54 		fprintf(stderr, "     Focus follows, auto-raise.  ");
55 	    } else {
56 		fprintf(stderr, "     Focus follows pointer.  ");
57 	    }
58 	}
59     }
60 
61     if (CONFIG_EVERYTHING_ON_ROOT_MENU) {
62 	fprintf(stderr, "All clients on menu.\n");
63     } else {
64 	fprintf(stderr, "Hidden clients only on menu.\n");
65     }
66 
67     if (CONFIG_PROD_SHAPE) {
68 	fprintf(stderr, "     Shape prodding on.  ");
69     } else {
70 	fprintf(stderr, "     Shape prodding off.  ");
71     }
72 
73     fprintf(stderr, "\n     (To reconfigure, simply edit and recompile.)\n\n");
74 
75     m_display = XOpenDisplay(NULL);
76     if (!m_display) fatal("can't open display");
77 
78     m_shell = (char *)getenv("SHELL");
79     if (!m_shell) m_shell = NewString("/bin/sh");
80 
81     m_initialising = True;
82     XSetErrorHandler(errorHandler);
83     ignoreBadWindowErrors = False;
84 
85     // 9wm does more, I think for nohup
86     signal(SIGTERM, sigHandler);
87     signal(SIGINT,  sigHandler);
88     signal(SIGHUP,  sigHandler);
89 
90     m_currentTime = -1;
91     m_activeClient = 0;
92 
93     Atoms::wm_state      = XInternAtom(m_display, "WM_STATE",            False);
94     Atoms::wm_changeState= XInternAtom(m_display, "WM_CHANGE_STATE",     False);
95     Atoms::wm_protocols  = XInternAtom(m_display, "WM_PROTOCOLS",        False);
96     Atoms::wm_delete     = XInternAtom(m_display, "WM_DELETE_WINDOW",    False);
97     Atoms::wm_takeFocus  = XInternAtom(m_display, "WM_TAKE_FOCUS",       False);
98     Atoms::wm_colormaps  = XInternAtom(m_display, "WM_COLORMAP_WINDOWS", False);
99     Atoms::wm2_running   = XInternAtom(m_display, "_WM2_RUNNING",        False);
100 
101     int dummy;
102     if (!XShapeQueryExtension(m_display, &m_shapeEvent, &dummy))
103 	fatal("no shape extension, can't run without it");
104 
105     // we only cope with one screen!
106     initialiseScreen();
107 
108     XSetSelectionOwner(m_display, Atoms::wm2_running,
109 		       m_menuWindow, timestamp(True));
110     XSync(m_display, False);
111     m_initialising = False;
112     m_returnCode = 0;
113 
114     clearFocus();
115     scanInitialWindows();
116     loop();
117 }
118 
119 
~WindowManager()120 WindowManager::~WindowManager()
121 {
122     // empty
123 }
124 
125 
release()126 void WindowManager::release()
127 {
128     if (m_returnCode != 0) return; // hasty exit
129 
130     ClientList normalList, unparentList;
131     Client *c;
132     int i;
133 
134     for (i = 0; i < m_clients.count(); ++i) {
135 	c = m_clients.item(i);
136 //    fprintf(stderr, "client %d is %p\n", i, c);
137 
138 	if (c->isNormal()) normalList.append(c);
139 	else unparentList.append(c);
140     }
141 
142     for (i = normalList.count()-1; i >= 0; --i) {
143 	unparentList.append(normalList.item(i));
144     }
145 
146     m_clients.remove_all();
147 
148     for (i = 0; i < unparentList.count(); ++i) {
149 //	fprintf(stderr, "unparenting client %p\n",unparentList.item(i));
150 	unparentList.item(i)->unreparent();
151 	unparentList.item(i)->release();
152 	unparentList.item(i) = 0;
153     }
154 
155     XSetInputFocus(m_display, PointerRoot, RevertToPointerRoot,
156 		   timestamp(False));
157     installColormap(None);
158 
159     XFreeCursor(m_display, m_cursor);
160     XFreeCursor(m_display, m_xCursor);
161     XFreeCursor(m_display, m_vCursor);
162     XFreeCursor(m_display, m_hCursor);
163     XFreeCursor(m_display, m_vhCursor);
164 
165     XFreeFont(m_display, m_menuFont);
166     XFreeGC(m_display, m_menuGC);
167 
168     XCloseDisplay(m_display);
169 }
170 
171 
fatal(const char * message)172 void WindowManager::fatal(const char *message)
173 {
174     fprintf(stderr, "wm2: ");
175     perror(message);
176     fprintf(stderr, "\n");
177     exit(1);
178 }
179 
180 
errorHandler(Display * d,XErrorEvent * e)181 int WindowManager::errorHandler(Display *d, XErrorEvent *e)
182 {
183     if (m_initialising && (e->request_code == X_ChangeWindowAttributes) &&
184 	e->error_code == BadAccess) {
185 	fprintf(stderr, "wm2: another window manager running?\n");
186 	exit(1);
187     }
188 
189     // ugh
190     if (ignoreBadWindowErrors == True && e->error_code == BadWindow) return 0;
191 
192     char msg[100], number[30], request[100];
193     XGetErrorText(d, e->error_code, msg, 100);
194     sprintf(number, "%d", e->request_code);
195     XGetErrorDatabaseText(d, "XRequest", number, "", request, 100);
196 
197     if (request[0] == '\0') sprintf(request, "<request-code-%d>",
198 				    e->request_code);
199 
200     fprintf(stderr, "wm2: %s (0x%lx): %s\n", request, e->resourceid, msg);
201 
202     if (m_initialising) {
203 	fprintf(stderr, "wm2: failure during initialisation, abandoning\n");
204 	exit(1);
205     }
206 
207     return 0;
208 }
209 
210 
makeCursor(Display * d,Window w,unsigned char * bits,unsigned char * mask_bits,int width,int height,int xhot,int yhot,XColor * fg,XColor * bg)211 static Cursor makeCursor(Display *d, Window w,
212 			 unsigned char *bits, unsigned char *mask_bits,
213 			 int width, int height, int xhot, int yhot,
214 			 XColor *fg, XColor *bg)
215 {
216     Pixmap pixmap =
217  	XCreateBitmapFromData(d, w, (const char *)bits, width, height);
218 
219     Pixmap mask =
220  	XCreateBitmapFromData(d, w, (const char *)mask_bits, width, height);
221 
222     Cursor cursor = XCreatePixmapCursor(d, pixmap, mask, fg, bg, xhot, yhot);
223     XFreePixmap(d, pixmap);
224     XFreePixmap(d, mask);
225 
226     return cursor;
227 }
228 
229 
initialiseScreen()230 void WindowManager::initialiseScreen()
231 {
232     int i = 0;
233     m_screenNumber = i;
234 
235     m_root = RootWindow(m_display, i);
236     m_defaultColormap = DefaultColormap(m_display, i);
237     m_minimumColormaps = MinCmapsOfScreen(ScreenOfDisplay(m_display, i));
238 
239     XColor black, white, temp;
240 
241     if (!XAllocNamedColor(m_display, m_defaultColormap, "black", &black, &temp))
242 	fatal("couldn't load colour \"black\"!");
243     if (!XAllocNamedColor(m_display, m_defaultColormap, "white", &white, &temp))
244 	fatal("couldn't load colour \"white\"!");
245 
246     m_cursor = makeCursor
247 	(m_display, m_root, cursor_bits, cursor_mask_bits,
248 	 cursor_width, cursor_height, cursor_x_hot,
249 	 cursor_y_hot, &black, &white);
250 
251     m_xCursor = makeCursor
252 	(m_display, m_root, ninja_cross_bits, ninja_cross_mask_bits,
253 	 ninja_cross_width, ninja_cross_height, ninja_cross_x_hot,
254 	 ninja_cross_y_hot, &black, &white);
255 
256     m_hCursor = makeCursor
257 	(m_display, m_root, cursor_right_bits, cursor_right_mask_bits,
258 	 cursor_right_width, cursor_right_height, cursor_right_x_hot,
259 	 cursor_right_y_hot, &black, &white);
260 
261     m_vCursor = makeCursor
262 	(m_display, m_root, cursor_down_bits, cursor_down_mask_bits,
263 	 cursor_down_width, cursor_down_height, cursor_down_x_hot,
264 	 cursor_down_y_hot, &black, &white);
265 
266     m_vhCursor = makeCursor
267 	(m_display, m_root, cursor_down_right_bits, cursor_down_right_mask_bits,
268 	 cursor_down_right_width, cursor_down_right_height,
269 	 cursor_down_right_x_hot, cursor_down_right_y_hot, &black, &white);
270 
271     XSetWindowAttributes attr;
272     attr.cursor = m_cursor;
273     attr.event_mask = SubstructureRedirectMask | SubstructureNotifyMask |
274 	ColormapChangeMask | ButtonPressMask | ButtonReleaseMask |
275 	PropertyChangeMask;
276     XChangeWindowAttributes(m_display, m_root, CWCursor | CWEventMask, &attr);
277     XSync(m_display, False);
278 
279     m_menuForegroundPixel =
280 	allocateColour(CONFIG_MENU_FOREGROUND, "menu foreground");
281     m_menuBackgroundPixel =
282 	allocateColour(CONFIG_MENU_BACKGROUND, "menu background");
283     m_menuBorderPixel =
284 	allocateColour(CONFIG_MENU_BORDERS, "menu border");
285 
286     m_menuWindow = XCreateSimpleWindow
287 	(m_display, m_root, 0, 0, 1, 1, 1,
288 	 m_menuBorderPixel, m_menuBackgroundPixel);
289 
290     if (DoesSaveUnders(ScreenOfDisplay(m_display, m_screenNumber))) {
291 	XSetWindowAttributes attr;
292 	attr.save_under = True;
293 	XChangeWindowAttributes(m_display, m_menuWindow, CWSaveUnder, &attr);
294     }
295 
296     XGCValues values;
297     values.background = m_menuBackgroundPixel;
298     values.foreground = m_menuForegroundPixel ^ m_menuBackgroundPixel;
299     values.function = GXxor;
300     values.line_width = 0;
301     values.subwindow_mode = IncludeInferiors;
302 
303     m_menuFont = XLoadQueryFont(display(), CONFIG_NICE_MENU_FONT);
304     if (!m_menuFont) m_menuFont = XLoadQueryFont(display(),
305 						 CONFIG_NASTY_FONT);
306     if (!m_menuFont) fatal("couldn't load default menu font\n");
307 
308     values.font = m_menuFont->fid;
309     m_menuGC = XCreateGC
310 	(display(), root(), GCForeground | GCBackground |
311 	 GCFunction | GCLineWidth | GCSubwindowMode | GCFont, &values);
312 }
313 
314 
allocateColour(char * name,char * desc)315 unsigned long WindowManager::allocateColour(char *name, char *desc)
316 {
317     XColor nearest, ideal;
318 
319     if (!XAllocNamedColor
320 	(display(), DefaultColormap(display(), m_screenNumber), name,
321 	 &nearest, &ideal)) {
322 
323 	char error[100];
324 	sprintf(error, "couldn't load %s colour", desc);
325 	fatal(error);
326 
327     } else return nearest.pixel;
328 }
329 
330 
installCursor(RootCursor c)331 void WindowManager::installCursor(RootCursor c)
332 {
333     installCursorOnWindow(c, m_root);
334 }
335 
336 
installCursorOnWindow(RootCursor c,Window w)337 void WindowManager::installCursorOnWindow(RootCursor c, Window w)
338 {
339     XSetWindowAttributes attr;
340 
341     switch (c) {
342     case DeleteCursor:    attr.cursor = m_xCursor;  break;
343     case DownCursor:      attr.cursor = m_vCursor;  break;
344     case RightCursor:     attr.cursor = m_hCursor;  break;
345     case DownrightCursor: attr.cursor = m_vhCursor; break;
346     case NormalCursor:    attr.cursor = m_cursor;   break;
347     }
348 
349     XChangeWindowAttributes(m_display, w, CWCursor, &attr);
350 }
351 
352 
timestamp(Boolean reset)353 Time WindowManager::timestamp(Boolean reset)
354 {
355     if (reset) m_currentTime = CurrentTime;
356 
357     if (m_currentTime == CurrentTime) {
358 
359 	XEvent event;
360 	XChangeProperty(m_display, m_root, Atoms::wm2_running,
361 			Atoms::wm2_running, 8, PropModeAppend,
362 			(unsigned char *)"", 0);
363 	XMaskEvent(m_display, PropertyChangeMask, &event);
364 
365 	m_currentTime = event.xproperty.time;
366     }
367 
368     return m_currentTime;
369 }
370 
sigHandler(int)371 void WindowManager::sigHandler(int)
372 {
373     m_signalled = True;
374 }
375 
scanInitialWindows()376 void WindowManager::scanInitialWindows()
377 {
378     unsigned int i, n;
379     Window w1, w2, *wins;
380     XWindowAttributes attr;
381 
382     XQueryTree(m_display, m_root, &w1, &w2, &wins, &n);
383 
384     for (i = 0; i < n; ++i) {
385 
386 	XGetWindowAttributes(m_display, wins[i], &attr);
387 	if (attr.override_redirect || wins[i] == m_menuWindow) continue;
388 
389 	(void)windowToClient(wins[i], True);
390     }
391 
392     XFree((void *)wins);
393 }
394 
windowToClient(Window w,Boolean create)395 Client *WindowManager::windowToClient(Window w, Boolean create)
396 {
397     if (w == 0) return 0;
398 
399     for (int i = m_clients.count()-1; i >= 0; --i) {
400 
401 	if (m_clients.item(i)->hasWindow(w)) {
402 	    return m_clients.item(i);
403 	}
404     }
405 
406     if (!create) return 0;
407     else {
408 	Client *newC = new Client(this, w);
409 	m_clients.append(newC);
410 	return newC;
411     }
412 }
413 
installColormap(Colormap cmap)414 void WindowManager::installColormap(Colormap cmap)
415 {
416     if (cmap == None) {
417 	XInstallColormap(m_display, m_defaultColormap);
418     } else {
419 	XInstallColormap(m_display, cmap);
420     }
421 }
422 
clearFocus()423 void WindowManager::clearFocus()
424 {
425     static Window w = 0;
426     Client *active = activeClient();
427 
428     if (CONFIG_AUTO_RAISE || !CONFIG_CLICK_TO_FOCUS) {
429 	setActiveClient(0);
430 	return;
431     }
432 
433     if (active) {
434 
435 	setActiveClient(0);
436 	active->deactivate();
437 
438 	for (Client *c = active->revertTo(); c; c = c->revertTo()) {
439 	    if (c->isNormal()) {
440 		c->activate();
441 		return;
442 	    }
443 	}
444 
445 	installColormap(None);
446     }
447 
448     if (w == 0) {
449 
450 	XSetWindowAttributes attr;
451 	int mask = CWOverrideRedirect;
452 	attr.override_redirect = 1;
453 
454 	w = XCreateWindow(display(), root(), 0, 0, 1, 1, 0,
455 			  CopyFromParent, InputOnly, CopyFromParent,
456 			  mask, &attr);
457 
458 	XMapWindow(display(), w);
459     }
460 
461     XSetInputFocus(display(), w, RevertToPointerRoot, timestamp(False));
462 }
463 
464 
skipInRevert(Client * c,Client * myRevert)465 void WindowManager::skipInRevert(Client *c, Client *myRevert)
466 {
467     for (int i = 0; i < m_clients.count(); ++i) {
468 	if (m_clients.item(i) != c &&
469 	    m_clients.item(i)->revertTo() == c) {
470 	    m_clients.item(i)->setRevertTo(myRevert);
471 	}
472     }
473 }
474 
475 
addToHiddenList(Client * c)476 void WindowManager::addToHiddenList(Client *c)
477 {
478     for (int i = 0; i < m_hiddenClients.count(); ++i) {
479 	if (m_hiddenClients.item(i) == c) return;
480     }
481 
482     m_hiddenClients.append(c);
483 }
484 
485 
removeFromHiddenList(Client * c)486 void WindowManager::removeFromHiddenList(Client *c)
487 {
488     for (int i = 0; i < m_hiddenClients.count(); ++i) {
489 	if (m_hiddenClients.item(i) == c) {
490 	    m_hiddenClients.remove(i);
491 	    return;
492 	}
493     }
494 }
495 
496 
raiseTransients(Client * c)497 Boolean WindowManager::raiseTransients(Client *c)
498 {
499     Client *first = 0;
500 
501     if (!c->isNormal()) return False;
502 
503     for (int i = 0; i < m_clients.count(); ++i) {
504 
505 	if (m_clients.item(i)->isNormal() &&
506 	    m_clients.item(i)->isTransient()) {
507 
508 	    if (c->hasWindow(m_clients.item(i)->transientFor())) {
509 
510 		if (!first) first = m_clients.item(i);
511 		else m_clients.item(i)->mapRaised();
512 	    }
513 	}
514     }
515 
516     if (first) {
517 	first->mapRaised();
518 	return True;
519     } else {
520 	return False;
521     }
522 }
523 
524 #ifdef sgi
525 extern "C" {
526 extern int putenv(char *);	/* not POSIX */
527 }
528 #endif
529 
spawn()530 void WindowManager::spawn()
531 {
532     // strange code thieved from 9wm to avoid leaving zombies
533 
534     char *displayName = DisplayString(m_display);
535 
536     if (fork() == 0) {
537 	if (fork() == 0) {
538 
539 	    close(ConnectionNumber(m_display));
540 
541 	    // if you don't have putenv, miss out this next
542 	    // conditional and its contents
543 
544 	    if (displayName && (displayName[0] != '\0')) {
545 
546 		char *pstring = (char *)malloc(strlen(displayName) + 10);
547 		sprintf(pstring, "DISPLAY=%s", displayName);
548 		putenv(pstring);
549 	    }
550 
551 	    if (CONFIG_EXEC_USING_SHELL) {
552 		execl(m_shell, m_shell, "-c", CONFIG_NEW_WINDOW_COMMAND, 0);
553 		fprintf(stderr, "wm2: exec %s", m_shell);
554 		perror(" failed");
555 	    }
556 
557 	    execlp(CONFIG_NEW_WINDOW_COMMAND, CONFIG_NEW_WINDOW_COMMAND, 0);
558 	    fprintf(stderr, "wm2: exec %s", CONFIG_NEW_WINDOW_COMMAND);
559 	    perror(" failed");
560 
561 	    execlp("xterm", "xterm", "-ut", 0);
562 	    perror("wm2: exec xterm failed");
563 	    exit(1);
564 	}
565 	exit(0);
566     }
567     wait((int *) 0);
568 }
569 
570 
571