Bug 1751217 Part 3: Make HDR-capable macOS screens report 30 pixelDepth. r=mstange
[gecko.git] / widget / gtk / nsWaylandDisplay.cpp
blobcd3f8689665f9080399f8da3404fca58ab594d57
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/. */
8 #include "nsWaylandDisplay.h"
10 #include <dlfcn.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"
18 struct _GdkSeat;
19 typedef struct _GdkSeat GdkSeat;
21 namespace mozilla {
22 namespace widget {
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
30 // there.
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) {
39 if (display) {
40 display->DispatchEventQueue();
45 void WaylandDisplayRelease() {
46 StaticMutexAutoLock lock(gWaylandDisplayArrayWriteMutex);
47 for (auto& display : gWaylandDisplays) {
48 if (display) {
49 display = nullptr;
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) {
58 return nullptr;
61 // Search existing display connections for wl_display:thread combination.
62 for (auto& display : gWaylandDisplays) {
63 if (display && display->Matches(waylandDisplay)) {
64 return display;
68 StaticMutexAutoLock arrayLock(gWaylandDisplayArrayWriteMutex);
69 for (auto& display : gWaylandDisplays) {
70 if (display == nullptr) {
71 display = new nsWaylandDisplay(waylandDisplay);
72 return display;
76 MOZ_CRASH("There's too many wayland display conections!");
77 return nullptr;
80 wl_display* WaylandDisplayGetWLDisplay(GdkDisplay* aGdkDisplay) {
81 if (!aGdkDisplay) {
82 aGdkDisplay = gdk_display_get_default();
83 if (!GdkIsWaylandDisplay(aGdkDisplay)) {
84 return nullptr;
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();
108 if (!gdkDisplay) {
109 return nullptr;
112 static auto sGdkDisplayGetDefaultSeat = (GdkSeat * (*)(GdkDisplay*))
113 dlsym(RTLD_DEFAULT, "gdk_display_get_default_seat");
114 if (!sGdkDisplayGetDefaultSeat) {
115 return nullptr;
118 static auto sGdkWaylandSeatGetWlSeat = (struct wl_seat * (*)(GdkSeat*))
119 dlsym(RTLD_DEFAULT, "gdk_wayland_seat_get_wl_seat");
120 if (!sGdkWaylandSeatGetWlSeat) {
121 return nullptr;
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) {
158 mDmabuf = 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,
167 uint32_t version) {
168 auto* display = static_cast<nsWaylandDisplay*>(data);
169 if (!display) {
170 return;
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, &gtk_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") ==
193 0) {
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,
251 uint32_t id) {}
253 static const struct wl_registry_listener registry_listener = {
254 global_registry_handler, global_registry_remover};
256 bool nsWaylandDisplay::DispatchEventQueue() {
257 if (mEventQueue) {
258 wl_display_dispatch_queue_pending(mDisplay, mEventQueue);
260 return true;
263 void nsWaylandDisplay::SyncEnd() {
264 wl_callback_destroy(mSyncCallback);
265 mSyncCallback = nullptr;
268 static void wayland_sync_callback(void* data, struct wl_callback* callback,
269 uint32_t time) {
270 auto display = static_cast<nsWaylandDisplay*>(data);
271 display->SyncEnd();
274 static const struct wl_callback_listener sync_callback_listener = {
275 .done = wayland_sync_callback};
277 void nsWaylandDisplay::SyncBegin() {
278 WaitForSyncEnd();
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!");
287 return;
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!");
296 return;
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() {
311 MOZ_RELEASE_ASSERT(
312 NS_IsMainThread(),
313 "nsWaylandDisplay::WaitForSyncEnd() can be called in main thread only!");
315 // We're done here
316 if (!mSyncCallback) {
317 return;
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!");
325 SyncEnd();
326 return;
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()),
341 mDisplay(aDisplay),
342 mEventQueue(nullptr),
343 mDataDeviceManager(nullptr),
344 mCompositor(nullptr),
345 mSubcompositor(nullptr),
346 mShm(nullptr),
347 mSyncCallback(nullptr),
348 mPrimarySelectionDeviceManagerGtk(nullptr),
349 mPrimarySelectionDeviceManagerZwpV1(nullptr),
350 mIdleInhibitManager(nullptr),
351 mRelativePointerManager(nullptr),
352 mPointerConstraints(nullptr),
353 mViewporter(nullptr),
354 mDmabuf(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, &registry_listener, this);
363 if (!NS_IsMainThread()) {
364 mEventQueue = wl_display_create_queue(mDisplay);
365 wl_proxy_set_queue((struct wl_proxy*)registry, mEventQueue);
367 if (mEventQueue) {
368 wl_display_roundtrip_queue(mDisplay, mEventQueue);
369 wl_display_roundtrip_queue(mDisplay, mEventQueue);
370 } else {
371 wl_display_roundtrip(mDisplay);
372 wl_display_roundtrip(mDisplay);
374 wl_registry_destroy(registry);
377 nsWaylandDisplay::~nsWaylandDisplay() {
378 if (mEventQueue) {
379 wl_event_queue_destroy(mEventQueue);
380 mEventQueue = nullptr;
382 mDisplay = nullptr;
385 } // namespace widget
386 } // namespace mozilla