1 #include "config.h"
2 #include "base.h"
3 #include <string.h>
4 #include <stdio.h>
5 #include <X11/Xlib.h>
6 #ifdef CONFIG_SHAPE
7 #include <X11/extensions/shape.h>
8 #endif
9 #ifdef CONFIG_XRANDR
10 #include <X11/extensions/Xrandr.h>
11 #endif
12 #include "logevent.h"
13 
14 bool loggingEvents;
15 bool loggedEventsInited;
16 
emptyAtom(Atom atom)17 static const char* emptyAtom(Atom atom) { return ""; }
18 static AtomNameFunc atomName = emptyAtom;
setAtomName(AtomNameFunc func)19 void setAtomName(AtomNameFunc func) { atomName = func; }
getAtomName(unsigned long atom)20 const char* getAtomName(unsigned long atom) {
21     return atomName ? atomName(atom) : "";
22 }
23 
24 #if LOGEVENTS
25 
26 bool loggedEvents[LASTEvent];
27 
28 static const char eventNames[][17] = {
29     "KeyPress",             //  2
30     "KeyRelease",           //  3
31     "ButtonPress",          //  4
32     "ButtonRelease",        //  5
33     "MotionNotify",         //  6
34     "EnterNotify",          //  7
35     "LeaveNotify",          //  8
36     "FocusIn",              //  9
37     "FocusOut",             // 10
38     "KeymapNotify",         // 11
39     "Expose",               // 12
40     "GraphicsExpose",       // 13
41     "NoExpose",             // 14
42     "VisibilityNotify",     // 15
43     "CreateNotify",         // 16
44     "DestroyNotify",        // 17
45     "UnmapNotify",          // 18
46     "MapNotify",            // 19
47     "MapRequest",           // 20
48     "ReparentNotify",       // 21
49     "ConfigureNotify",      // 22
50     "ConfigureRequest",     // 23
51     "GravityNotify",        // 24
52     "ResizeRequest",        // 25
53     "CirculateNotify",      // 26
54     "CirculateRequest",     // 27
55     "PropertyNotify",       // 28
56     "SelectionClear",       // 29
57     "SelectionRequest",     // 30
58     "SelectionNotify",      // 31
59     "ColormapNotify",       // 32
60     "ClientMessage",        // 33
61     "MappingNotify",        // 34
62     "GenericEvent",         // 35
63 };
eventName(int eventType)64 const char* eventName(int eventType) {
65     if (inrange(eventType, KeyPress, GenericEvent))
66         return eventNames[eventType - KeyPress];
67     return "UnknownEvent!";
68 }
69 
setLogEvent(int evtype,bool enable)70 void setLogEvent(int evtype, bool enable) {
71     if (size_t(evtype) < sizeof loggedEvents)
72         loggedEvents[evtype] = enable;
73     else if (evtype == -1)
74         memset(loggedEvents, enable, sizeof loggedEvents);
75 }
76 
boolStr(Bool aBool)77 inline const char* boolStr(Bool aBool) {
78     return aBool ? "True" : "False";
79 }
80 
logAny(const XAnyEvent & xev)81 void logAny(const XAnyEvent& xev) {
82     tlog("window=0x%lX: %s type=%d, send=%s, #%lu",
83         xev.window, eventName(xev.type), xev.type,
84         boolStr(xev.send_event), (unsigned long) xev.serial);
85 }
86 
logButton(const XButtonEvent & xev)87 void logButton(const XButtonEvent& xev) {
88     tlog("window=0x%lX: %s root=0x%lX, subwindow=0x%lX, time=%ld, "
89         "(%d:%d %d:%d) state=0x%X button=%d same_screen=%s",
90         xev.window,
91         eventName(xev.type),
92         xev.root,
93         xev.subwindow,
94         xev.time,
95         xev.x, xev.y,
96         xev.x_root, xev.y_root,
97         xev.state,
98         xev.button,
99         boolStr(xev.same_screen));
100 }
101 
logColormap(const XColormapEvent & xev)102 void logColormap(const XColormapEvent& xev) {
103     tlog("window=0x%lX: colormapNotify colormap=%ld new=%s state=%d",
104         xev.window,
105         xev.colormap,
106         boolStr(xev.c_new),
107         xev.state);
108 }
109 
logConfigureNotify(const XConfigureEvent & xev)110 void logConfigureNotify(const XConfigureEvent& xev) {
111     const int size = 256;
112     char buf[size];
113     int len = snprintf(buf, size, "window=0x%lX: configureNotify serial=%lu event=0x%lX, (%+d%+d %dx%d) border=%d",
114         xev.window,
115         (unsigned long) xev.serial,
116         xev.event,
117         xev.x, xev.y,
118         xev.width, xev.height,
119         xev.border_width);
120     if (xev.above)
121         len += snprintf(buf + len, size - len, " above=0x%lX", xev.above);
122     if (xev.override_redirect)
123         len += snprintf(buf + len, size - len, " override=%s", boolStr(xev.override_redirect));
124     if (xev.send_event)
125         len += snprintf(buf + len, size - len, " send=%s", boolStr(xev.send_event));
126     tlog("%s", (0 < len && len < size) ? buf : "lcnbug");
127 }
128 
logConfigureRequest(const XConfigureRequestEvent & xev)129 void logConfigureRequest(const XConfigureRequestEvent& xev) {
130     const int size = 256;
131     char buf[size];
132     int len = snprintf(buf, size, "window=0x%lX: %s configureRequest",
133                        xev.window, xev.send_event ? "synth" : "real");
134     len += snprintf(buf + len, size - len, " serial=%lu parent=0x%lX",
135                     (unsigned long) xev.serial, xev.parent);
136     if ((xev.value_mask & CWX) && 0 < len && len < size)
137         len += snprintf(buf + len, size - len, " X=%d", xev.x);
138     if ((xev.value_mask & CWY) && 0 < len && len < size)
139         len += snprintf(buf + len, size - len, " Y=%d", xev.y);
140     if ((xev.value_mask & CWWidth) && 0 < len && len < size)
141         len += snprintf(buf + len, size - len, " Width=%d", xev.width);
142     if ((xev.value_mask & CWHeight) && 0 < len && len < size)
143         len += snprintf(buf + len, size - len, " Height=%d", xev.height);
144     if ((xev.value_mask & CWBorderWidth) && 0 < len && len < size)
145         len += snprintf(buf + len, size - len, " Border=%d", xev.border_width);
146     if ((xev.value_mask & CWSibling) && 0 < len && len < size)
147         len += snprintf(buf + len, size - len, " Sibling=0x%lX", xev.above);
148     if ((xev.value_mask & CWStackMode) && 0 < len && len < size)
149         len += snprintf(buf + len, size - len, " StackMode=%s",
150                         xev.detail == Above ? "Above" :
151                         xev.detail == Below ? "Below" :
152                         xev.detail == TopIf ? "TopIf" :
153                         xev.detail == BottomIf ? "BottomIf" :
154                         xev.detail == Opposite ? "Opposite" : "Invalid");
155     tlog("%s", (0 < len && len < size) ? buf : "lcrbug");
156 }
157 
logCreate(const XCreateWindowEvent & xev)158 void logCreate(const XCreateWindowEvent& xev) {
159     tlog("window=0x%lX: create serial=%lu parent=0x%lX, (%+d%+d %dx%d) border=%d, override=%s",
160         xev.window,
161         (unsigned long) xev.serial,
162         xev.parent,
163         xev.x, xev.y,
164         xev.width, xev.height,
165         xev.border_width,
166         boolStr(xev.override_redirect));
167 }
168 
logCrossing(const XCrossingEvent & xev)169 void logCrossing(const XCrossingEvent& xev) {
170     tlog("window=0x%06lX: %s serial=%lu root=0x%lX, subwindow=0x%lX, time=%ld, "
171         "(%d:%d %d:%d) mode=%s detail=%s same_screen=%s, focus=%s state=0x%X",
172         xev.window,
173         eventName(xev.type),
174         (unsigned long) xev.serial,
175         xev.root,
176         xev.subwindow,
177         xev.time,
178         xev.x, xev.y,
179         xev.x_root, xev.y_root,
180         xev.mode == NotifyNormal ? "Normal" :
181         xev.mode == NotifyGrab ? "Grab" :
182         xev.mode == NotifyUngrab ? "Ungrab" :
183         xev.mode == NotifyWhileGrabbed ? "Grabbed" : "Unknown",
184         xev.detail == NotifyAncestor ? "Ancestor" :
185         xev.detail == NotifyVirtual ? "Virtual" :
186         xev.detail == NotifyInferior ? "Inferior" :
187         xev.detail == NotifyNonlinear ? "Nonlinear" :
188         xev.detail == NotifyNonlinearVirtual ? "NonlinearVirtual" :
189         xev.detail == NotifyPointer ? "Pointer" :
190         xev.detail == NotifyPointerRoot ? "PointerRoot" :
191         xev.detail == NotifyDetailNone ? "DetailNone" : "Unknown",
192         boolStr(xev.same_screen),
193         boolStr(xev.focus),
194         xev.state);
195 }
196 
logDestroy(const XDestroyWindowEvent & xev)197 void logDestroy(const XDestroyWindowEvent& xev) {
198     tlog("window=0x%lX: destroy serial=%lu event=0x%lX",
199         xev.window,
200         (unsigned long) xev.serial,
201         xev.event);
202 }
203 
logExpose(const XExposeEvent & xev)204 void logExpose(const XExposeEvent& xev) {
205     tlog("window=0x%lX: expose (%+d%+d %dx%d) count=%d",
206         xev.window,
207         xev.x, xev.y, xev.width, xev.height,
208         xev.count);
209 }
210 
logFocus(const XFocusChangeEvent & xev)211 void logFocus(const XFocusChangeEvent& xev) {
212     tlog("window=0x%lX: %s mode=%s, detail=%s",
213         xev.window,
214         eventName(xev.type),
215         xev.mode == NotifyNormal ? "NotifyNormal" :
216         xev.mode == NotifyWhileGrabbed ? "NotifyWhileGrabbed" :
217         xev.mode == NotifyGrab ? "NotifyGrab" :
218         xev.mode == NotifyUngrab ? "NotifyUngrab" : "???",
219         xev.detail == NotifyAncestor ? "NotifyAncestor" :
220         xev.detail == NotifyVirtual ? "NotifyVirtual" :
221         xev.detail == NotifyInferior ? "NotifyInferior" :
222         xev.detail == NotifyNonlinear ? "NotifyNonlinear" :
223         xev.detail == NotifyNonlinearVirtual ? "NotifyNonlinearVirtual" :
224         xev.detail == NotifyPointer ? "NotifyPointer" :
225         xev.detail == NotifyPointerRoot ? "NotifyPointerRoot" :
226         xev.detail == NotifyDetailNone ? "NotifyDetailNone" : "???");
227 }
228 
logGravity(const XGravityEvent & xev)229 void logGravity(const XGravityEvent& xev) {
230     tlog("window=0x%lX: gravityNotify serial=%lu, x=%+d, y=%+d",
231         xev.window,
232         (unsigned long) xev.serial,
233         xev.x, xev.y);
234 }
235 
logKey(const XKeyEvent & xev)236 void logKey(const XKeyEvent& xev) {
237     tlog("window=0x%lX: %s root=0x%lX, subwindow=0x%lX, time=%ld, (%d:%d %d:%d) state=0x%X keycode=0x%x same_screen=%s",
238         xev.window,
239         eventName(xev.type),
240         xev.root,
241         xev.subwindow,
242         xev.time,
243         xev.x, xev.y,
244         xev.x_root, xev.y_root,
245         xev.state,
246         xev.keycode,
247         boolStr(xev.same_screen));
248 }
249 
logMapping(const XMappingEvent & xev)250 void logMapping(const XMappingEvent& xev) {
251     tlog("window=0x%lX: mapping request=%s, keycode=%d, n=%d, serial=%lu, send=%s",
252         xev.window,
253         xev.request == MappingModifier ? "MappingModifier" :
254         xev.request == MappingKeyboard ? "MappingKeyboard" :
255         xev.request == MappingPointer ? "MappingPointer" : "?",
256         xev.first_keycode, xev.count,
257         (unsigned long) xev.serial,
258         boolStr(xev.send_event));
259 }
260 
logMapRequest(const XMapRequestEvent & xev)261 void logMapRequest(const XMapRequestEvent& xev) {
262     tlog("window=0x%lX: mapRequest serial=%lu parent=0x%lX",
263         xev.window,
264         (unsigned long) xev.serial,
265         xev.parent);
266 }
267 
logMapNotify(const XMapEvent & xev)268 void logMapNotify(const XMapEvent& xev) {
269     tlog("window=0x%lX: mapNotify serial=%lu event=0x%lX, override=%s send=%s",
270         xev.window,
271         (unsigned long) xev.serial,
272         xev.event,
273         boolStr(xev.override_redirect),
274         boolStr(xev.send_event));
275 }
276 
logUnmap(const XUnmapEvent & xev)277 void logUnmap(const XUnmapEvent& xev) {
278     tlog("window=0x%lX: unmapNotify serial=%lu event=0x%lX, from_configure=%s send=%s",
279         xev.window,
280         (unsigned long) xev.serial,
281         xev.event,
282         boolStr(xev.from_configure),
283         boolStr(xev.send_event));
284 }
285 
logMotion(const XMotionEvent & xev)286 void logMotion(const XMotionEvent& xev) {
287     tlog("window=0x%lX: %s root=0x%lX, subwindow=0x%lX, time=%ld, "
288         "(%d:%d %d:%d) state=0x%X is_hint=%s same_screen=%s",
289         xev.window,
290         eventName(xev.type),
291         xev.root,
292         xev.subwindow,
293         xev.time,
294         xev.x, xev.y,
295         xev.x_root, xev.y_root,
296         xev.state,
297         xev.is_hint == NotifyHint ? "NotifyHint" : "",
298         boolStr(xev.same_screen));
299 }
300 
logProperty(const XPropertyEvent & xev)301 void logProperty(const XPropertyEvent& xev) {
302     tlog("window=0x%lX: propertyNotify %s time=%ld state=%s",
303         xev.window,
304         atomName(xev.atom),
305         xev.time,
306         xev.state == PropertyNewValue ? "NewValue" :
307         xev.state == PropertyDelete ? "Delete" : "?");
308 }
309 
logReparent(const XReparentEvent & xev)310 void logReparent(const XReparentEvent& xev) {
311     tlog("window=0x%lX: reparentNotify serial=%lu event=0x%lX, parent=0x%lX, (%d:%d), override=%s",
312         xev.window,
313         (unsigned long) xev.serial,
314         xev.event,
315         xev.parent,
316         xev.x, xev.y,
317         boolStr(xev.override_redirect));
318 }
319 
logVisibility(const XVisibilityEvent & xev)320 void logVisibility(const XVisibilityEvent& xev) {
321     tlog("window=0x%lX: visibilityNotify state=%s",
322         xev.window,
323         xev.state == VisibilityPartiallyObscured ? "partial" :
324         xev.state == VisibilityFullyObscured ? "obscured" :
325         xev.state == VisibilityUnobscured ? "unobscured" : "bogus"
326         );
327 }
328 
329 #ifdef CONFIG_SHAPE
logShape(const XEvent & xev)330 void logShape(const XEvent& xev) {
331     const XShapeEvent &shp = (const XShapeEvent &)xev;
332     tlog("window=0x%lX: %s kind=%s %d:%d=%dx%d shaped=%s time=%ld",
333         shp.window, "ShapeEvent",
334         shp.kind == ShapeBounding ? "ShapeBounding" :
335         shp.kind == ShapeClip ? "ShapeClip" : "unknown_shape_kind",
336         shp.x, shp.y, shp.width, shp.height, boolstr(shp.shaped), shp.time);
337 }
338 #endif
339 
340 #ifdef CONFIG_XRANDR
logRandrScreen(const XEvent & xev)341 void logRandrScreen(const XEvent& xev) {
342     const XRRScreenChangeNotifyEvent& evt =
343         (const XRRScreenChangeNotifyEvent &)xev;
344     tlog("window=0x%lX: %s index=%u order=%u "
345         "rotation=%u width=%dpx(%dmm) height=%dpx(%dmm)",
346         evt.window, "XRRScreenChangeNotifyEvent",
347         evt.size_index, evt.subpixel_order, (evt.rotation & 15) * 45,
348         evt.width, evt.mwidth, evt.height, evt.mheight
349        );
350 }
351 #endif
352 
353 #ifdef CONFIG_XRANDR
logRandrNotify(const XEvent & xev)354 void logRandrNotify(const XEvent& xev) {
355     const XRRNotifyEvent& nev = (const XRRNotifyEvent &)xev;
356     if (nev.subtype == RRNotify_CrtcChange) {
357         const XRRCrtcChangeNotifyEvent& e =
358             (const XRRCrtcChangeNotifyEvent &) xev;
359         tlog("window=0x%lX: %s crtc=%lu mode=%lu rotation=%u %ux%u+%d+%d",
360             e.window, "XRRCrtcChangeNotifyEvent",
361             e.crtc, e.mode, (e.rotation & 15) * 45, e.width, e.height, e.x, e.y
362            );
363     }
364     else if (nev.subtype == RRNotify_OutputChange) {
365         const XRROutputChangeNotifyEvent& e =
366             (const XRROutputChangeNotifyEvent &) xev;
367         tlog("window=0x%lX: %s output=%lu crtc=%lu mode=%lu "
368             "rotation=%u connection=%s subpixel=%u",
369             e.window, "XRROutputChangeNotifyEvent",
370             e.output, e.crtc, e.mode, (e.rotation & 15) * 45,
371             e.connection == RR_Connected ? "RR_Connected" :
372             e.connection == RR_Disconnected ? "RR_Disconnected" :
373             e.connection == RR_UnknownConnection ? "RR_UnknownConnection" :
374             "unknown", e.subpixel_order
375            );
376     }
377     else if (nev.subtype == RRNotify_OutputProperty) {
378         const XRROutputPropertyNotifyEvent& e =
379             (const XRROutputPropertyNotifyEvent &) xev;
380         tlog("window=0x%lX: %s output=%lu property=%lu state=%s",
381             e.window, "XRROutputPropertyNotifyEvent",
382             e.output, e.property,
383             e.state == PropertyNewValue ? "NewValue" :
384             e.state == PropertyDelete ? "Deleted" :
385             "unknown"
386            );
387     }
388 #ifdef RRNotify_ProviderChange
389     else if (nev.subtype == RRNotify_ProviderChange) {
390         const XRRProviderChangeNotifyEvent& e =
391             (const XRRProviderChangeNotifyEvent &) xev;
392         tlog("window=0x%lX: %s provider=%lu current_role=%u",
393             e.window, "XRRProviderChangeNotifyEvent",
394             e.provider, e.current_role
395            );
396     }
397 #endif
398 #ifdef RRNotify_ProviderProperty
399     else if (nev.subtype == RRNotify_ProviderProperty) {
400         const XRRProviderPropertyNotifyEvent& e =
401             (const XRRProviderPropertyNotifyEvent &) xev;
402         tlog("window=0x%lX: %s provider=%lu property=%lu state=%s",
403             e.window, "XRRProviderPropertyNotifyEvent",
404             e.provider, e.property,
405             e.state == PropertyNewValue ? "NewValue" :
406             e.state == PropertyDelete ? "Deleted" :
407             "unknown"
408            );
409     }
410 #endif
411 #ifdef RRNotify_ResourceChange
412     else if (nev.subtype == RRNotify_ResourceChange) {
413         const XRRResourceChangeNotifyEvent& e =
414             (const XRRResourceChangeNotifyEvent &) xev;
415         tlog("window=0x%lX: %s",
416             e.window, "XRRResourceChangeNotifyEvent"
417            );
418     }
419 #endif
420 }
421 #endif
422 
423 #else
eventName(int eventType)424 const char* eventName(int eventType) {
425     static char name[3];
426     name[0] = eventType / 10 % 10 + '0';
427     name[1] = eventType % 10 + '0';
428     name[2] = '\0';
429     return name;
430 }
431 #endif
432 
433 #if LOGEVENTS
434 #pragma GCC diagnostic push
435 #pragma GCC diagnostic ignored "-Wpragmas"
436 #pragma GCC diagnostic ignored "-Wunknown-pragmas"
437 #pragma GCC diagnostic ignored "-Wunknown-warning-option"
438 #pragma GCC diagnostic ignored "-Wcast-function-type"
439 #endif
440 
logEvent(const XEvent & xev)441 void logEvent(const XEvent& xev) {
442 #if LOGEVENTS
443     typedef void (*fun)(const XEvent&);
444     static void (*const loggers[])(const XEvent&) = {
445         (fun) logAny,               //  0 reserved
446         (fun) logAny,               //  1 reserved
447         (fun) logKey,               //  2 KeyPress
448         (fun) logKey,               //  3 KeyRelease
449         (fun) logButton,            //  4 ButtonPress
450         (fun) logButton,            //  5 ButtonRelease
451         (fun) logMotion,            //  6 MotionNotify
452         (fun) logCrossing,          //  7 EnterNotify
453         (fun) logCrossing,          //  8 LeaveNotify
454         (fun) logFocus,             //  9 FocusIn
455         (fun) logFocus,             // 10 FocusOut
456         (fun) logAny,               // 11 KeymapNotify
457         (fun) logExpose,            // 12 Expose
458         (fun) logAny,               // 13 GraphicsExpose
459         (fun) logAny,               // 14 NoExpose
460         (fun) logVisibility,        // 15 VisibilityNotify
461         (fun) logCreate,            // 16 CreateNotify
462         (fun) logDestroy,           // 17 DestroyNotify
463         (fun) logUnmap,             // 18 UnmapNotify
464         (fun) logMapNotify,         // 19 MapNotify
465         (fun) logMapRequest,        // 20 MapRequest
466         (fun) logReparent,          // 21 ReparentNotify
467         (fun) logConfigureNotify,   // 22 ConfigureNotify
468         (fun) logConfigureRequest,  // 23 ConfigureRequest
469         (fun) logGravity,           // 24 GravityNotify
470         (fun) logAny,               // 25 ResizeRequest
471         (fun) logAny,               // 26 CirculateNotify
472         (fun) logAny,               // 27 CirculateRequest
473         (fun) logProperty,          // 28 PropertyNotify
474         (fun) logAny,               // 29 SelectionClear
475         (fun) logAny,               // 30 SelectionRequest
476         (fun) logAny,               // 31 SelectionNotify
477         (fun) logColormap,          // 32 ColormapNotify
478         (fun) logClientMessage,     // 33 ClientMessage
479         (fun) logMapping,           // 34 MappingNotify
480         (fun) logAny,               // 35 GenericEvent
481     };
482     if (loggingEvents && size_t(xev.type) < sizeof loggedEvents &&
483         (loggedEventsInited || initLogEvents()) && loggedEvents[xev.type])
484     {
485         loggers[xev.type](xev);
486     }
487 #endif
488 }
489 
490 #if LOGEVENTS
491 #pragma GCC diagnostic pop
492 #endif
493 
initLogEvents()494 bool initLogEvents() {
495 #if LOGEVENTS
496     if (loggedEventsInited == false) {
497         memset(loggedEvents, false, sizeof loggedEvents);
498 
499         // setLogEvent(KeyPress, true);
500         // setLogEvent(KeyRelease, true);
501         setLogEvent(ButtonPress, true);
502         setLogEvent(ButtonRelease, true);
503         // setLogEvent(MotionNotify, true);
504         setLogEvent(EnterNotify, true);
505         setLogEvent(LeaveNotify, true);
506         setLogEvent(FocusIn, true);
507         setLogEvent(FocusOut, true);
508         // setLogEvent(KeymapNotify, true);
509         // setLogEvent(Expose, true);
510         // setLogEvent(GraphicsExpose, true);
511         // setLogEvent(NoExpose, true);
512         // setLogEvent(VisibilityNotify, true);
513         setLogEvent(CreateNotify, true);
514         setLogEvent(DestroyNotify, true);
515         setLogEvent(UnmapNotify, true);
516         setLogEvent(MapNotify, true);
517         setLogEvent(MapRequest, true);
518         setLogEvent(ReparentNotify, true);
519         setLogEvent(ConfigureNotify, true);
520         setLogEvent(ConfigureRequest, true);
521         // setLogEvent(GravityNotify, true);
522         setLogEvent(ResizeRequest, true);
523         // setLogEvent(CirculateNotify, true);
524         // setLogEvent(CirculateRequest, true);
525         // setLogEvent(PropertyNotify, true);
526         // setLogEvent(SelectionClear, true);
527         // setLogEvent(SelectionRequest, true);
528         // setLogEvent(SelectionNotify, true);
529         // setLogEvent(ColormapNotify, true);
530         setLogEvent(ClientMessage, true);
531         setLogEvent(MappingNotify, true);
532         // setLogEvent(GenericEvent, true);
533 
534         loggedEventsInited = true;
535     }
536 #endif
537     return loggedEventsInited;
538 }
539 
toggleLogEvents()540 bool toggleLogEvents() {
541 #if LOGEVENTS
542     loggingEvents = !loggingEvents && initLogEvents();
543 #endif
544     return loggingEvents;
545 }
546 
logClientMessage(const XClientMessageEvent & event)547 void logClientMessage(const XClientMessageEvent& event) {
548     const char* name = atomName(event.message_type);
549     const long* data = event.data.l;
550     char head[64];
551     snprintf(head, sizeof head, "window=0x%lX: ", event.window);
552     if (strcmp(name, "_NET_WM_STATE") == 0) {
553         const char* op =
554             data[0] == 0 ? "REMOVE" :
555             data[0] == 1 ? "ADD" :
556             data[0] == 2 ? "TOGGLE" : "?";
557         const char* p1 = data[1] ? atomName(data[1]) : "";
558         const char* p2 = data[2] ? atomName(data[2]) : "";
559         tlog("%sClientMessage %s %d data=%s,%s,%s\n",
560                 head, name, event.format, op, p1, p2);
561     }
562     else if (strcmp(name, "WM_CHANGE_STATE") == 0) {
563         const char* op =
564             data[0] == 0 ? "WithdrawnState" :
565             data[0] == 1 ? "NormalState" :
566             data[0] == 3 ? "IconicState" : "?";
567         tlog("%sClientMessage %s %s\n", head, name, op);
568     }
569     else {
570         tlog("%sClientMessage %s fmt=%d data=%ld,0x%lX,0x%lX",
571             head, name, event.format, data[0], data[1], data[2]);
572     }
573 }
574 
575