1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7 #ifndef mozilla_X11Util_h
8 #define mozilla_X11Util_h
9
10 // Utilities common to all X clients, regardless of UI toolkit.
11
12 #if defined(MOZ_WIDGET_GTK)
13 # include <gdk/gdk.h>
14 # include <gdk/gdkx.h>
15 # include "X11UndefineNone.h"
16 #else
17 # error Unknown toolkit
18 #endif
19
20 #include <string.h> // for memset
21 #include "mozilla/Scoped.h" // for SCOPED_TEMPLATE
22
23 namespace mozilla {
24
25 /**
26 * Return the default X Display created and used by the UI toolkit.
27 */
DefaultXDisplay()28 inline Display* DefaultXDisplay() {
29 #if defined(MOZ_WIDGET_GTK)
30 return GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
31 #endif
32 }
33
34 /**
35 * Sets *aVisual to point to aDisplay's Visual struct corresponding to
36 * aVisualID, and *aDepth to its depth. When aVisualID is None, these are set
37 * to nullptr and 0 respectively. Both out-parameter pointers are assumed
38 * non-nullptr.
39 */
40 void FindVisualAndDepth(Display* aDisplay, VisualID aVisualID, Visual** aVisual,
41 int* aDepth);
42
43 /**
44 * Ensure that all X requests have been processed.
45 *
46 * This is similar to XSync, but doesn't need a round trip if the previous
47 * request was synchronous or if events have been received since the last
48 * request. Subsequent FinishX calls will be noops if there have been no
49 * intermediate requests.
50 */
51
52 void FinishX(Display* aDisplay);
53
54 /**
55 * Invoke XFree() on a pointer to memory allocated by Xlib (if the
56 * pointer is nonnull) when this class goes out of scope.
57 */
58 template <typename T>
59 struct ScopedXFreePtrTraits {
60 typedef T* type;
emptyScopedXFreePtrTraits61 static T* empty() { return nullptr; }
releaseScopedXFreePtrTraits62 static void release(T* ptr) {
63 if (ptr != nullptr) XFree(ptr);
64 }
65 };
SCOPED_TEMPLATE(ScopedXFree,ScopedXFreePtrTraits)66 SCOPED_TEMPLATE(ScopedXFree, ScopedXFreePtrTraits)
67
68 /**
69 * On construction, set a graceful X error handler that doesn't crash the
70 * application and records X errors. On destruction, restore the X error handler
71 * to what it was before construction.
72 *
73 * The SyncAndGetError() method allows to know whether a X error occurred,
74 * optionally allows to get the full XErrorEvent, and resets the recorded X
75 * error state so that a single X error will be reported only once.
76 *
77 * Nesting is correctly handled: multiple nested ScopedXErrorHandler's don't
78 * interfere with each other's state. However, if SyncAndGetError is not called
79 * on the nested ScopedXErrorHandler, then any X errors caused by X calls made
80 * while the nested ScopedXErrorHandler was in place may then be caught by the
81 * other ScopedXErrorHandler. This is just a result of X being asynchronous and
82 * us not doing any implicit syncing: the only method in this class what causes
83 * syncing is SyncAndGetError().
84 *
85 * This class is not thread-safe at all. It is assumed that only one thread is
86 * using any ScopedXErrorHandler's. Given that it's not used on Mac, it should
87 * be easy to make it thread-safe by using thread-local storage with __thread.
88 */
89 class ScopedXErrorHandler {
90 public:
91 // trivial wrapper around XErrorEvent, just adding ctor initializing by zero.
92 struct ErrorEvent {
93 XErrorEvent mError;
94
95 ErrorEvent() { memset(this, 0, sizeof(ErrorEvent)); }
96 };
97
98 private:
99 // this ScopedXErrorHandler's ErrorEvent object
100 ErrorEvent mXError;
101
102 // static pointer for use by the error handler
103 static ErrorEvent* sXErrorPtr;
104
105 // what to restore sXErrorPtr to on destruction
106 ErrorEvent* mOldXErrorPtr;
107
108 // what to restore the error handler to on destruction
109 int (*mOldErrorHandler)(Display*, XErrorEvent*);
110
111 public:
112 static int ErrorHandler(Display*, XErrorEvent* ev);
113
114 /**
115 * @param aAllowOffMainThread whether to warn if used off main thread
116 */
117 explicit ScopedXErrorHandler(bool aAllowOffMainThread = false);
118
119 ~ScopedXErrorHandler();
120
121 /** \returns true if a X error occurred since the last time this method was
122 * called on this ScopedXErrorHandler object, or since the creation of this
123 * ScopedXErrorHandler object if this method was never called on it.
124 *
125 * \param ev this optional parameter, if set, will be filled with the
126 * XErrorEvent object. If multiple errors occurred, the first one will be
127 * returned.
128 */
129 bool SyncAndGetError(Display* dpy, XErrorEvent* ev = nullptr);
130 };
131
132 class OffMainThreadScopedXErrorHandler : public ScopedXErrorHandler {
133 public:
OffMainThreadScopedXErrorHandler()134 OffMainThreadScopedXErrorHandler() : ScopedXErrorHandler(true) {}
135 };
136
137 } // namespace mozilla
138
139 #endif // mozilla_X11Util_h
140