1 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim:expandtab:shiftwidth=4:tabstop=4:
3  */
4 /* This Source Code Form is subject to the terms of the Mozilla Public
5  * License, v. 2.0. If a copy of the MPL was not distributed with this
6  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 
8 #include "nsWaylandDisplay.h"
9 
10 #include <dlfcn.h>
11 
12 #include "base/message_loop.h"  // for MessageLoop
13 #include "base/task.h"          // for NewRunnableMethod, etc
14 #include "mozilla/StaticMutex.h"
15 #include "mozilla/StaticPrefs_widget.h"
16 #include "WidgetUtilsGtk.h"
17 
18 struct _GdkSeat;
19 typedef struct _GdkSeat GdkSeat;
20 
21 namespace mozilla {
22 namespace widget {
23 
24 // nsWaylandDisplay needs to be created for each calling thread(main thread,
25 // compositor thread and render thread)
26 #define MAX_DISPLAY_CONNECTIONS 10
27 
28 // An array of active wayland displays. We need a display for every thread
29 // where is wayland interface used as we need to dispatch waylands events
30 // there.
31 static RefPtr<nsWaylandDisplay> gWaylandDisplays[MAX_DISPLAY_CONNECTIONS];
32 static StaticMutex gWaylandDisplayArrayWriteMutex;
33 
34 // Dispatch events to Compositor/Render queues
WaylandDispatchDisplays()35 void WaylandDispatchDisplays() {
36   MOZ_ASSERT(NS_IsMainThread(),
37              "WaylandDispatchDisplays() is supposed to run in main thread");
38   for (auto& display : gWaylandDisplays) {
39     if (display) {
40       display->DispatchEventQueue();
41     }
42   }
43 }
44 
WaylandDisplayRelease()45 void WaylandDisplayRelease() {
46   StaticMutexAutoLock lock(gWaylandDisplayArrayWriteMutex);
47   for (auto& display : gWaylandDisplays) {
48     if (display) {
49       display = nullptr;
50     }
51   }
52 }
53 
54 // Get WaylandDisplay for given wl_display and actual calling thread.
WaylandDisplayGet(GdkDisplay * aGdkDisplay)55 RefPtr<nsWaylandDisplay> WaylandDisplayGet(GdkDisplay* aGdkDisplay) {
56   wl_display* waylandDisplay = WaylandDisplayGetWLDisplay(aGdkDisplay);
57   if (!waylandDisplay) {
58     return nullptr;
59   }
60 
61   // Search existing display connections for wl_display:thread combination.
62   for (auto& display : gWaylandDisplays) {
63     if (display && display->Matches(waylandDisplay)) {
64       return display;
65     }
66   }
67 
68   StaticMutexAutoLock arrayLock(gWaylandDisplayArrayWriteMutex);
69   for (auto& display : gWaylandDisplays) {
70     if (display == nullptr) {
71       display = new nsWaylandDisplay(waylandDisplay);
72       return display;
73     }
74   }
75 
76   MOZ_CRASH("There's too many wayland display conections!");
77   return nullptr;
78 }
79 
WaylandDisplayGetWLDisplay(GdkDisplay * aGdkDisplay)80 wl_display* WaylandDisplayGetWLDisplay(GdkDisplay* aGdkDisplay) {
81   if (!aGdkDisplay) {
82     aGdkDisplay = gdk_display_get_default();
83     if (!GdkIsWaylandDisplay(aGdkDisplay)) {
84       return nullptr;
85     }
86   }
87 
88   return gdk_wayland_display_get_wl_display(aGdkDisplay);
89 }
90 
SetShm(wl_shm * aShm)91 void nsWaylandDisplay::SetShm(wl_shm* aShm) { mShm = aShm; }
92 
SetCompositor(wl_compositor * aCompositor)93 void nsWaylandDisplay::SetCompositor(wl_compositor* aCompositor) {
94   mCompositor = aCompositor;
95 }
96 
SetSubcompositor(wl_subcompositor * aSubcompositor)97 void nsWaylandDisplay::SetSubcompositor(wl_subcompositor* aSubcompositor) {
98   mSubcompositor = aSubcompositor;
99 }
100 
SetDataDeviceManager(wl_data_device_manager * aDataDeviceManager)101 void nsWaylandDisplay::SetDataDeviceManager(
102     wl_data_device_manager* aDataDeviceManager) {
103   mDataDeviceManager = aDataDeviceManager;
104 }
105 
GetSeat()106 wl_seat* nsWaylandDisplay::GetSeat() {
107   GdkDisplay* gdkDisplay = gdk_display_get_default();
108   if (!gdkDisplay) {
109     return nullptr;
110   }
111 
112   static auto sGdkDisplayGetDefaultSeat = (GdkSeat * (*)(GdkDisplay*))
113       dlsym(RTLD_DEFAULT, "gdk_display_get_default_seat");
114   if (!sGdkDisplayGetDefaultSeat) {
115     return nullptr;
116   }
117 
118   static auto sGdkWaylandSeatGetWlSeat = (struct wl_seat * (*)(GdkSeat*))
119       dlsym(RTLD_DEFAULT, "gdk_wayland_seat_get_wl_seat");
120   if (!sGdkWaylandSeatGetWlSeat) {
121     return nullptr;
122   }
123 
124   GdkSeat* gdkSeat = sGdkDisplayGetDefaultSeat(gdkDisplay);
125   return sGdkWaylandSeatGetWlSeat(gdkSeat);
126 }
127 
SetPrimarySelectionDeviceManager(gtk_primary_selection_device_manager * aPrimarySelectionDeviceManager)128 void nsWaylandDisplay::SetPrimarySelectionDeviceManager(
129     gtk_primary_selection_device_manager* aPrimarySelectionDeviceManager) {
130   mPrimarySelectionDeviceManagerGtk = aPrimarySelectionDeviceManager;
131 }
132 
SetPrimarySelectionDeviceManager(zwp_primary_selection_device_manager_v1 * aPrimarySelectionDeviceManager)133 void nsWaylandDisplay::SetPrimarySelectionDeviceManager(
134     zwp_primary_selection_device_manager_v1* aPrimarySelectionDeviceManager) {
135   mPrimarySelectionDeviceManagerZwpV1 = aPrimarySelectionDeviceManager;
136 }
137 
SetIdleInhibitManager(zwp_idle_inhibit_manager_v1 * aIdleInhibitManager)138 void nsWaylandDisplay::SetIdleInhibitManager(
139     zwp_idle_inhibit_manager_v1* aIdleInhibitManager) {
140   mIdleInhibitManager = aIdleInhibitManager;
141 }
142 
SetViewporter(wp_viewporter * aViewporter)143 void nsWaylandDisplay::SetViewporter(wp_viewporter* aViewporter) {
144   mViewporter = aViewporter;
145 }
146 
SetRelativePointerManager(zwp_relative_pointer_manager_v1 * aRelativePointerManager)147 void nsWaylandDisplay::SetRelativePointerManager(
148     zwp_relative_pointer_manager_v1* aRelativePointerManager) {
149   mRelativePointerManager = aRelativePointerManager;
150 }
151 
SetPointerConstraints(zwp_pointer_constraints_v1 * aPointerConstraints)152 void nsWaylandDisplay::SetPointerConstraints(
153     zwp_pointer_constraints_v1* aPointerConstraints) {
154   mPointerConstraints = aPointerConstraints;
155 }
156 
global_registry_handler(void * data,wl_registry * registry,uint32_t id,const char * interface,uint32_t version)157 static void global_registry_handler(void* data, wl_registry* registry,
158                                     uint32_t id, const char* interface,
159                                     uint32_t version) {
160   auto* display = static_cast<nsWaylandDisplay*>(data);
161   if (!display) {
162     return;
163   }
164 
165   if (strcmp(interface, "wl_shm") == 0) {
166     auto* shm = WaylandRegistryBind<wl_shm>(registry, id, &wl_shm_interface, 1);
167     wl_proxy_set_queue((struct wl_proxy*)shm, display->GetEventQueue());
168     display->SetShm(shm);
169   } else if (strcmp(interface, "wl_data_device_manager") == 0) {
170     int data_device_manager_version = MIN(version, 3);
171     auto* data_device_manager = WaylandRegistryBind<wl_data_device_manager>(
172         registry, id, &wl_data_device_manager_interface,
173         data_device_manager_version);
174     wl_proxy_set_queue((struct wl_proxy*)data_device_manager,
175                        display->GetEventQueue());
176     display->SetDataDeviceManager(data_device_manager);
177   } else if (strcmp(interface, "gtk_primary_selection_device_manager") == 0) {
178     auto* primary_selection_device_manager =
179         WaylandRegistryBind<gtk_primary_selection_device_manager>(
180             registry, id, &gtk_primary_selection_device_manager_interface, 1);
181     wl_proxy_set_queue((struct wl_proxy*)primary_selection_device_manager,
182                        display->GetEventQueue());
183     display->SetPrimarySelectionDeviceManager(primary_selection_device_manager);
184   } else if (strcmp(interface, "zwp_primary_selection_device_manager_v1") ==
185              0) {
186     auto* primary_selection_device_manager =
187         WaylandRegistryBind<gtk_primary_selection_device_manager>(
188             registry, id, &zwp_primary_selection_device_manager_v1_interface,
189             1);
190     wl_proxy_set_queue((struct wl_proxy*)primary_selection_device_manager,
191                        display->GetEventQueue());
192     display->SetPrimarySelectionDeviceManager(primary_selection_device_manager);
193   } else if (strcmp(interface, "zwp_idle_inhibit_manager_v1") == 0) {
194     auto* idle_inhibit_manager =
195         WaylandRegistryBind<zwp_idle_inhibit_manager_v1>(
196             registry, id, &zwp_idle_inhibit_manager_v1_interface, 1);
197     wl_proxy_set_queue((struct wl_proxy*)idle_inhibit_manager,
198                        display->GetEventQueue());
199     display->SetIdleInhibitManager(idle_inhibit_manager);
200   } else if (strcmp(interface, "zwp_relative_pointer_manager_v1") == 0) {
201     auto* relative_pointer_manager =
202         WaylandRegistryBind<zwp_relative_pointer_manager_v1>(
203             registry, id, &zwp_relative_pointer_manager_v1_interface, 1);
204     wl_proxy_set_queue((struct wl_proxy*)relative_pointer_manager,
205                        display->GetEventQueue());
206     display->SetRelativePointerManager(relative_pointer_manager);
207   } else if (strcmp(interface, "zwp_pointer_constraints_v1") == 0) {
208     auto* pointer_constraints = WaylandRegistryBind<zwp_pointer_constraints_v1>(
209         registry, id, &zwp_pointer_constraints_v1_interface, 1);
210     wl_proxy_set_queue((struct wl_proxy*)pointer_constraints,
211                        display->GetEventQueue());
212     display->SetPointerConstraints(pointer_constraints);
213   } else if (strcmp(interface, "wl_compositor") == 0) {
214     // Requested wl_compositor version 4 as we need wl_surface_damage_buffer().
215     auto* compositor = WaylandRegistryBind<wl_compositor>(
216         registry, id, &wl_compositor_interface, 4);
217     wl_proxy_set_queue((struct wl_proxy*)compositor, display->GetEventQueue());
218     display->SetCompositor(compositor);
219   } else if (strcmp(interface, "wl_subcompositor") == 0) {
220     auto* subcompositor = WaylandRegistryBind<wl_subcompositor>(
221         registry, id, &wl_subcompositor_interface, 1);
222     wl_proxy_set_queue((struct wl_proxy*)subcompositor,
223                        display->GetEventQueue());
224     display->SetSubcompositor(subcompositor);
225   } else if (strcmp(interface, "wp_viewporter") == 0) {
226     auto* viewporter = WaylandRegistryBind<wp_viewporter>(
227         registry, id, &wp_viewporter_interface, 1);
228     wl_proxy_set_queue((struct wl_proxy*)viewporter, display->GetEventQueue());
229     display->SetViewporter(viewporter);
230   }
231 }
232 
global_registry_remover(void * data,wl_registry * registry,uint32_t id)233 static void global_registry_remover(void* data, wl_registry* registry,
234                                     uint32_t id) {}
235 
236 static const struct wl_registry_listener registry_listener = {
237     global_registry_handler, global_registry_remover};
238 
DispatchEventQueue()239 bool nsWaylandDisplay::DispatchEventQueue() {
240   if (mEventQueue) {
241     wl_display_dispatch_queue_pending(mDisplay, mEventQueue);
242   }
243   return true;
244 }
245 
SyncEnd()246 void nsWaylandDisplay::SyncEnd() {
247   wl_callback_destroy(mSyncCallback);
248   mSyncCallback = nullptr;
249 }
250 
wayland_sync_callback(void * data,struct wl_callback * callback,uint32_t time)251 static void wayland_sync_callback(void* data, struct wl_callback* callback,
252                                   uint32_t time) {
253   auto display = static_cast<nsWaylandDisplay*>(data);
254   display->SyncEnd();
255 }
256 
257 static const struct wl_callback_listener sync_callback_listener = {
258     .done = wayland_sync_callback};
259 
SyncBegin()260 void nsWaylandDisplay::SyncBegin() {
261   WaitForSyncEnd();
262 
263   // Use wl_display_sync() to synchronize wayland events.
264   // See dri2_wl_swap_buffers_with_damage() from MESA
265   // or wl_display_roundtrip_queue() from wayland-client.
266   struct wl_display* displayWrapper =
267       static_cast<wl_display*>(wl_proxy_create_wrapper((void*)mDisplay));
268   if (!displayWrapper) {
269     NS_WARNING("Failed to create wl_proxy wrapper!");
270     return;
271   }
272 
273   wl_proxy_set_queue((struct wl_proxy*)displayWrapper, mEventQueue);
274   mSyncCallback = wl_display_sync(displayWrapper);
275   wl_proxy_wrapper_destroy((void*)displayWrapper);
276 
277   if (!mSyncCallback) {
278     NS_WARNING("Failed to create wl_display_sync callback!");
279     return;
280   }
281 
282   wl_callback_add_listener(mSyncCallback, &sync_callback_listener, this);
283   wl_display_flush(mDisplay);
284 }
285 
QueueSyncBegin()286 void nsWaylandDisplay::QueueSyncBegin() {
287   RefPtr<nsWaylandDisplay> self(this);
288   NS_DispatchToMainThread(
289       NS_NewRunnableFunction("nsWaylandDisplay::QueueSyncBegin",
290                              [self]() -> void { self->SyncBegin(); }));
291 }
292 
WaitForSyncEnd()293 void nsWaylandDisplay::WaitForSyncEnd() {
294   MOZ_RELEASE_ASSERT(
295       NS_IsMainThread(),
296       "nsWaylandDisplay::WaitForSyncEnd() can be called in main thread only!");
297 
298   // We're done here
299   if (!mSyncCallback) {
300     return;
301   }
302 
303   while (mSyncCallback != nullptr) {
304     // TODO: wl_display_dispatch_queue() should not be called while
305     // glib main loop is iterated at nsAppShell::ProcessNextNativeEvent().
306     if (wl_display_dispatch_queue(mDisplay, mEventQueue) == -1) {
307       NS_WARNING("wl_display_dispatch_queue failed!");
308       SyncEnd();
309       return;
310     }
311   }
312 }
313 
Matches(wl_display * aDisplay)314 bool nsWaylandDisplay::Matches(wl_display* aDisplay) {
315   return mThreadId == PR_GetCurrentThread() && aDisplay == mDisplay;
316 }
317 
nsWaylandDisplay(wl_display * aDisplay,bool aLighWrapper)318 nsWaylandDisplay::nsWaylandDisplay(wl_display* aDisplay, bool aLighWrapper)
319     : mThreadId(PR_GetCurrentThread()),
320       mDisplay(aDisplay),
321       mEventQueue(nullptr),
322       mDataDeviceManager(nullptr),
323       mCompositor(nullptr),
324       mSubcompositor(nullptr),
325       mShm(nullptr),
326       mSyncCallback(nullptr),
327       mPrimarySelectionDeviceManagerGtk(nullptr),
328       mPrimarySelectionDeviceManagerZwpV1(nullptr),
329       mIdleInhibitManager(nullptr),
330       mRelativePointerManager(nullptr),
331       mPointerConstraints(nullptr),
332       mRegistry(nullptr),
333       mViewporter(nullptr),
334       mExplicitSync(false) {
335   if (!aLighWrapper) {
336     mRegistry = wl_display_get_registry(mDisplay);
337     wl_registry_add_listener(mRegistry, &registry_listener, this);
338   }
339 
340   if (!NS_IsMainThread()) {
341     mEventQueue = wl_display_create_queue(mDisplay);
342     wl_proxy_set_queue((struct wl_proxy*)mRegistry, mEventQueue);
343   }
344 
345   if (!aLighWrapper) {
346     if (mEventQueue) {
347       wl_display_roundtrip_queue(mDisplay, mEventQueue);
348       wl_display_roundtrip_queue(mDisplay, mEventQueue);
349     } else {
350       wl_display_roundtrip(mDisplay);
351       wl_display_roundtrip(mDisplay);
352     }
353   }
354 }
355 
~nsWaylandDisplay()356 nsWaylandDisplay::~nsWaylandDisplay() {
357   wl_registry_destroy(mRegistry);
358   mRegistry = nullptr;
359 
360   if (mEventQueue) {
361     wl_event_queue_destroy(mEventQueue);
362     mEventQueue = nullptr;
363   }
364   mDisplay = nullptr;
365 }
366 
367 }  // namespace widget
368 }  // namespace mozilla
369