1/*
2 * Taking over the screen
3 */
4
5#include "ctwm.h"
6
7#include <stdio.h>
8#include <X11/Xproto.h>
9#include <X11/Xmu/Error.h>
10
11#include "ctwm_takeover.h"
12#include "screen.h"
13
14
15/// Flag for "we got an error trying to take over".  Set in temporary
16/// error handler.
17static bool RedirectError;
18
19// Our special during-takeover and normal operating error handlers.
20static int CatchRedirectError(Display *display, XErrorEvent *event);
21static int TwmErrorHandler(Display *display, XErrorEvent *event);
22
23
24
25/**
26 * Take over as WM for a screen
27 */
28bool
29takeover_screen(ScreenInfo *scr)
30{
31	unsigned long attrmask;
32
33#ifdef EWMH
34	// Early EWMH setup.  This tries to do the EWMH display takeover.
35	EwmhInitScreenEarly(scr);
36#endif
37
38
39	/*
40	 * Subscribe to various events on the root window.  Because X
41	 * only allows a single client to subscribe to
42	 * SubstructureRedirect and ButtonPress bits, this also serves to
43	 * mutex who is The WM for the root window, and thus (aside from
44	 * captive) the Screen.
45	 *
46	 * To catch whether that failed, we set a special one-shot error
47	 * handler to flip a var that we test to find out whether the
48	 * redirect failed.
49	 */
50	// Flush out any previous errors
51	XSync(dpy, 0);
52
53	// Set our event listening mask
54	RedirectError = false;
55	XSetErrorHandler(CatchRedirectError);
56	attrmask = ColormapChangeMask | EnterWindowMask |
57	           PropertyChangeMask | SubstructureRedirectMask |
58	           KeyPressMask | ButtonPressMask | ButtonReleaseMask;
59#ifdef EWMH
60	attrmask |= StructureNotifyMask;
61#endif
62#ifdef CAPTIVE
63	if(CLarg.is_captive) {
64		attrmask |= StructureNotifyMask;
65	}
66#endif
67	XSelectInput(dpy, scr->Root, attrmask);
68
69	// Make sure we flush out any errors that may have caused.  This
70	// ensures our RedirectError flag will be set if the server sent us
71	// one.
72	XSync(dpy, 0);
73
74	// Go ahead and set our normal-operation error handler.
75	XSetErrorHandler(TwmErrorHandler);
76
77	// So, did we fail?
78	if(RedirectError) {
79		fprintf(stderr, "%s: another window manager is already running",
80		        ProgramName);
81		if(CLarg.MultiScreen && NumScreens > 0) {
82			fprintf(stderr, " on screen %d?\n", scr->screen);
83		}
84		else {
85			fprintf(stderr, "?\n");
86		}
87
88		// XSetErrorHandler() isn't local to the Screen; it's for the
89		// whole connection.  We wind up in a slightly weird state
90		// once we've set it up, but decided we aren't taking over
91		// this screen, but resetting it would be a little weird too,
92		// because maybe we have taken over some other screen.  So,
93		// just throw up our hands.
94		return false;
95	}
96
97	// Nope, it's ours!
98	return true;
99}
100
101
102
103/**
104 * Temporary error handler used during startup.  We expect an
105 * error if we fail to take over some of the XSelectInput() events
106 * we're trying to (which only 1 thing at a time is allowed to).
107 * Probably that would be a BadAccess error type?  But really, any error
108 * means we're in trouble and should skip over the display, so we don't
109 * check any more deeply...
110 */
111static int
112CatchRedirectError(Display *display, XErrorEvent *event)
113{
114	// Set the global flag
115	RedirectError = true;
116	return 0;
117}
118
119
120/**
121 * Error handler used in normal operation.  Or, perhaps, error ignorer
122 * used in normal operation.  If run with `-v`, we'll print out a lot of
123 * the errors we might get, though we always skip several.
124 */
125static int
126TwmErrorHandler(Display *display, XErrorEvent *event)
127{
128	if(!CLarg.PrintErrorMessages) {
129		// Not `-v`?  Always be silent.
130		return 0;
131	}
132
133	// If a client dies and we try to touch it before we notice, we get a
134	// BadWindow error for most operations, except a few (GetGeometry
135	// being the big one?) where we'll get a BadDrawable.  This isn't
136	// really an "error", just a harmless race.
137	if(event->error_code == BadWindow
138	                || (event->request_code == X_GetGeometry && event->error_code != BadDrawable)) {
139		return 0;
140	}
141
142	// Else, we're `-v`'d, and it wasn't one of our 'expected' bits, so
143	// talk about it.
144	XmuPrintDefaultErrorMessage(display, event, stderr);
145	return 0;
146}
147