1 /*
2  *  Window Maker window manager
3  *
4  *  Copyright (c) 1997-2003 Alfredo K. Kojima
5  *  Copyright (c) 1998-2003 Dan Pascu
6  *
7  *  This program is free software; you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License as published by
9  *  the Free Software Foundation; either version 2 of the License, or
10  *  (at your option) any later version.
11  *
12  *  This program is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *  GNU General Public License for more details.
16  *
17  *  You should have received a copy of the GNU General Public License along
18  *  with this program; if not, write to the Free Software Foundation, Inc.,
19  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20  */
21 
22 #include "wconfig.h"
23 
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <unistd.h>
27 #include <string.h>
28 #include <errno.h>
29 #include <signal.h>
30 #include <sys/wait.h>
31 #ifdef __FreeBSD__
32 #include <sys/signal.h>
33 #endif
34 
35 #include <X11/Xlib.h>
36 #include <X11/Xresource.h>
37 #include <X11/Xutil.h>
38 #include <X11/cursorfont.h>
39 #include <X11/Xproto.h>
40 #include <X11/keysym.h>
41 #ifdef USE_XSHAPE
42 #include <X11/extensions/shape.h>
43 #endif
44 #ifdef KEEP_XKB_LOCK_STATUS
45 #include <X11/XKBlib.h>
46 #endif
47 #ifdef USE_RANDR
48 #include <X11/extensions/Xrandr.h>
49 #endif
50 
51 #include "WindowMaker.h"
52 #include "GNUstep.h"
53 #include "texture.h"
54 #include "screen.h"
55 #include "window.h"
56 #include "actions.h"
57 #include "client.h"
58 #include "main.h"
59 #include "startup.h"
60 #include "dock.h"
61 #include "workspace.h"
62 #include "keybind.h"
63 #include "framewin.h"
64 #include "session.h"
65 #include "defaults.h"
66 #include "properties.h"
67 #include "dialog.h"
68 #include "wmspec.h"
69 #include "event.h"
70 #include "switchmenu.h"
71 #ifdef USE_DOCK_XDND
72 #include "xdnd.h"
73 #endif
74 
75 #include "xutil.h"
76 
77 /* for SunOS */
78 #ifndef SA_RESTART
79 # define SA_RESTART 0
80 #endif
81 
82 /* Just in case, for weirdo systems */
83 #ifndef SA_NODEFER
84 # define SA_NODEFER 0
85 #endif
86 
87 /***** Local *****/
88 static WScreen **wScreen = NULL;
89 static unsigned int _NumLockMask = 0;
90 static unsigned int _ScrollLockMask = 0;
91 static void manageAllWindows(WScreen * scr, int crashed);
92 
catchXError(Display * dpy,XErrorEvent * error)93 static int catchXError(Display * dpy, XErrorEvent * error)
94 {
95 	char buffer[MAXLINE];
96 
97 	/* ignore some errors */
98 	if (error->resourceid != None
99 	    && ((error->error_code == BadDrawable && error->request_code == X_GetGeometry)
100 		|| (error->error_code == BadMatch && (error->request_code == X_SetInputFocus))
101 		|| (error->error_code == BadWindow)
102 		/*
103 		   && (error->request_code == X_GetWindowAttributes
104 		   || error->request_code == X_SetInputFocus
105 		   || error->request_code == X_ChangeWindowAttributes
106 		   || error->request_code == X_GetProperty
107 		   || error->request_code == X_ChangeProperty
108 		   || error->request_code == X_QueryTree
109 		   || error->request_code == X_GrabButton
110 		   || error->request_code == X_UngrabButton
111 		   || error->request_code == X_SendEvent
112 		   || error->request_code == X_ConfigureWindow))
113 		 */
114 		|| (error->request_code == X_InstallColormap))) {
115 		return 0;
116 	}
117 	FormatXError(dpy, error, buffer, MAXLINE);
118 	wwarning(_("internal X error: %s"), buffer);
119 	return -1;
120 }
121 
122 /*
123  *----------------------------------------------------------------------
124  * handleXIO-
125  * 	Handle X shutdowns and other stuff.
126  *----------------------------------------------------------------------
127  */
handleXIO(Display * xio_dpy)128 static int handleXIO(Display * xio_dpy)
129 {
130 	/* Parameter not used, but tell the compiler that it is ok */
131 	(void) xio_dpy;
132 
133 	dpy = NULL;
134 	Exit(0);
135 	return 0;
136 }
137 
138 /*
139  *----------------------------------------------------------------------
140  * handleExitSig--
141  * 	User generated exit signal handler.
142  *----------------------------------------------------------------------
143  */
handleExitSig(int sig)144 static RETSIGTYPE handleExitSig(int sig)
145 {
146 	sigset_t sigs;
147 
148 	sigfillset(&sigs);
149 	sigprocmask(SIG_BLOCK, &sigs, NULL);
150 
151 	if (sig == SIGUSR1) {
152 		wwarning("got signal %i - restarting", sig);
153 		SIG_WCHANGE_STATE(WSTATE_NEED_RESTART);
154 	} else if (sig == SIGUSR2) {
155 		wwarning("got signal %i - rereading defaults", sig);
156 		SIG_WCHANGE_STATE(WSTATE_NEED_REREAD);
157 	} else if (sig == SIGTERM || sig == SIGINT || sig == SIGHUP) {
158 		wwarning("got signal %i - exiting...", sig);
159 		SIG_WCHANGE_STATE(WSTATE_NEED_EXIT);
160 	}
161 
162 	sigprocmask(SIG_UNBLOCK, &sigs, NULL);
163 	DispatchEvent(NULL);	/* Dispatch events immediately. */
164 }
165 
166 /* Dummy signal handler */
dummyHandler(int sig)167 static void dummyHandler(int sig)
168 {
169 	/* Parameter is not used, but tell the compiler that it is ok */
170 	(void) sig;
171 }
172 
173 /*
174  *----------------------------------------------------------------------
175  * handleSig--
176  * 	general signal handler. Exits the program gently.
177  *----------------------------------------------------------------------
178  */
handleSig(int sig)179 static RETSIGTYPE handleSig(int sig)
180 {
181 	wfatal("got signal %i", sig);
182 
183 	/* Setting the signal behaviour back to default and then reraising the
184 	 * signal is a cleaner way to make program exit and core dump than calling
185 	 * abort(), since it correctly returns from the signal handler and sets
186 	 * the flags accordingly. -Dan
187 	 */
188 	if (sig == SIGSEGV || sig == SIGFPE || sig == SIGBUS || sig == SIGILL || sig == SIGABRT) {
189 		signal(sig, SIG_DFL);
190 		kill(getpid(), sig);
191 		return;
192 	}
193 
194 	wAbort(0);
195 }
196 
buryChild(int foo)197 static RETSIGTYPE buryChild(int foo)
198 {
199 	pid_t pid;
200 	int status;
201 	int save_errno = errno;
202 	sigset_t sigs;
203 
204 	/* Parameter not used, but tell the compiler that it is ok */
205 	(void) foo;
206 
207 	sigfillset(&sigs);
208 	/* Block signals so that NotifyDeadProcess() doesn't get fux0red */
209 	sigprocmask(SIG_BLOCK, &sigs, NULL);
210 
211 	/* R.I.P. */
212 	/* If 2 or more kids exit in a small time window, before this handler gets
213 	 * the chance to get invoked, the SIGCHLD signals will be merged and only
214 	 * one SIGCHLD signal will be sent to us. We use a while loop to get all
215 	 * exited child status because we can't count on the number of SIGCHLD
216 	 * signals to know exactly how many kids have exited. -Dan
217 	 */
218 	while ((pid = waitpid(-1, &status, WNOHANG)) > 0 || (pid < 0 && errno == EINTR)) {
219 		NotifyDeadProcess(pid, WEXITSTATUS(status));
220 	}
221 
222 	sigprocmask(SIG_UNBLOCK, &sigs, NULL);
223 
224 	errno = save_errno;
225 }
226 
getOffendingModifiers(void)227 static void getOffendingModifiers(void)
228 {
229 	int i;
230 	XModifierKeymap *modmap;
231 	KeyCode nlock, slock;
232 	static int mask_table[8] = {
233 		ShiftMask, LockMask, ControlMask, Mod1Mask,
234 		Mod2Mask, Mod3Mask, Mod4Mask, Mod5Mask
235 	};
236 
237 	nlock = XKeysymToKeycode(dpy, XK_Num_Lock);
238 	slock = XKeysymToKeycode(dpy, XK_Scroll_Lock);
239 
240 	/*
241 	 * Find out the masks for the NumLock and ScrollLock modifiers,
242 	 * so that we can bind the grabs for when they are enabled too.
243 	 */
244 	modmap = XGetModifierMapping(dpy);
245 
246 	if (modmap != NULL && modmap->max_keypermod > 0) {
247 		for (i = 0; i < 8 * modmap->max_keypermod; i++) {
248 			if (modmap->modifiermap[i] == nlock && nlock != 0)
249 				_NumLockMask = mask_table[i / modmap->max_keypermod];
250 			else if (modmap->modifiermap[i] == slock && slock != 0)
251 				_ScrollLockMask = mask_table[i / modmap->max_keypermod];
252 		}
253 	}
254 
255 	if (modmap)
256 		XFreeModifiermap(modmap);
257 }
258 
259 #ifdef NUMLOCK_HACK
260 void
wHackedGrabKey(int keycode,unsigned int modifiers,Window grab_window,Bool owner_events,int pointer_mode,int keyboard_mode)261 wHackedGrabKey(int keycode, unsigned int modifiers,
262 	       Window grab_window, Bool owner_events, int pointer_mode, int keyboard_mode)
263 {
264 	if (modifiers == AnyModifier)
265 		return;
266 
267 	/* grab all combinations of the modifier with CapsLock, NumLock and
268 	 * ScrollLock. How much memory/CPU does such a monstrosity consume
269 	 * in the server?
270 	 */
271 	if (_NumLockMask)
272 		XGrabKey(dpy, keycode, modifiers | _NumLockMask,
273 			 grab_window, owner_events, pointer_mode, keyboard_mode);
274 	if (_ScrollLockMask)
275 		XGrabKey(dpy, keycode, modifiers | _ScrollLockMask,
276 			 grab_window, owner_events, pointer_mode, keyboard_mode);
277 	if (_NumLockMask && _ScrollLockMask)
278 		XGrabKey(dpy, keycode, modifiers | _NumLockMask | _ScrollLockMask,
279 			 grab_window, owner_events, pointer_mode, keyboard_mode);
280 	if (_NumLockMask)
281 		XGrabKey(dpy, keycode, modifiers | _NumLockMask | LockMask,
282 			 grab_window, owner_events, pointer_mode, keyboard_mode);
283 	if (_ScrollLockMask)
284 		XGrabKey(dpy, keycode, modifiers | _ScrollLockMask | LockMask,
285 			 grab_window, owner_events, pointer_mode, keyboard_mode);
286 	if (_NumLockMask && _ScrollLockMask)
287 		XGrabKey(dpy, keycode, modifiers | _NumLockMask | _ScrollLockMask | LockMask,
288 			 grab_window, owner_events, pointer_mode, keyboard_mode);
289 	/* phew, I guess that's all, right? */
290 }
291 #endif
292 
293 void
wHackedGrabButton(unsigned int button,unsigned int modifiers,Window grab_window,Bool owner_events,unsigned int event_mask,int pointer_mode,int keyboard_mode,Window confine_to,Cursor cursor)294 wHackedGrabButton(unsigned int button, unsigned int modifiers,
295 		  Window grab_window, Bool owner_events,
296 		  unsigned int event_mask, int pointer_mode, int keyboard_mode, Window confine_to, Cursor cursor)
297 {
298 	XGrabButton(dpy, button, modifiers, grab_window, owner_events,
299 		    event_mask, pointer_mode, keyboard_mode, confine_to, cursor);
300 
301 	if (modifiers == AnyModifier)
302 		return;
303 
304 	XGrabButton(dpy, button, modifiers | LockMask, grab_window, owner_events,
305 		    event_mask, pointer_mode, keyboard_mode, confine_to, cursor);
306 
307 #ifdef NUMLOCK_HACK
308 	/* same as above, but for mouse buttons */
309 	if (_NumLockMask)
310 		XGrabButton(dpy, button, modifiers | _NumLockMask,
311 			    grab_window, owner_events, event_mask, pointer_mode,
312 			    keyboard_mode, confine_to, cursor);
313 	if (_ScrollLockMask)
314 		XGrabButton(dpy, button, modifiers | _ScrollLockMask,
315 			    grab_window, owner_events, event_mask, pointer_mode,
316 			    keyboard_mode, confine_to, cursor);
317 	if (_NumLockMask && _ScrollLockMask)
318 		XGrabButton(dpy, button, modifiers | _ScrollLockMask | _NumLockMask,
319 			    grab_window, owner_events, event_mask, pointer_mode,
320 			    keyboard_mode, confine_to, cursor);
321 	if (_NumLockMask)
322 		XGrabButton(dpy, button, modifiers | _NumLockMask | LockMask,
323 			    grab_window, owner_events, event_mask, pointer_mode,
324 			    keyboard_mode, confine_to, cursor);
325 	if (_ScrollLockMask)
326 		XGrabButton(dpy, button, modifiers | _ScrollLockMask | LockMask,
327 			    grab_window, owner_events, event_mask, pointer_mode,
328 			    keyboard_mode, confine_to, cursor);
329 	if (_NumLockMask && _ScrollLockMask)
330 		XGrabButton(dpy, button, modifiers | _ScrollLockMask | _NumLockMask | LockMask,
331 			    grab_window, owner_events, event_mask, pointer_mode,
332 			    keyboard_mode, confine_to, cursor);
333 #endif				/* NUMLOCK_HACK */
334 }
335 
wScreenWithNumber(int i)336 WScreen *wScreenWithNumber(int i)
337 {
338 	assert(i < w_global.screen_count);
339 
340 	return wScreen[i];
341 }
342 
wScreenForRootWindow(Window window)343 WScreen *wScreenForRootWindow(Window window)
344 {
345 	int i;
346 
347 	if (w_global.screen_count == 1)
348 		return wScreen[0];
349 
350 	/* Since the number of heads will probably be small (normally 2),
351 	 * it should be faster to use this than a hash table, because
352 	 * of the overhead. */
353 	for (i = 0; i < w_global.screen_count; i++)
354 		if (wScreen[i]->root_win == window)
355 			return wScreen[i];
356 
357 	return wScreenForWindow(window);
358 }
359 
wScreenForWindow(Window window)360 WScreen *wScreenForWindow(Window window)
361 {
362 	XWindowAttributes attr;
363 
364 	if (w_global.screen_count == 1)
365 		return wScreen[0];
366 
367 	if (XGetWindowAttributes(dpy, window, &attr))
368 		return wScreenForRootWindow(attr.root);
369 
370 	return NULL;
371 }
372 
373 static char *atomNames[] = {
374 	"WM_STATE",
375 	"WM_CHANGE_STATE",
376 	"WM_PROTOCOLS",
377 	"WM_TAKE_FOCUS",
378 	"WM_DELETE_WINDOW",
379 	"WM_SAVE_YOURSELF",
380 	"WM_CLIENT_LEADER",
381 	"WM_COLORMAP_WINDOWS",
382 	"WM_COLORMAP_NOTIFY",
383 
384 	"_WINDOWMAKER_MENU",
385 	"_WINDOWMAKER_STATE",
386 	"_WINDOWMAKER_WM_PROTOCOLS",
387 	"_WINDOWMAKER_WM_FUNCTION",
388 	"_WINDOWMAKER_NOTICEBOARD",
389 	"_WINDOWMAKER_COMMAND",
390 	"_WINDOWMAKER_ICON_SIZE",
391 	"_WINDOWMAKER_ICON_TILE",
392 
393 	GNUSTEP_WM_ATTR_NAME,
394 	GNUSTEP_WM_MINIATURIZE_WINDOW,
395 	GNUSTEP_TITLEBAR_STATE,
396 
397 	"_GTK_APPLICATION_OBJECT_PATH",
398 
399 	"WM_IGNORE_FOCUS_EVENTS"
400 };
401 
402 /*
403  *----------------------------------------------------------
404  * StartUp--
405  * 	starts the window manager and setup global data.
406  * Called from main() at startup.
407  *
408  * Side effects:
409  * global data declared in main.c is initialized
410  *----------------------------------------------------------
411  */
StartUp(Bool defaultScreenOnly)412 void StartUp(Bool defaultScreenOnly)
413 {
414 	struct sigaction sig_action;
415 	int i, j, max;
416 	char **formats;
417 	Atom atom[wlengthof(atomNames)];
418 
419 	/*
420 	 * Ignore CapsLock in modifiers
421 	 */
422 	w_global.shortcut.modifiers_mask = 0xff & ~LockMask;
423 
424 	getOffendingModifiers();
425 	/*
426 	 * Ignore NumLock and ScrollLock too
427 	 */
428 	w_global.shortcut.modifiers_mask &= ~(_NumLockMask | _ScrollLockMask);
429 
430 	memset(&wKeyBindings, 0, sizeof(wKeyBindings));
431 
432 	w_global.context.client_win = XUniqueContext();
433 	w_global.context.app_win = XUniqueContext();
434 	w_global.context.stack = XUniqueContext();
435 
436 	/*    _XA_VERSION = XInternAtom(dpy, "VERSION", False); */
437 
438 #ifdef HAVE_XINTERNATOMS
439 	XInternAtoms(dpy, atomNames, wlengthof(atomNames), False, atom);
440 #else
441 
442 	{
443 		int i;
444 		for (i = 0; i < wlengthof(atomNames); i++)
445 			atom[i] = XInternAtom(dpy, atomNames[i], False);
446 	}
447 #endif
448 
449 	w_global.atom.wm.state = atom[0];
450 	w_global.atom.wm.change_state = atom[1];
451 	w_global.atom.wm.protocols = atom[2];
452 	w_global.atom.wm.take_focus = atom[3];
453 	w_global.atom.wm.delete_window = atom[4];
454 	w_global.atom.wm.save_yourself = atom[5];
455 	w_global.atom.wm.client_leader = atom[6];
456 	w_global.atom.wm.colormap_windows = atom[7];
457 	w_global.atom.wm.colormap_notify = atom[8];
458 
459 	w_global.atom.wmaker.menu = atom[9];
460 	w_global.atom.wmaker.state = atom[10];
461 	w_global.atom.wmaker.wm_protocols = atom[11];
462 	w_global.atom.wmaker.wm_function = atom[12];
463 	w_global.atom.wmaker.noticeboard = atom[13];
464 	w_global.atom.wmaker.command = atom[14];
465 	w_global.atom.wmaker.icon_size = atom[15];
466 	w_global.atom.wmaker.icon_tile = atom[16];
467 
468 	w_global.atom.gnustep.wm_attr = atom[17];
469 	w_global.atom.gnustep.wm_miniaturize_window = atom[18];
470 	w_global.atom.gnustep.titlebar_state = atom[19];
471 
472 	w_global.atom.desktop.gtk_object_path = atom[20];
473 
474 	w_global.atom.wm.ignore_focus_events = atom[21];
475 
476 #ifdef USE_DOCK_XDND
477 	wXDNDInitializeAtoms();
478 #endif
479 
480 	/* cursors */
481 	wPreferences.cursor[WCUR_NORMAL] = None;	/* inherit from root */
482 	wPreferences.cursor[WCUR_ROOT] = XCreateFontCursor(dpy, XC_left_ptr);
483 	wPreferences.cursor[WCUR_ARROW] = XCreateFontCursor(dpy, XC_top_left_arrow);
484 	wPreferences.cursor[WCUR_MOVE] = XCreateFontCursor(dpy, XC_fleur);
485 	wPreferences.cursor[WCUR_RESIZE] = XCreateFontCursor(dpy, XC_sizing);
486 	wPreferences.cursor[WCUR_TOPLEFTRESIZE] = XCreateFontCursor(dpy, XC_top_left_corner);
487 	wPreferences.cursor[WCUR_TOPRIGHTRESIZE] = XCreateFontCursor(dpy, XC_top_right_corner);
488 	wPreferences.cursor[WCUR_BOTTOMLEFTRESIZE] = XCreateFontCursor(dpy, XC_bottom_left_corner);
489 	wPreferences.cursor[WCUR_BOTTOMRIGHTRESIZE] = XCreateFontCursor(dpy, XC_bottom_right_corner);
490 	wPreferences.cursor[WCUR_VERTICALRESIZE] = XCreateFontCursor(dpy, XC_sb_v_double_arrow);
491 	wPreferences.cursor[WCUR_HORIZONRESIZE] = XCreateFontCursor(dpy, XC_sb_h_double_arrow);
492 	wPreferences.cursor[WCUR_WAIT] = XCreateFontCursor(dpy, XC_watch);
493 	wPreferences.cursor[WCUR_QUESTION] = XCreateFontCursor(dpy, XC_question_arrow);
494 	wPreferences.cursor[WCUR_TEXT] = XCreateFontCursor(dpy, XC_xterm);	/* odd name??? */
495 	wPreferences.cursor[WCUR_SELECT] = XCreateFontCursor(dpy, XC_cross);
496 
497 	Pixmap cur = XCreatePixmap(dpy, DefaultRootWindow(dpy), 16, 16, 1);
498 	GC gc = XCreateGC(dpy, cur, 0, NULL);
499 	XColor black;
500 	memset(&black, 0, sizeof(XColor));
501 	XSetForeground(dpy, gc, 0);
502 	XFillRectangle(dpy, cur, gc, 0, 0, 16, 16);
503 	XFreeGC(dpy, gc);
504 	wPreferences.cursor[WCUR_EMPTY] = XCreatePixmapCursor(dpy, cur, cur, &black, &black, 0, 0);
505 	XFreePixmap(dpy, cur);
506 
507 	/* emergency exit... */
508 	sig_action.sa_handler = handleSig;
509 	sigemptyset(&sig_action.sa_mask);
510 
511 	sig_action.sa_flags = SA_RESTART;
512 	sigaction(SIGQUIT, &sig_action, NULL);
513 	/* instead of catching these, we let the default handler abort the
514 	 * program. The new monitor process will take appropriate action
515 	 * when it detects the crash.
516 	 sigaction(SIGSEGV, &sig_action, NULL);
517 	 sigaction(SIGBUS, &sig_action, NULL);
518 	 sigaction(SIGFPE, &sig_action, NULL);
519 	 sigaction(SIGABRT, &sig_action, NULL);
520 	 */
521 
522 	sig_action.sa_handler = handleExitSig;
523 
524 	/* Here we set SA_RESTART for safety, because SIGUSR1 may not be handled
525 	 * immediately. -Dan */
526 	sig_action.sa_flags = SA_RESTART;
527 	sigaction(SIGTERM, &sig_action, NULL);
528 	sigaction(SIGINT, &sig_action, NULL);
529 	sigaction(SIGHUP, &sig_action, NULL);
530 	sigaction(SIGUSR1, &sig_action, NULL);
531 	sigaction(SIGUSR2, &sig_action, NULL);
532 
533 	/* ignore dead pipe */
534 	/* Because POSIX mandates that only signal with handlers are reset
535 	 * across an exec*(), we do not want to propagate ignoring SIGPIPEs
536 	 * to children. Hence the dummy handler.
537 	 * Philippe Troin <phil@fifi.org>
538 	 */
539 	sig_action.sa_handler = &dummyHandler;
540 	sig_action.sa_flags = SA_RESTART;
541 	sigaction(SIGPIPE, &sig_action, NULL);
542 
543 	/* handle dead children */
544 	sig_action.sa_handler = buryChild;
545 	sig_action.sa_flags = SA_NOCLDSTOP | SA_RESTART;
546 	sigaction(SIGCHLD, &sig_action, NULL);
547 
548 	/* Now we unblock all signals, that may have been blocked by the parent
549 	 * who exec()-ed us. This can happen for example if Window Maker crashes
550 	 * and restarts itself or another window manager from the signal handler.
551 	 * In this case, the new process inherits the blocked signal mask and
552 	 * will no longer react to that signal, until unblocked.
553 	 * This is because the signal handler of the process who crashed (parent)
554 	 * didn't return, and the signal remained blocked. -Dan
555 	 */
556 	sigfillset(&sig_action.sa_mask);
557 	sigprocmask(SIG_UNBLOCK, &sig_action.sa_mask, NULL);
558 
559 	/* handle X shutdowns a such */
560 	XSetIOErrorHandler(handleXIO);
561 
562 	/* set hook for out event dispatcher in WINGs event dispatcher */
563 	WMHookEventHandler(DispatchEvent);
564 
565 	/* initialize defaults stuff */
566 	w_global.domain.wmaker = wDefaultsInitDomain("WindowMaker", True);
567 	if (!w_global.domain.wmaker->dictionary)
568 		wwarning(_("could not read domain \"%s\" from defaults database"), "WindowMaker");
569 
570 	/* read defaults that don't change until a restart and are
571 	 * screen independent */
572 	wReadStaticDefaults(w_global.domain.wmaker ? w_global.domain.wmaker->dictionary : NULL);
573 
574 	/* check sanity of some values */
575 	if (wPreferences.icon_size < 16) {
576 		wwarning(_("icon size is configured to %i, but it's too small. Using 16 instead"),
577 			 wPreferences.icon_size);
578 		wPreferences.icon_size = 16;
579 	}
580 
581 	/* init other domains */
582 	w_global.domain.root_menu = wDefaultsInitDomain("WMRootMenu", False);
583 	if (!w_global.domain.root_menu->dictionary)
584 		wwarning(_("could not read domain \"%s\" from defaults database"), "WMRootMenu");
585 
586 	wDefaultsMergeGlobalMenus(w_global.domain.root_menu);
587 
588 	w_global.domain.window_attr = wDefaultsInitDomain("WMWindowAttributes", True);
589 	if (!w_global.domain.window_attr->dictionary)
590 		wwarning(_("could not read domain \"%s\" from defaults database"), "WMWindowAttributes");
591 
592 	XSetErrorHandler((XErrorHandler) catchXError);
593 
594 #ifdef USE_XSHAPE
595 	/* ignore j */
596 	w_global.xext.shape.supported = XShapeQueryExtension(dpy, &w_global.xext.shape.event_base, &j);
597 #endif
598 
599 #ifdef USE_RANDR
600 	w_global.xext.randr.supported = XRRQueryExtension(dpy, &w_global.xext.randr.event_base, &j);
601 #endif
602 
603 #ifdef KEEP_XKB_LOCK_STATUS
604 	w_global.xext.xkb.supported = XkbQueryExtension(dpy, NULL, &w_global.xext.xkb.event_base, NULL, NULL, NULL);
605 	if (wPreferences.modelock && !w_global.xext.xkb.supported) {
606 		wwarning(_("XKB is not supported. KbdModeLock is automatically disabled."));
607 		wPreferences.modelock = 0;
608 	}
609 #endif
610 
611 	if (defaultScreenOnly)
612 		max = 1;
613 	else
614 		max = ScreenCount(dpy);
615 
616 	wScreen = wmalloc(sizeof(WScreen *) * max);
617 
618 	w_global.screen_count = 0;
619 
620 	/* Check if TIFF images are supported */
621 	formats = RSupportedFileFormats();
622 	if (formats) {
623 		for (i = 0; formats[i] != NULL; i++) {
624 			if (strcmp(formats[i], "TIFF") == 0) {
625 				wPreferences.supports_tiff = 1;
626 				break;
627 			}
628 		}
629 	}
630 
631 	/* manage the screens */
632 	for (j = 0; j < max; j++) {
633 		if (defaultScreenOnly || max == 1) {
634 			wScreen[w_global.screen_count] = wScreenInit(DefaultScreen(dpy));
635 			if (!wScreen[w_global.screen_count]) {
636 				wfatal(_("it seems that there is already a window manager running"));
637 				Exit(1);
638 			}
639 		} else {
640 			wScreen[w_global.screen_count] = wScreenInit(j);
641 			if (!wScreen[w_global.screen_count]) {
642 				wwarning(_("could not manage screen %i"), j);
643 				continue;
644 			}
645 		}
646 		w_global.screen_count++;
647 	}
648 
649 	InitializeSwitchMenu();
650 
651 	/* initialize/restore state for the screens */
652 	for (j = 0; j < w_global.screen_count; j++) {
653 		int lastDesktop;
654 
655 		lastDesktop = wNETWMGetCurrentDesktopFromHint(wScreen[j]);
656 
657 		wScreenRestoreState(wScreen[j]);
658 
659 		/* manage all windows that were already here before us */
660 		if (!wPreferences.flags.nodock && wScreen[j]->dock)
661 			wScreen[j]->last_dock = wScreen[j]->dock;
662 
663 		manageAllWindows(wScreen[j], wPreferences.flags.restarting == 2);
664 
665 		/* restore saved menus */
666 		wMenuRestoreState(wScreen[j]);
667 
668 		/* If we're not restarting, restore session */
669 		if (!wPreferences.flags.restarting && !wPreferences.flags.norestore)
670 			wSessionRestoreState(wScreen[j]);
671 
672 		if (!wPreferences.flags.noautolaunch) {
673 			/* auto-launch apps */
674 			if (!wPreferences.flags.nodock && wScreen[j]->dock) {
675 				wScreen[j]->last_dock = wScreen[j]->dock;
676 				wDockDoAutoLaunch(wScreen[j]->dock, 0);
677 			}
678 			/* auto-launch apps in clip */
679 			if (!wPreferences.flags.noclip) {
680 				int i;
681 				for (i = 0; i < wScreen[j]->workspace_count; i++) {
682 					if (wScreen[j]->workspaces[i]->clip) {
683 						wScreen[j]->last_dock = wScreen[j]->workspaces[i]->clip;
684 						wDockDoAutoLaunch(wScreen[j]->workspaces[i]->clip, i);
685 					}
686 				}
687 			}
688 			/* auto-launch apps in drawers */
689 			if (!wPreferences.flags.nodrawer) {
690 				WDrawerChain *dc;
691 				for (dc = wScreen[j]->drawers; dc; dc = dc->next) {
692 					wScreen[j]->last_dock = dc->adrawer;
693 					wDockDoAutoLaunch(dc->adrawer, 0);
694 				}
695 			}
696 		}
697 
698 		/* go to workspace where we were before restart */
699 		if (lastDesktop >= 0)
700 			wWorkspaceForceChange(wScreen[j], lastDesktop);
701 		else
702 			wSessionRestoreLastWorkspace(wScreen[j]);
703 	}
704 
705 	if (w_global.screen_count == 0) {
706 		wfatal(_("could not manage any screen"));
707 		Exit(1);
708 	}
709 
710 #ifndef HAVE_INOTIFY
711 	/* setup defaults file polling */
712 	if (!wPreferences.flags.noupdates)
713 		WMAddTimerHandler(3000, wDefaultsCheckDomains, NULL);
714 #endif
715 
716 }
717 
windowInList(Window window,Window * list,int count)718 static Bool windowInList(Window window, Window * list, int count)
719 {
720 	for (; count >= 0; count--) {
721 		if (window == list[count])
722 			return True;
723 	}
724 	return False;
725 }
726 
727 /*
728  *-----------------------------------------------------------------------
729  * manageAllWindows--
730  * 	Manages all windows in the screen.
731  *
732  * Notes:
733  * 	Called when the wm is being started.
734  *	No events can be processed while the windows are being
735  * reparented/managed.
736  *-----------------------------------------------------------------------
737  */
manageAllWindows(WScreen * scr,int crashRecovery)738 static void manageAllWindows(WScreen * scr, int crashRecovery)
739 {
740 	Window root, parent;
741 	Window *children;
742 	unsigned int nchildren;
743 	unsigned int i, j;
744 	WWindow *wwin;
745 
746 	XGrabServer(dpy);
747 	XQueryTree(dpy, scr->root_win, &root, &parent, &children, &nchildren);
748 
749 	scr->flags.startup = 1;
750 
751 	/* first remove all icon windows */
752 	for (i = 0; i < nchildren; i++) {
753 		XWMHints *wmhints;
754 
755 		if (children[i] == None)
756 			continue;
757 
758 		wmhints = XGetWMHints(dpy, children[i]);
759 		if (wmhints && (wmhints->flags & IconWindowHint)) {
760 			for (j = 0; j < nchildren; j++) {
761 				if (children[j] == wmhints->icon_window) {
762 					XFree(wmhints);
763 					wmhints = NULL;
764 					children[j] = None;
765 					break;
766 				}
767 			}
768 		}
769 		if (wmhints) {
770 			XFree(wmhints);
771 		}
772 	}
773 
774 	for (i = 0; i < nchildren; i++) {
775 		if (children[i] == None)
776 			continue;
777 
778 		wwin = wManageWindow(scr, children[i]);
779 		if (wwin) {
780 			/* apply states got from WSavedState */
781 			/* shaded + minimized is not restored correctly */
782 			if (wwin->flags.shaded) {
783 				wwin->flags.shaded = 0;
784 				wShadeWindow(wwin);
785 			}
786 			if (wwin->flags.miniaturized
787 			    && (wwin->transient_for == None
788 				|| wwin->transient_for == scr->root_win
789 				|| !windowInList(wwin->transient_for, children, nchildren))) {
790 
791 				wwin->flags.skip_next_animation = 1;
792 				wwin->flags.miniaturized = 0;
793 				wIconifyWindow(wwin);
794 			} else {
795 				wClientSetState(wwin, NormalState, None);
796 			}
797 			if (crashRecovery) {
798 				int border;
799 
800 				border = (!HAS_BORDER(wwin) ? 0 : scr->frame_border_width);
801 
802 				wWindowMove(wwin, wwin->frame_x - border,
803 					    wwin->frame_y - border -
804 					    (wwin->frame->titlebar ? wwin->frame->titlebar->height : 0));
805 			}
806 		}
807 	}
808 	XUngrabServer(dpy);
809 
810 	/* hide apps */
811 	wwin = scr->focused_window;
812 	while (wwin) {
813 		if (wwin->flags.hidden) {
814 			WApplication *wapp = wApplicationOf(wwin->main_window);
815 
816 			if (wapp) {
817 				wwin->flags.hidden = 0;
818 				wHideApplication(wapp);
819 			} else {
820 				wwin->flags.hidden = 0;
821 			}
822 		}
823 		wwin = wwin->prev;
824 	}
825 
826 	XFree(children);
827 	scr->flags.startup = 0;
828 	scr->flags.startup2 = 1;
829 
830 	while (XPending(dpy)) {
831 		XEvent ev;
832 		WMNextEvent(dpy, &ev);
833 		WMHandleEvent(&ev);
834 	}
835 	scr->last_workspace = 0;
836 	wWorkspaceForceChange(scr, 0);
837 	if (!wPreferences.flags.noclip)
838 		wDockShowIcons(scr->workspaces[scr->current_workspace]->clip);
839 	scr->flags.startup2 = 0;
840 }
841