1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=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/. */
7 #include "nsWaylandDisplay.h"
11 #include "base/message_loop.h" // for MessageLoop
12 #include "base/task.h" // for NewRunnableMethod, etc
13 #include "mozilla/gfx/Logging.h" // for gfxCriticalNote
14 #include "mozilla/StaticMutex.h"
15 #include "mozilla/Array.h"
16 #include "mozilla/StaticPtr.h"
17 #include "mozilla/ThreadLocal.h"
18 #include "mozilla/StaticPrefs_widget.h"
19 #include "mozilla/Sprintf.h"
20 #include "WidgetUtilsGtk.h"
21 #include "nsGtkKeyUtils.h"
23 namespace mozilla::widget
{
25 static nsWaylandDisplay
* gWaylandDisplay
;
27 void WaylandDisplayRelease() {
28 MOZ_RELEASE_ASSERT(NS_IsMainThread(),
29 "WaylandDisplay can be released in main thread only!");
30 if (!gWaylandDisplay
) {
31 NS_WARNING("WaylandDisplayRelease(): Wayland display is missing!");
34 delete gWaylandDisplay
;
35 gWaylandDisplay
= nullptr;
38 wl_display
* WaylandDisplayGetWLDisplay() {
39 GdkDisplay
* disp
= gdk_display_get_default();
40 if (!GdkIsWaylandDisplay(disp
)) {
43 return gdk_wayland_display_get_wl_display(disp
);
46 nsWaylandDisplay
* WaylandDisplayGet() {
47 if (!gWaylandDisplay
) {
48 MOZ_RELEASE_ASSERT(NS_IsMainThread(),
49 "WaylandDisplay can be created in main thread only!");
50 wl_display
* waylandDisplay
= WaylandDisplayGetWLDisplay();
51 if (!waylandDisplay
) {
54 gWaylandDisplay
= new nsWaylandDisplay(waylandDisplay
);
56 return gWaylandDisplay
;
59 void nsWaylandDisplay::SetShm(wl_shm
* aShm
) { mShm
= aShm
; }
61 void nsWaylandDisplay::SetCompositor(wl_compositor
* aCompositor
) {
62 mCompositor
= aCompositor
;
65 void nsWaylandDisplay::SetSubcompositor(wl_subcompositor
* aSubcompositor
) {
66 mSubcompositor
= aSubcompositor
;
69 void nsWaylandDisplay::SetIdleInhibitManager(
70 zwp_idle_inhibit_manager_v1
* aIdleInhibitManager
) {
71 mIdleInhibitManager
= aIdleInhibitManager
;
74 void nsWaylandDisplay::SetViewporter(wp_viewporter
* aViewporter
) {
75 mViewporter
= aViewporter
;
78 void nsWaylandDisplay::SetRelativePointerManager(
79 zwp_relative_pointer_manager_v1
* aRelativePointerManager
) {
80 mRelativePointerManager
= aRelativePointerManager
;
83 void nsWaylandDisplay::SetPointerConstraints(
84 zwp_pointer_constraints_v1
* aPointerConstraints
) {
85 mPointerConstraints
= aPointerConstraints
;
88 void nsWaylandDisplay::SetDmabuf(zwp_linux_dmabuf_v1
* aDmabuf
) {
92 void nsWaylandDisplay::SetXdgActivation(xdg_activation_v1
* aXdgActivation
) {
93 mXdgActivation
= aXdgActivation
;
96 static void global_registry_handler(void* data
, wl_registry
* registry
,
97 uint32_t id
, const char* interface
,
99 auto* display
= static_cast<nsWaylandDisplay
*>(data
);
104 nsDependentCString
iface(interface
);
105 if (iface
.EqualsLiteral("wl_shm")) {
106 auto* shm
= WaylandRegistryBind
<wl_shm
>(registry
, id
, &wl_shm_interface
, 1);
107 display
->SetShm(shm
);
108 } else if (iface
.EqualsLiteral("zwp_idle_inhibit_manager_v1")) {
109 auto* idle_inhibit_manager
=
110 WaylandRegistryBind
<zwp_idle_inhibit_manager_v1
>(
111 registry
, id
, &zwp_idle_inhibit_manager_v1_interface
, 1);
112 display
->SetIdleInhibitManager(idle_inhibit_manager
);
113 } else if (iface
.EqualsLiteral("zwp_relative_pointer_manager_v1")) {
114 auto* relative_pointer_manager
=
115 WaylandRegistryBind
<zwp_relative_pointer_manager_v1
>(
116 registry
, id
, &zwp_relative_pointer_manager_v1_interface
, 1);
117 display
->SetRelativePointerManager(relative_pointer_manager
);
118 } else if (iface
.EqualsLiteral("zwp_pointer_constraints_v1")) {
119 auto* pointer_constraints
= WaylandRegistryBind
<zwp_pointer_constraints_v1
>(
120 registry
, id
, &zwp_pointer_constraints_v1_interface
, 1);
121 display
->SetPointerConstraints(pointer_constraints
);
122 } else if (iface
.EqualsLiteral("wl_compositor")) {
123 // Requested wl_compositor version 4 as we need wl_surface_damage_buffer().
124 auto* compositor
= WaylandRegistryBind
<wl_compositor
>(
125 registry
, id
, &wl_compositor_interface
, 4);
126 display
->SetCompositor(compositor
);
127 } else if (iface
.EqualsLiteral("wl_subcompositor")) {
128 auto* subcompositor
= WaylandRegistryBind
<wl_subcompositor
>(
129 registry
, id
, &wl_subcompositor_interface
, 1);
130 display
->SetSubcompositor(subcompositor
);
131 } else if (iface
.EqualsLiteral("wp_viewporter")) {
132 auto* viewporter
= WaylandRegistryBind
<wp_viewporter
>(
133 registry
, id
, &wp_viewporter_interface
, 1);
134 display
->SetViewporter(viewporter
);
135 } else if (iface
.EqualsLiteral("zwp_linux_dmabuf_v1") && version
> 2) {
136 auto* dmabuf
= WaylandRegistryBind
<zwp_linux_dmabuf_v1
>(
137 registry
, id
, &zwp_linux_dmabuf_v1_interface
, 3);
138 display
->SetDmabuf(dmabuf
);
139 } else if (iface
.EqualsLiteral("xdg_activation_v1")) {
140 auto* activation
= WaylandRegistryBind
<xdg_activation_v1
>(
141 registry
, id
, &xdg_activation_v1_interface
, 1);
142 display
->SetXdgActivation(activation
);
143 } else if (iface
.EqualsLiteral("wl_seat")) {
144 // Install keyboard handlers for main thread only
146 WaylandRegistryBind
<wl_seat
>(registry
, id
, &wl_seat_interface
, 1);
147 KeymapWrapper::SetSeat(seat
, id
);
148 } else if (iface
.EqualsLiteral("wp_fractional_scale_manager_v1")) {
149 auto* manager
= WaylandRegistryBind
<wp_fractional_scale_manager_v1
>(
150 registry
, id
, &wp_fractional_scale_manager_v1_interface
, 1);
151 display
->SetFractionalScaleManager(manager
);
152 } else if (iface
.EqualsLiteral("gtk_primary_selection_device_manager") ||
153 iface
.EqualsLiteral("zwp_primary_selection_device_manager_v1")) {
154 display
->EnablePrimarySelection();
158 static void global_registry_remover(void* data
, wl_registry
* registry
,
160 KeymapWrapper::ClearSeat(id
);
163 static const struct wl_registry_listener registry_listener
= {
164 global_registry_handler
, global_registry_remover
};
166 nsWaylandDisplay::~nsWaylandDisplay() {}
168 static void WlLogHandler(const char* format
, va_list args
) {
170 VsprintfLiteral(error
, format
, args
);
171 gfxCriticalNote
<< "Wayland protocol error: " << error
;
173 // See Bug 1826583 and Bug 1844653 for reference.
174 // "warning: queue %p destroyed while proxies still attached" and variants
175 // like "zwp_linux_dmabuf_feedback_v1@%d still attached" are exceptions on
176 // Wayland and non-fatal. They are triggered in certain versions of Mesa or
177 // the proprietary Nvidia driver and we don't want to crash because of them.
178 if (strstr(error
, "still attached")) {
182 MOZ_CRASH_UNSAFE(error
);
185 nsWaylandDisplay::nsWaylandDisplay(wl_display
* aDisplay
)
186 : mThreadId(PR_GetCurrentThread()), mDisplay(aDisplay
) {
187 // GTK sets the log handler on display creation, thus we overwrite it here
188 // in a similar fashion
189 wl_log_set_handler_client(WlLogHandler
);
191 mRegistry
= wl_display_get_registry(mDisplay
);
192 wl_registry_add_listener(mRegistry
, ®istry_listener
, this);
193 wl_display_roundtrip(mDisplay
);
194 wl_display_roundtrip(mDisplay
);
196 // Check we have critical Wayland interfaces.
197 // Missing ones indicates a compositor bug and we can't continue.
198 MOZ_DIAGNOSTIC_ASSERT(GetShm(), "We're missing shm interface!");
199 MOZ_DIAGNOSTIC_ASSERT(GetCompositor(), "We're missing compositor interface!");
200 MOZ_DIAGNOSTIC_ASSERT(GetSubcompositor(),
201 "We're missing subcompositor interface!");
204 } // namespace mozilla::widget