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