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
) {
33 delete gWaylandDisplay
;
34 gWaylandDisplay
= nullptr;
37 wl_display
* WaylandDisplayGetWLDisplay() {
38 GdkDisplay
* disp
= gdk_display_get_default();
39 if (!GdkIsWaylandDisplay(disp
)) {
42 return gdk_wayland_display_get_wl_display(disp
);
45 nsWaylandDisplay
* WaylandDisplayGet() {
46 if (!gWaylandDisplay
) {
47 MOZ_RELEASE_ASSERT(NS_IsMainThread(),
48 "WaylandDisplay can be created in main thread only!");
49 wl_display
* waylandDisplay
= WaylandDisplayGetWLDisplay();
50 if (!waylandDisplay
) {
53 gWaylandDisplay
= new nsWaylandDisplay(waylandDisplay
);
55 return gWaylandDisplay
;
58 void nsWaylandDisplay::SetShm(wl_shm
* aShm
) { mShm
= aShm
; }
60 void nsWaylandDisplay::SetCompositor(wl_compositor
* aCompositor
) {
61 mCompositor
= aCompositor
;
64 void nsWaylandDisplay::SetSubcompositor(wl_subcompositor
* aSubcompositor
) {
65 mSubcompositor
= aSubcompositor
;
68 void nsWaylandDisplay::SetIdleInhibitManager(
69 zwp_idle_inhibit_manager_v1
* aIdleInhibitManager
) {
70 mIdleInhibitManager
= aIdleInhibitManager
;
73 void nsWaylandDisplay::SetViewporter(wp_viewporter
* aViewporter
) {
74 mViewporter
= aViewporter
;
77 void nsWaylandDisplay::SetRelativePointerManager(
78 zwp_relative_pointer_manager_v1
* aRelativePointerManager
) {
79 mRelativePointerManager
= aRelativePointerManager
;
82 void nsWaylandDisplay::SetPointerConstraints(
83 zwp_pointer_constraints_v1
* aPointerConstraints
) {
84 mPointerConstraints
= aPointerConstraints
;
87 void nsWaylandDisplay::SetDmabuf(zwp_linux_dmabuf_v1
* aDmabuf
) {
91 void nsWaylandDisplay::SetXdgActivation(xdg_activation_v1
* aXdgActivation
) {
92 mXdgActivation
= aXdgActivation
;
95 void nsWaylandDisplay::SetXdgDbusAnnotationManager(
96 xdg_dbus_annotation_manager_v1
* aXdgDbusAnnotationManager
) {
97 mXdgDbusAnnotationManager
= aXdgDbusAnnotationManager
;
100 static void global_registry_handler(void* data
, wl_registry
* registry
,
101 uint32_t id
, const char* interface
,
103 auto* display
= static_cast<nsWaylandDisplay
*>(data
);
108 nsDependentCString
iface(interface
);
109 if (iface
.EqualsLiteral("wl_shm")) {
110 auto* shm
= WaylandRegistryBind
<wl_shm
>(registry
, id
, &wl_shm_interface
, 1);
111 display
->SetShm(shm
);
112 } else if (iface
.EqualsLiteral("zwp_idle_inhibit_manager_v1")) {
113 auto* idle_inhibit_manager
=
114 WaylandRegistryBind
<zwp_idle_inhibit_manager_v1
>(
115 registry
, id
, &zwp_idle_inhibit_manager_v1_interface
, 1);
116 display
->SetIdleInhibitManager(idle_inhibit_manager
);
117 } else if (iface
.EqualsLiteral("zwp_relative_pointer_manager_v1")) {
118 auto* relative_pointer_manager
=
119 WaylandRegistryBind
<zwp_relative_pointer_manager_v1
>(
120 registry
, id
, &zwp_relative_pointer_manager_v1_interface
, 1);
121 display
->SetRelativePointerManager(relative_pointer_manager
);
122 } else if (iface
.EqualsLiteral("zwp_pointer_constraints_v1")) {
123 auto* pointer_constraints
= WaylandRegistryBind
<zwp_pointer_constraints_v1
>(
124 registry
, id
, &zwp_pointer_constraints_v1_interface
, 1);
125 display
->SetPointerConstraints(pointer_constraints
);
126 } else if (iface
.EqualsLiteral("wl_compositor")) {
127 // Requested wl_compositor version 4 as we need wl_surface_damage_buffer().
128 auto* compositor
= WaylandRegistryBind
<wl_compositor
>(
129 registry
, id
, &wl_compositor_interface
, 4);
130 display
->SetCompositor(compositor
);
131 } else if (iface
.EqualsLiteral("wl_subcompositor")) {
132 auto* subcompositor
= WaylandRegistryBind
<wl_subcompositor
>(
133 registry
, id
, &wl_subcompositor_interface
, 1);
134 display
->SetSubcompositor(subcompositor
);
135 } else if (iface
.EqualsLiteral("wp_viewporter")) {
136 auto* viewporter
= WaylandRegistryBind
<wp_viewporter
>(
137 registry
, id
, &wp_viewporter_interface
, 1);
138 display
->SetViewporter(viewporter
);
139 } else if (iface
.EqualsLiteral("zwp_linux_dmabuf_v1") && version
> 2) {
140 auto* dmabuf
= WaylandRegistryBind
<zwp_linux_dmabuf_v1
>(
141 registry
, id
, &zwp_linux_dmabuf_v1_interface
, 3);
142 display
->SetDmabuf(dmabuf
);
143 } else if (iface
.EqualsLiteral("xdg_activation_v1")) {
144 auto* activation
= WaylandRegistryBind
<xdg_activation_v1
>(
145 registry
, id
, &xdg_activation_v1_interface
, 1);
146 display
->SetXdgActivation(activation
);
147 } else if (iface
.EqualsLiteral("xdg_dbus_annotation_manager_v1")) {
148 auto* annotationManager
=
149 WaylandRegistryBind
<xdg_dbus_annotation_manager_v1
>(
150 registry
, id
, &xdg_dbus_annotation_manager_v1_interface
, 1);
151 display
->SetXdgDbusAnnotationManager(annotationManager
);
152 } else if (iface
.EqualsLiteral("wl_seat")) {
153 // Install keyboard handlers for main thread only
155 WaylandRegistryBind
<wl_seat
>(registry
, id
, &wl_seat_interface
, 1);
156 KeymapWrapper::SetSeat(seat
, id
);
157 } else if (iface
.EqualsLiteral("wp_fractional_scale_manager_v1")) {
158 auto* manager
= WaylandRegistryBind
<wp_fractional_scale_manager_v1
>(
159 registry
, id
, &wp_fractional_scale_manager_v1_interface
, 1);
160 display
->SetFractionalScaleManager(manager
);
161 } else if (iface
.EqualsLiteral("gtk_primary_selection_device_manager") ||
162 iface
.EqualsLiteral("zwp_primary_selection_device_manager_v1")) {
163 display
->EnablePrimarySelection();
167 static void global_registry_remover(void* data
, wl_registry
* registry
,
169 KeymapWrapper::ClearSeat(id
);
172 static const struct wl_registry_listener registry_listener
= {
173 global_registry_handler
, global_registry_remover
};
175 nsWaylandDisplay::~nsWaylandDisplay() {}
177 static void WlLogHandler(const char* format
, va_list args
) {
179 VsprintfLiteral(error
, format
, args
);
180 gfxCriticalNote
<< "Wayland protocol error: " << error
;
182 // See Bug 1826583 and Bug 1844653 for reference.
183 // "warning: queue %p destroyed while proxies still attached" and variants
184 // like "zwp_linux_dmabuf_feedback_v1@%d still attached" are exceptions on
185 // Wayland and non-fatal. They are triggered in certain versions of Mesa or
186 // the proprietary Nvidia driver and we don't want to crash because of them.
187 if (strstr(error
, "still attached")) {
191 MOZ_CRASH_UNSAFE(error
);
194 nsWaylandDisplay::nsWaylandDisplay(wl_display
* aDisplay
)
195 : mThreadId(PR_GetCurrentThread()), mDisplay(aDisplay
) {
196 // GTK sets the log handler on display creation, thus we overwrite it here
197 // in a similar fashion
198 wl_log_set_handler_client(WlLogHandler
);
200 mRegistry
= wl_display_get_registry(mDisplay
);
201 wl_registry_add_listener(mRegistry
, ®istry_listener
, this);
202 wl_display_roundtrip(mDisplay
);
203 wl_display_roundtrip(mDisplay
);
205 // Check we have critical Wayland interfaces.
206 // Missing ones indicates a compositor bug and we can't continue.
207 MOZ_DIAGNOSTIC_ASSERT(GetShm(), "We're missing shm interface!");
208 MOZ_DIAGNOSTIC_ASSERT(GetCompositor(), "We're missing compositor interface!");
209 MOZ_DIAGNOSTIC_ASSERT(GetSubcompositor(),
210 "We're missing subcompositor interface!");
213 } // namespace mozilla::widget