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, >k_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, ®istry_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