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