1 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim:expandtab:shiftwidth=4:tabstop=4:
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/. */
8 #include "nsWaylandDisplay.h"
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"
19 typedef struct _GdkSeat GdkSeat
;
24 // nsWaylandDisplay needs to be created for each calling thread(main thread,
25 // compositor thread and render thread)
26 #define MAX_DISPLAY_CONNECTIONS 10
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
31 static RefPtr
<nsWaylandDisplay
> gWaylandDisplays
[MAX_DISPLAY_CONNECTIONS
];
32 static StaticMutex gWaylandDisplayArrayWriteMutex MOZ_UNANNOTATED
;
34 // Dispatch events to Compositor/Render queues
35 void WaylandDispatchDisplays() {
36 MOZ_ASSERT(NS_IsMainThread(),
37 "WaylandDispatchDisplays() is supposed to run in main thread");
38 for (auto& display
: gWaylandDisplays
) {
40 display
->DispatchEventQueue();
45 void WaylandDisplayRelease() {
46 StaticMutexAutoLock
lock(gWaylandDisplayArrayWriteMutex
);
47 for (auto& display
: gWaylandDisplays
) {
54 // Get WaylandDisplay for given wl_display and actual calling thread.
55 RefPtr
<nsWaylandDisplay
> WaylandDisplayGet(GdkDisplay
* aGdkDisplay
) {
56 wl_display
* waylandDisplay
= WaylandDisplayGetWLDisplay(aGdkDisplay
);
57 if (!waylandDisplay
) {
61 // Search existing display connections for wl_display:thread combination.
62 for (auto& display
: gWaylandDisplays
) {
63 if (display
&& display
->Matches(waylandDisplay
)) {
68 StaticMutexAutoLock
arrayLock(gWaylandDisplayArrayWriteMutex
);
69 for (auto& display
: gWaylandDisplays
) {
70 if (display
== nullptr) {
71 display
= new nsWaylandDisplay(waylandDisplay
);
76 MOZ_CRASH("There's too many wayland display conections!");
80 wl_display
* WaylandDisplayGetWLDisplay(GdkDisplay
* aGdkDisplay
) {
82 aGdkDisplay
= gdk_display_get_default();
83 if (!GdkIsWaylandDisplay(aGdkDisplay
)) {
88 return gdk_wayland_display_get_wl_display(aGdkDisplay
);
91 void nsWaylandDisplay::SetShm(wl_shm
* aShm
) { mShm
= aShm
; }
93 void nsWaylandDisplay::SetCompositor(wl_compositor
* aCompositor
) {
94 mCompositor
= aCompositor
;
97 void nsWaylandDisplay::SetSubcompositor(wl_subcompositor
* aSubcompositor
) {
98 mSubcompositor
= aSubcompositor
;
101 void nsWaylandDisplay::SetDataDeviceManager(
102 wl_data_device_manager
* aDataDeviceManager
) {
103 mDataDeviceManager
= aDataDeviceManager
;
106 wl_seat
* nsWaylandDisplay::GetSeat() {
107 GdkDisplay
* gdkDisplay
= gdk_display_get_default();
112 static auto sGdkDisplayGetDefaultSeat
= (GdkSeat
* (*)(GdkDisplay
*))
113 dlsym(RTLD_DEFAULT
, "gdk_display_get_default_seat");
114 if (!sGdkDisplayGetDefaultSeat
) {
118 static auto sGdkWaylandSeatGetWlSeat
= (struct wl_seat
* (*)(GdkSeat
*))
119 dlsym(RTLD_DEFAULT
, "gdk_wayland_seat_get_wl_seat");
120 if (!sGdkWaylandSeatGetWlSeat
) {
124 GdkSeat
* gdkSeat
= sGdkDisplayGetDefaultSeat(gdkDisplay
);
125 return sGdkWaylandSeatGetWlSeat(gdkSeat
);
128 void nsWaylandDisplay::SetPrimarySelectionDeviceManager(
129 gtk_primary_selection_device_manager
* aPrimarySelectionDeviceManager
) {
130 mPrimarySelectionDeviceManagerGtk
= aPrimarySelectionDeviceManager
;
133 void nsWaylandDisplay::SetPrimarySelectionDeviceManager(
134 zwp_primary_selection_device_manager_v1
* aPrimarySelectionDeviceManager
) {
135 mPrimarySelectionDeviceManagerZwpV1
= aPrimarySelectionDeviceManager
;
138 void nsWaylandDisplay::SetIdleInhibitManager(
139 zwp_idle_inhibit_manager_v1
* aIdleInhibitManager
) {
140 mIdleInhibitManager
= aIdleInhibitManager
;
143 void nsWaylandDisplay::SetViewporter(wp_viewporter
* aViewporter
) {
144 mViewporter
= aViewporter
;
147 void nsWaylandDisplay::SetRelativePointerManager(
148 zwp_relative_pointer_manager_v1
* aRelativePointerManager
) {
149 mRelativePointerManager
= aRelativePointerManager
;
152 void nsWaylandDisplay::SetPointerConstraints(
153 zwp_pointer_constraints_v1
* aPointerConstraints
) {
154 mPointerConstraints
= aPointerConstraints
;
157 void nsWaylandDisplay::SetDmabuf(zwp_linux_dmabuf_v1
* aDmabuf
) {
161 void nsWaylandDisplay::SetXdgActivation(xdg_activation_v1
* aXdgActivation
) {
162 mXdgActivation
= aXdgActivation
;
165 static void global_registry_handler(void* data
, wl_registry
* registry
,
166 uint32_t id
, const char* interface
,
168 auto* display
= static_cast<nsWaylandDisplay
*>(data
);
173 if (strcmp(interface
, "wl_shm") == 0) {
174 auto* shm
= WaylandRegistryBind
<wl_shm
>(registry
, id
, &wl_shm_interface
, 1);
175 wl_proxy_set_queue((struct wl_proxy
*)shm
, display
->GetEventQueue());
176 display
->SetShm(shm
);
177 } else if (strcmp(interface
, "wl_data_device_manager") == 0) {
178 int data_device_manager_version
= MIN(version
, 3);
179 auto* data_device_manager
= WaylandRegistryBind
<wl_data_device_manager
>(
180 registry
, id
, &wl_data_device_manager_interface
,
181 data_device_manager_version
);
182 wl_proxy_set_queue((struct wl_proxy
*)data_device_manager
,
183 display
->GetEventQueue());
184 display
->SetDataDeviceManager(data_device_manager
);
185 } else if (strcmp(interface
, "gtk_primary_selection_device_manager") == 0) {
186 auto* primary_selection_device_manager
=
187 WaylandRegistryBind
<gtk_primary_selection_device_manager
>(
188 registry
, id
, >k_primary_selection_device_manager_interface
, 1);
189 wl_proxy_set_queue((struct wl_proxy
*)primary_selection_device_manager
,
190 display
->GetEventQueue());
191 display
->SetPrimarySelectionDeviceManager(primary_selection_device_manager
);
192 } else if (strcmp(interface
, "zwp_primary_selection_device_manager_v1") ==
194 auto* primary_selection_device_manager
=
195 WaylandRegistryBind
<gtk_primary_selection_device_manager
>(
196 registry
, id
, &zwp_primary_selection_device_manager_v1_interface
,
198 wl_proxy_set_queue((struct wl_proxy
*)primary_selection_device_manager
,
199 display
->GetEventQueue());
200 display
->SetPrimarySelectionDeviceManager(primary_selection_device_manager
);
201 } else if (strcmp(interface
, "zwp_idle_inhibit_manager_v1") == 0) {
202 auto* idle_inhibit_manager
=
203 WaylandRegistryBind
<zwp_idle_inhibit_manager_v1
>(
204 registry
, id
, &zwp_idle_inhibit_manager_v1_interface
, 1);
205 wl_proxy_set_queue((struct wl_proxy
*)idle_inhibit_manager
,
206 display
->GetEventQueue());
207 display
->SetIdleInhibitManager(idle_inhibit_manager
);
208 } else if (strcmp(interface
, "zwp_relative_pointer_manager_v1") == 0) {
209 auto* relative_pointer_manager
=
210 WaylandRegistryBind
<zwp_relative_pointer_manager_v1
>(
211 registry
, id
, &zwp_relative_pointer_manager_v1_interface
, 1);
212 wl_proxy_set_queue((struct wl_proxy
*)relative_pointer_manager
,
213 display
->GetEventQueue());
214 display
->SetRelativePointerManager(relative_pointer_manager
);
215 } else if (strcmp(interface
, "zwp_pointer_constraints_v1") == 0) {
216 auto* pointer_constraints
= WaylandRegistryBind
<zwp_pointer_constraints_v1
>(
217 registry
, id
, &zwp_pointer_constraints_v1_interface
, 1);
218 wl_proxy_set_queue((struct wl_proxy
*)pointer_constraints
,
219 display
->GetEventQueue());
220 display
->SetPointerConstraints(pointer_constraints
);
221 } else if (strcmp(interface
, "wl_compositor") == 0) {
222 // Requested wl_compositor version 4 as we need wl_surface_damage_buffer().
223 auto* compositor
= WaylandRegistryBind
<wl_compositor
>(
224 registry
, id
, &wl_compositor_interface
, 4);
225 wl_proxy_set_queue((struct wl_proxy
*)compositor
, display
->GetEventQueue());
226 display
->SetCompositor(compositor
);
227 } else if (strcmp(interface
, "wl_subcompositor") == 0) {
228 auto* subcompositor
= WaylandRegistryBind
<wl_subcompositor
>(
229 registry
, id
, &wl_subcompositor_interface
, 1);
230 wl_proxy_set_queue((struct wl_proxy
*)subcompositor
,
231 display
->GetEventQueue());
232 display
->SetSubcompositor(subcompositor
);
233 } else if (strcmp(interface
, "wp_viewporter") == 0) {
234 auto* viewporter
= WaylandRegistryBind
<wp_viewporter
>(
235 registry
, id
, &wp_viewporter_interface
, 1);
236 wl_proxy_set_queue((struct wl_proxy
*)viewporter
, display
->GetEventQueue());
237 display
->SetViewporter(viewporter
);
238 } else if (strcmp(interface
, "zwp_linux_dmabuf_v1") == 0 && version
> 2) {
239 auto* dmabuf
= WaylandRegistryBind
<zwp_linux_dmabuf_v1
>(
240 registry
, id
, &zwp_linux_dmabuf_v1_interface
, 3);
241 wl_proxy_set_queue((struct wl_proxy
*)dmabuf
, display
->GetEventQueue());
242 display
->SetDmabuf(dmabuf
);
243 } else if (strcmp(interface
, "xdg_activation_v1") == 0) {
244 auto* activation
= WaylandRegistryBind
<xdg_activation_v1
>(
245 registry
, id
, &xdg_activation_v1_interface
, 1);
246 display
->SetXdgActivation(activation
);
250 static void global_registry_remover(void* data
, wl_registry
* registry
,
253 static const struct wl_registry_listener registry_listener
= {
254 global_registry_handler
, global_registry_remover
};
256 bool nsWaylandDisplay::DispatchEventQueue() {
258 wl_display_dispatch_queue_pending(mDisplay
, mEventQueue
);
263 void nsWaylandDisplay::SyncEnd() {
264 wl_callback_destroy(mSyncCallback
);
265 mSyncCallback
= nullptr;
268 static void wayland_sync_callback(void* data
, struct wl_callback
* callback
,
270 auto display
= static_cast<nsWaylandDisplay
*>(data
);
274 static const struct wl_callback_listener sync_callback_listener
= {
275 .done
= wayland_sync_callback
};
277 void nsWaylandDisplay::SyncBegin() {
280 // Use wl_display_sync() to synchronize wayland events.
281 // See dri2_wl_swap_buffers_with_damage() from MESA
282 // or wl_display_roundtrip_queue() from wayland-client.
283 struct wl_display
* displayWrapper
=
284 static_cast<wl_display
*>(wl_proxy_create_wrapper((void*)mDisplay
));
285 if (!displayWrapper
) {
286 NS_WARNING("Failed to create wl_proxy wrapper!");
290 wl_proxy_set_queue((struct wl_proxy
*)displayWrapper
, mEventQueue
);
291 mSyncCallback
= wl_display_sync(displayWrapper
);
292 wl_proxy_wrapper_destroy((void*)displayWrapper
);
294 if (!mSyncCallback
) {
295 NS_WARNING("Failed to create wl_display_sync callback!");
299 wl_callback_add_listener(mSyncCallback
, &sync_callback_listener
, this);
300 wl_display_flush(mDisplay
);
303 void nsWaylandDisplay::QueueSyncBegin() {
304 RefPtr
<nsWaylandDisplay
> self(this);
305 NS_DispatchToMainThread(
306 NS_NewRunnableFunction("nsWaylandDisplay::QueueSyncBegin",
307 [self
]() -> void { self
->SyncBegin(); }));
310 void nsWaylandDisplay::WaitForSyncEnd() {
313 "nsWaylandDisplay::WaitForSyncEnd() can be called in main thread only!");
316 if (!mSyncCallback
) {
320 while (mSyncCallback
!= nullptr) {
321 // TODO: wl_display_dispatch_queue() should not be called while
322 // glib main loop is iterated at nsAppShell::ProcessNextNativeEvent().
323 if (wl_display_dispatch_queue(mDisplay
, mEventQueue
) == -1) {
324 NS_WARNING("wl_display_dispatch_queue failed!");
331 bool nsWaylandDisplay::Matches(wl_display
* aDisplay
) {
332 return mThreadId
== PR_GetCurrentThread() && aDisplay
== mDisplay
;
335 static void WlCrashHandler(const char* format
, va_list args
) {
336 MOZ_CRASH_UNSAFE(g_strdup_vprintf(format
, args
));
339 nsWaylandDisplay::nsWaylandDisplay(wl_display
* aDisplay
)
340 : mThreadId(PR_GetCurrentThread()),
342 mEventQueue(nullptr),
343 mDataDeviceManager(nullptr),
344 mCompositor(nullptr),
345 mSubcompositor(nullptr),
347 mSyncCallback(nullptr),
348 mPrimarySelectionDeviceManagerGtk(nullptr),
349 mPrimarySelectionDeviceManagerZwpV1(nullptr),
350 mIdleInhibitManager(nullptr),
351 mRelativePointerManager(nullptr),
352 mPointerConstraints(nullptr),
353 mViewporter(nullptr),
355 mXdgActivation(nullptr),
356 mExplicitSync(false) {
357 // GTK sets the log handler on display creation, thus we overwrite it here
358 // in a similar fashion
359 wl_log_set_handler_client(WlCrashHandler
);
361 wl_registry
* registry
= wl_display_get_registry(mDisplay
);
362 wl_registry_add_listener(registry
, ®istry_listener
, this);
363 if (!NS_IsMainThread()) {
364 mEventQueue
= wl_display_create_queue(mDisplay
);
365 wl_proxy_set_queue((struct wl_proxy
*)registry
, mEventQueue
);
368 wl_display_roundtrip_queue(mDisplay
, mEventQueue
);
369 wl_display_roundtrip_queue(mDisplay
, mEventQueue
);
371 wl_display_roundtrip(mDisplay
);
372 wl_display_roundtrip(mDisplay
);
374 wl_registry_destroy(registry
);
377 nsWaylandDisplay::~nsWaylandDisplay() {
379 wl_event_queue_destroy(mEventQueue
);
380 mEventQueue
= nullptr;
385 } // namespace widget
386 } // namespace mozilla