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