1 /* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim:expandtab:shiftwidth=2:tabstop=2:
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 * MozContainerWayland is a wrapper over MozContainer which provides
9 * wl_surface for MozContainer widget.
11 * The widget scheme looks like:
13 * ---------------------------------------------------------
14 * | mShell Gtk widget (contains wl_surface owned by Gtk+) |
16 * | --------------------------------------------------- |
17 * | | mContainer (contains wl_surface owned by Gtk+) | |
19 * | | --------------------------------------------- | |
20 * | | | wl_subsurface (attached to wl_surface | | |
21 * | | | of mContainer) | | |
24 * | | --------------------------------------------- | |
25 * | --------------------------------------------------- |
26 * ---------------------------------------------------------
28 * We draw to wl_subsurface owned by MozContainerWayland.
29 * We need to wait until wl_surface of mContainer is created
30 * and then we create and attach our wl_subsurface to it.
32 * First wl_subsurface creation has these steps:
34 * 1) moz_container_wayland_size_allocate() handler is called when
35 * mContainer size/position is known.
36 * It calls moz_container_wayland_surface_create_locked(), registers
37 * a frame callback handler
38 * moz_container_wayland_frame_callback_handler().
40 * 2) moz_container_wayland_frame_callback_handler() is called
41 * when wl_surface owned by mozContainer is ready.
42 * We call initial_draw_cbs() handler and we can create our wl_subsurface
43 * on top of wl_surface owned by mozContainer.
45 * When MozContainer hides/show again, moz_container_wayland_size_allocate()
46 * handler may not be called as MozContainer size is set. So after first
47 * show/hide sequence use moz_container_wayland_map_event() to create
48 * wl_subsurface of MozContainer.
51 #include "MozContainer.h"
56 #include <wayland-egl.h>
58 #include "mozilla/gfx/gfxVars.h"
59 #include "mozilla/StaticPrefs_widget.h"
60 #include "nsGtkUtils.h"
61 #include "nsWaylandDisplay.h"
62 #include "base/task.h"
66 # include "mozilla/Logging.h"
67 # include "nsTArray.h"
69 # include "nsWindow.h"
70 extern mozilla::LazyLogModule gWidgetWaylandLog
;
71 extern mozilla::LazyLogModule gWidgetLog
;
72 # define LOGWAYLAND(...) \
73 MOZ_LOG(gWidgetWaylandLog, mozilla::LogLevel::Debug, (__VA_ARGS__))
74 # define LOGCONTAINER(...) \
75 MOZ_LOG(gWidgetLog, mozilla::LogLevel::Debug, (__VA_ARGS__))
77 # define LOGWAYLAND(...)
78 # define LOGCONTAINER(...)
79 #endif /* MOZ_LOGGING */
81 using namespace mozilla
;
82 using namespace mozilla::widget
;
84 static bool moz_container_wayland_surface_create_locked(
85 const MutexAutoLock
& aProofOfLock
, MozContainer
* container
);
86 static void moz_container_wayland_set_opaque_region_locked(
87 const MutexAutoLock
& aProofOfLock
, MozContainer
* container
,
88 const LayoutDeviceIntRegion
&);
90 // Lock mozcontainer and get wayland surface of it. You need to pair with
91 // moz_container_wayland_surface_unlock() even
92 // if moz_container_wayland_surface_lock() fails and returns nullptr.
93 static struct wl_surface
* moz_container_wayland_surface_lock(
94 MozContainer
* container
);
95 static void moz_container_wayland_surface_unlock(MozContainer
* container
,
96 struct wl_surface
** surface
);
98 MozContainerSurfaceLock::MozContainerSurfaceLock(MozContainer
* aContainer
) {
99 mContainer
= aContainer
;
100 mSurface
= moz_container_wayland_surface_lock(aContainer
);
102 MozContainerSurfaceLock::~MozContainerSurfaceLock() {
103 moz_container_wayland_surface_unlock(mContainer
, &mSurface
);
105 struct wl_surface
* MozContainerSurfaceLock::GetSurface() { return mSurface
; }
107 // Invalidate gtk wl_surface to commit changes to wl_subsurface.
108 // wl_subsurface changes are effective when parent surface is commited.
109 static void moz_container_wayland_invalidate(MozContainer
* container
) {
110 LOGWAYLAND("moz_container_wayland_invalidate [%p]\n",
111 (void*)moz_container_get_nsWindow(container
));
113 GdkWindow
* window
= gtk_widget_get_window(GTK_WIDGET(container
));
115 LOGWAYLAND(" Failed - missing GdkWindow!\n");
118 gdk_window_invalidate_rect(window
, nullptr, true);
121 // Route input to parent wl_surface owned by Gtk+ so we get input
123 static void moz_container_clear_input_region(MozContainer
* container
) {
124 struct wl_compositor
* compositor
= WaylandDisplayGet()->GetCompositor();
125 MozContainerWayland
* wl_container
= &container
->data
.wl_container
;
126 wl_region
* region
= wl_compositor_create_region(compositor
);
127 wl_surface_set_input_region(wl_container
->surface
, region
);
128 wl_region_destroy(region
);
131 static void moz_container_wayland_move_locked(const MutexAutoLock
& aProofOfLock
,
132 MozContainer
* container
, int dx
,
134 LOGCONTAINER("moz_container_wayland_move [%p] %d,%d\n",
135 (void*)moz_container_get_nsWindow(container
), dx
, dy
);
137 MozContainerWayland
* wl_container
= &container
->data
.wl_container
;
138 if (!wl_container
->subsurface
|| (wl_container
->subsurface_dx
== dx
&&
139 wl_container
->subsurface_dy
== dy
)) {
143 wl_container
->subsurface_dx
= dx
;
144 wl_container
->subsurface_dy
= dy
;
145 wl_subsurface_set_position(wl_container
->subsurface
,
146 wl_container
->subsurface_dx
,
147 wl_container
->subsurface_dy
);
150 // This is called from layout/compositor code only with
151 // size equal to GL rendering context.
153 // Return false if scale factor doesn't match buffer size.
154 // We need to skip painting in such case do avoid Wayland compositor freaking.
155 bool moz_container_wayland_egl_window_set_size(MozContainer
* container
,
156 nsIntSize aSize
, int aScale
) {
157 MozContainerWayland
* wl_container
= &container
->data
.wl_container
;
158 MutexAutoLock
lock(wl_container
->container_lock
);
160 // We may be called after unmap so we're missing egl window completelly.
161 // In such case don't return false which would block compositor.
162 // We return true here and don't block flush WebRender queue.
163 // We'll be repainted if our window become visible again anyway.
164 if (!wl_container
->eglwindow
) {
168 if (wl_container
->buffer_scale
!= aScale
) {
169 moz_container_wayland_set_scale_factor_locked(lock
, container
, aScale
);
172 /* Enable for size changes logging
174 "moz_container_wayland_egl_window_set_size [%p] %d x %d scale %d "
175 "(unscaled %d x %d)",
176 (void*)moz_container_get_nsWindow(container), aSize.width, aSize.height,
177 aScale, aSize.width / aScale, aSize.height / aScale);
179 wl_egl_window_resize(wl_container
->eglwindow
, aSize
.width
, aSize
.height
, 0,
182 return moz_container_wayland_size_matches_scale_factor_locked(
183 lock
, container
, aSize
.width
, aSize
.height
);
186 void moz_container_wayland_add_initial_draw_callback_locked(
187 MozContainer
* container
, const std::function
<void(void)>& initial_draw_cb
) {
188 MozContainerWayland
* wl_container
= &container
->data
.wl_container
;
190 if (wl_container
->ready_to_draw
&& !wl_container
->surface
) {
192 "moz_container_wayland_add_or_fire_initial_draw_callback:"
193 " ready to draw without wayland surface!");
195 MOZ_DIAGNOSTIC_ASSERT(!wl_container
->ready_to_draw
|| !wl_container
->surface
);
196 wl_container
->initial_draw_cbs
.push_back(initial_draw_cb
);
199 void moz_container_wayland_add_or_fire_initial_draw_callback(
200 MozContainer
* container
, const std::function
<void(void)>& initial_draw_cb
) {
201 MozContainerWayland
* wl_container
= &container
->data
.wl_container
;
203 MutexAutoLock
lock(wl_container
->container_lock
);
204 if (wl_container
->ready_to_draw
&& !wl_container
->surface
) {
206 "moz_container_wayland_add_or_fire_initial_draw_callback: ready to "
208 "without wayland surface!");
210 if (!wl_container
->ready_to_draw
|| !wl_container
->surface
) {
211 wl_container
->initial_draw_cbs
.push_back(initial_draw_cb
);
216 // We're ready to draw as
217 // wl_container->ready_to_draw && wl_container->surface
218 // call the callback directly instead of store them.
222 static void moz_container_wayland_clear_initial_draw_callback_locked(
223 const MutexAutoLock
& aProofOfLock
, MozContainer
* container
) {
224 MozContainerWayland
* wl_container
= &container
->data
.wl_container
;
225 MozClearPointer(wl_container
->frame_callback_handler
, wl_callback_destroy
);
226 wl_container
->initial_draw_cbs
.clear();
229 void moz_container_wayland_clear_initial_draw_callback(
230 MozContainer
* container
) {
231 MutexAutoLock
lock(container
->data
.wl_container
.container_lock
);
232 moz_container_wayland_clear_initial_draw_callback_locked(lock
, container
);
235 static void moz_container_wayland_frame_callback_handler(
236 void* data
, struct wl_callback
* callback
, uint32_t time
) {
237 MozContainerWayland
* wl_container
= MOZ_WL_CONTAINER(data
);
240 "%s [%p] frame_callback_handler %p ready_to_draw %d (set to true)"
241 " initial_draw callback %zd\n",
242 __FUNCTION__
, (void*)moz_container_get_nsWindow(MOZ_CONTAINER(data
)),
243 (void*)wl_container
->frame_callback_handler
, wl_container
->ready_to_draw
,
244 wl_container
->initial_draw_cbs
.size());
246 std::vector
<std::function
<void(void)>> cbs
;
248 // Protect mozcontainer internals changes by container_lock.
249 MutexAutoLock
lock(wl_container
->container_lock
);
250 MozClearPointer(wl_container
->frame_callback_handler
, wl_callback_destroy
);
251 // It's possible that container is already unmapped so quit in such case.
252 if (!wl_container
->surface
) {
253 LOGWAYLAND(" container is unmapped, quit.");
254 if (!wl_container
->initial_draw_cbs
.empty()) {
255 NS_WARNING("Unmapping MozContainer with active draw callback!");
256 wl_container
->initial_draw_cbs
.clear();
260 if (wl_container
->ready_to_draw
) {
263 wl_container
->ready_to_draw
= true;
264 cbs
= std::move(wl_container
->initial_draw_cbs
);
267 // Call the callbacks registered by
268 // moz_container_wayland_add_or_fire_initial_draw_callback().
269 // and we can't do that under mozcontainer lock.
270 for (auto const& cb
: cbs
) {
275 static const struct wl_callback_listener moz_container_frame_listener
= {
276 moz_container_wayland_frame_callback_handler
};
278 static void after_frame_clock_after_paint(GdkFrameClock
* clock
,
279 MozContainer
* container
) {
280 MozContainerSurfaceLock
lock(container
);
281 struct wl_surface
* surface
= lock
.GetSurface();
283 wl_surface_commit(surface
);
287 static bool moz_gdk_wayland_window_add_frame_callback_surface_locked(
288 const MutexAutoLock
& aProofOfLock
, MozContainer
* container
) {
289 static auto sGdkWaylandWindowAddCallbackSurface
=
290 (void (*)(GdkWindow
*, struct wl_surface
*))dlsym(
291 RTLD_DEFAULT
, "gdk_wayland_window_add_frame_callback_surface");
293 if (!StaticPrefs::widget_wayland_opaque_region_enabled_AtStartup() ||
294 !sGdkWaylandWindowAddCallbackSurface
) {
298 GdkWindow
* window
= gtk_widget_get_window(GTK_WIDGET(container
));
299 MozContainerWayland
* wl_container
= &container
->data
.wl_container
;
301 sGdkWaylandWindowAddCallbackSurface(window
, wl_container
->surface
);
303 GdkFrameClock
* frame_clock
= gdk_window_get_frame_clock(window
);
304 g_signal_connect_after(frame_clock
, "after-paint",
305 G_CALLBACK(after_frame_clock_after_paint
), container
);
309 static void moz_gdk_wayland_window_remove_frame_callback_surface_locked(
310 const MutexAutoLock
& aProofOfLock
, MozContainer
* container
) {
311 static auto sGdkWaylandWindowRemoveCallbackSurface
=
312 (void (*)(GdkWindow
*, struct wl_surface
*))dlsym(
313 RTLD_DEFAULT
, "gdk_wayland_window_remove_frame_callback_surface");
315 if (!sGdkWaylandWindowRemoveCallbackSurface
) {
319 GdkWindow
* window
= gtk_widget_get_window(GTK_WIDGET(container
));
320 MozContainerWayland
* wl_container
= &container
->data
.wl_container
;
322 if (wl_container
->surface
) {
323 sGdkWaylandWindowRemoveCallbackSurface(window
, wl_container
->surface
);
326 GdkFrameClock
* frame_clock
= gdk_window_get_frame_clock(window
);
327 g_signal_handlers_disconnect_by_func(
328 frame_clock
, FuncToGpointer(after_frame_clock_after_paint
), container
);
331 void moz_container_wayland_unmap(GtkWidget
* widget
) {
332 g_return_if_fail(IS_MOZ_CONTAINER(widget
));
334 // Unmap MozContainer first so we can remove our resources
335 moz_container_unmap(widget
);
337 MozContainer
* container
= MOZ_CONTAINER(widget
);
338 MozContainerWayland
* wl_container
= &container
->data
.wl_container
;
339 MutexAutoLock
lock(wl_container
->container_lock
);
341 LOGCONTAINER("%s [%p]\n", __FUNCTION__
,
342 (void*)moz_container_get_nsWindow(container
));
344 moz_container_wayland_clear_initial_draw_callback_locked(lock
, container
);
346 if (wl_container
->opaque_region_used
) {
347 moz_gdk_wayland_window_remove_frame_callback_surface_locked(lock
,
350 if (wl_container
->commit_to_parent
) {
351 wl_container
->surface
= nullptr;
354 MozClearPointer(wl_container
->eglwindow
, wl_egl_window_destroy
);
355 MozClearPointer(wl_container
->subsurface
, wl_subsurface_destroy
);
356 MozClearPointer(wl_container
->surface
, wl_surface_destroy
);
357 MozClearPointer(wl_container
->viewport
, wp_viewport_destroy
);
358 MozClearPointer(wl_container
->fractional_scale
,
359 wp_fractional_scale_v1_destroy
);
361 wl_container
->ready_to_draw
= false;
362 wl_container
->buffer_scale
= 1;
363 wl_container
->current_fractional_scale
= 0.0;
366 gboolean
moz_container_wayland_map_event(GtkWidget
* widget
,
367 GdkEventAny
* event
) {
368 MozContainerWayland
* wl_container
= &MOZ_CONTAINER(widget
)->data
.wl_container
;
370 LOGCONTAINER("%s [%p]\n", __FUNCTION__
,
371 (void*)moz_container_get_nsWindow(MOZ_CONTAINER(widget
)));
373 // Return early if we're not mapped. Gtk may send bogus map_event signal
374 // to unmapped widgets (see Bug 1875369).
375 if (!gtk_widget_get_mapped(widget
)) {
379 // Make sure we're on main thread as we can't lock mozContainer here
380 // due to moz_container_wayland_add_or_fire_initial_draw_callback() call
382 MOZ_DIAGNOSTIC_ASSERT(NS_IsMainThread());
384 // Set waiting_to_show flag. It means the mozcontainer is cofigured/mapped
385 // and it's supposed to be visible. *But* it's really visible when we get
386 // moz_container_wayland_add_or_fire_initial_draw_callback() which means
387 // wayland compositor makes it live.
388 wl_container
->waiting_to_show
= true;
389 MozContainer
* container
= MOZ_CONTAINER(widget
);
390 moz_container_wayland_add_or_fire_initial_draw_callback(
391 container
, [container
]() -> void {
393 "[%p] moz_container_wayland_add_or_fire_initial_draw_callback set "
395 moz_container_get_nsWindow(container
));
396 moz_container_wayland_clear_waiting_to_show_flag(container
);
399 MutexAutoLock
lock(wl_container
->container_lock
);
401 // Don't create wl_subsurface in map_event when it's already created or
402 // if we create it for the first time.
403 if (wl_container
->ready_to_draw
|| wl_container
->before_first_size_alloc
) {
407 if (!wl_container
->surface
) {
408 if (!moz_container_wayland_surface_create_locked(lock
,
409 MOZ_CONTAINER(widget
))) {
414 nsWindow
* window
= moz_container_get_nsWindow(MOZ_CONTAINER(widget
));
415 moz_container_wayland_set_scale_factor_locked(lock
, MOZ_CONTAINER(widget
),
416 window
->GdkCeiledScaleFactor());
417 if (container
->data
.wl_container
.opaque_region_needs_updates
) {
418 moz_container_wayland_set_opaque_region_locked(lock
, container
,
419 window
->GetOpaqueRegion());
421 moz_container_clear_input_region(MOZ_CONTAINER(widget
));
422 moz_container_wayland_invalidate(MOZ_CONTAINER(widget
));
426 void moz_container_wayland_size_allocate(GtkWidget
* widget
,
427 GtkAllocation
* allocation
) {
428 GtkAllocation tmp_allocation
;
430 g_return_if_fail(IS_MOZ_CONTAINER(widget
));
432 LOGCONTAINER("moz_container_wayland_size_allocate [%p] %d,%d -> %d x %d\n",
433 (void*)moz_container_get_nsWindow(MOZ_CONTAINER(widget
)),
434 allocation
->x
, allocation
->y
, allocation
->width
,
437 /* short circuit if you can */
438 gtk_widget_get_allocation(widget
, &tmp_allocation
);
439 if (tmp_allocation
.x
== allocation
->x
&& tmp_allocation
.y
== allocation
->y
&&
440 tmp_allocation
.width
== allocation
->width
&&
441 tmp_allocation
.height
== allocation
->height
) {
444 gtk_widget_set_allocation(widget
, allocation
);
446 if (gtk_widget_get_has_window(widget
) && gtk_widget_get_realized(widget
)) {
447 gdk_window_move_resize(gtk_widget_get_window(widget
), allocation
->x
,
448 allocation
->y
, allocation
->width
,
450 // We need to position our subsurface according to GdkWindow
451 // when offset changes (GdkWindow is maximized for instance).
452 // see gtk-clutter-embed.c for reference.
453 MozContainer
* container
= MOZ_CONTAINER(widget
);
454 MutexAutoLock
lock(container
->data
.wl_container
.container_lock
);
455 if (!container
->data
.wl_container
.surface
) {
456 if (!moz_container_wayland_surface_create_locked(lock
, container
)) {
460 nsWindow
* window
= moz_container_get_nsWindow(container
);
461 moz_container_wayland_set_scale_factor_locked(
462 lock
, container
, window
->GdkCeiledScaleFactor());
463 if (container
->data
.wl_container
.opaque_region_needs_updates
) {
464 moz_container_wayland_set_opaque_region_locked(lock
, container
,
465 window
->GetOpaqueRegion());
467 moz_container_wayland_move_locked(lock
, container
, allocation
->x
,
469 moz_container_clear_input_region(container
);
470 moz_container_wayland_invalidate(MOZ_CONTAINER(widget
));
471 container
->data
.wl_container
.before_first_size_alloc
= false;
475 static void moz_container_wayland_set_opaque_region_locked(
476 const MutexAutoLock
& aProofOfLock
, MozContainer
* container
,
477 const LayoutDeviceIntRegion
& aRegion
) {
478 MozContainerWayland
* wl_container
= &container
->data
.wl_container
;
479 MOZ_ASSERT(wl_container
->opaque_region_needs_updates
);
480 if (!wl_container
->surface
) {
484 wl_container
->opaque_region_needs_updates
= false;
485 if (!wl_container
->opaque_region_used
) {
490 wl_compositor_create_region(WaylandDisplayGet()->GetCompositor());
491 for (auto iter
= aRegion
.RectIter(); !iter
.Done(); iter
.Next()) {
492 const auto& rect
= iter
.Get();
493 wl_region_add(region
, rect
.x
, rect
.y
, rect
.Width(), rect
.Height());
495 wl_surface_set_opaque_region(wl_container
->surface
, region
);
496 wl_region_destroy(region
);
499 static void moz_container_wayland_surface_set_scale_locked(
500 const MutexAutoLock
& aProofOfLock
, MozContainerWayland
* wl_container
,
502 if (!wl_container
->surface
) {
505 if (wl_container
->buffer_scale
== scale
) {
509 LOGCONTAINER("%s scale %d\n", __FUNCTION__
, scale
);
511 // There is a chance that the attached wl_buffer has not yet been doubled
512 // on the main thread when scale factor changed to 2. This leads to
513 // crash with the following message:
514 // Buffer size (AxB) must be an integer multiple of the buffer_scale (2)
515 // Removing the possibly wrong wl_buffer to prevent that crash:
516 wl_surface_attach(wl_container
->surface
, nullptr, 0, 0);
517 wl_surface_set_buffer_scale(wl_container
->surface
, scale
);
518 wl_container
->buffer_scale
= scale
;
521 static void fractional_scale_handle_preferred_scale(
522 void* data
, struct wp_fractional_scale_v1
* info
, uint32_t wire_scale
) {
523 MozContainer
* container
= MOZ_CONTAINER(data
);
524 MozContainerWayland
* wl_container
= &container
->data
.wl_container
;
525 wl_container
->current_fractional_scale
= wire_scale
/ 120.0;
527 RefPtr
<nsWindow
> window
= moz_container_get_nsWindow(container
);
528 LOGWAYLAND("%s [%p] scale: %f\n", __func__
, window
.get(),
529 wl_container
->current_fractional_scale
);
530 MOZ_DIAGNOSTIC_ASSERT(window
);
531 window
->OnScaleChanged(/* aNotify = */ true);
534 static const struct wp_fractional_scale_v1_listener fractional_scale_listener
=
536 .preferred_scale
= fractional_scale_handle_preferred_scale
,
539 void moz_container_wayland_set_scale_factor_locked(
540 const MutexAutoLock
& aProofOfLock
, MozContainer
* container
, int aScale
) {
541 if (gfx::gfxVars::UseWebRenderCompositor()) {
542 // the compositor backend handles scaling itself
546 MozContainerWayland
* wl_container
= &container
->data
.wl_container
;
547 wl_container
->container_lock
.AssertCurrentThreadOwns();
549 if (StaticPrefs::widget_wayland_fractional_scale_enabled_AtStartup()) {
550 if (!wl_container
->fractional_scale
) {
551 if (auto* manager
= WaylandDisplayGet()->GetFractionalScaleManager()) {
552 wl_container
->fractional_scale
=
553 wp_fractional_scale_manager_v1_get_fractional_scale(
554 manager
, wl_container
->surface
);
555 wp_fractional_scale_v1_add_listener(wl_container
->fractional_scale
,
556 &fractional_scale_listener
,
561 if (wl_container
->fractional_scale
) {
562 if (!wl_container
->viewport
) {
563 if (auto* viewporter
= WaylandDisplayGet()->GetViewporter()) {
564 wl_container
->viewport
=
565 wp_viewporter_get_viewport(viewporter
, wl_container
->surface
);
568 if (wl_container
->viewport
) {
569 GdkWindow
* gdkWindow
= gtk_widget_get_window(GTK_WIDGET(container
));
570 wp_viewport_set_destination(wl_container
->viewport
,
571 gdk_window_get_width(gdkWindow
),
572 gdk_window_get_height(gdkWindow
));
578 moz_container_wayland_surface_set_scale_locked(aProofOfLock
, wl_container
,
582 bool moz_container_wayland_size_matches_scale_factor_locked(
583 const MutexAutoLock
& aProofOfLock
, MozContainer
* container
, int aWidth
,
585 return aWidth
% container
->data
.wl_container
.buffer_scale
== 0 &&
586 aHeight
% container
->data
.wl_container
.buffer_scale
== 0;
589 static bool moz_container_wayland_surface_create_locked(
590 const MutexAutoLock
& aProofOfLock
, MozContainer
* container
) {
591 MozContainerWayland
* wl_container
= &container
->data
.wl_container
;
593 LOGWAYLAND("%s [%p]\n", __FUNCTION__
,
594 (void*)moz_container_get_nsWindow(container
));
596 GdkWindow
* window
= gtk_widget_get_window(GTK_WIDGET(container
));
597 MOZ_DIAGNOSTIC_ASSERT(window
);
599 wl_surface
* parent_surface
= gdk_wayland_window_get_wl_surface(window
);
600 if (!parent_surface
) {
601 LOGWAYLAND(" Failed - missing parent surface!");
604 LOGWAYLAND(" gtk wl_surface %p ID %d\n", (void*)parent_surface
,
605 wl_proxy_get_id((struct wl_proxy
*)parent_surface
));
607 if (wl_container
->commit_to_parent
) {
608 LOGWAYLAND(" commit to parent");
609 wl_container
->surface
= parent_surface
;
610 NS_DispatchToCurrentThread(NewRunnableFunction(
611 "moz_container_wayland_frame_callback_handler",
612 &moz_container_wayland_frame_callback_handler
, container
, nullptr, 0));
616 // Available as of GTK 3.8+
617 struct wl_compositor
* compositor
= WaylandDisplayGet()->GetCompositor();
618 wl_container
->surface
= wl_compositor_create_surface(compositor
);
619 if (!wl_container
->surface
) {
620 LOGWAYLAND(" Failed - can't create surface!");
624 wl_container
->subsurface
=
625 wl_subcompositor_get_subsurface(WaylandDisplayGet()->GetSubcompositor(),
626 wl_container
->surface
, parent_surface
);
627 if (!wl_container
->subsurface
) {
628 MozClearPointer(wl_container
->surface
, wl_surface_destroy
);
629 LOGWAYLAND(" Failed - can't create sub-surface!");
632 wl_subsurface_set_desync(wl_container
->subsurface
);
634 // Try to guess subsurface offset to avoid potential flickering.
636 if (moz_container_get_nsWindow(container
)->GetCSDDecorationOffset(&dx
, &dy
)) {
637 wl_container
->subsurface_dx
= dx
;
638 wl_container
->subsurface_dy
= dy
;
639 wl_subsurface_set_position(wl_container
->subsurface
, dx
, dy
);
640 LOGWAYLAND(" guessing subsurface position %d %d\n", dx
, dy
);
643 // If there's pending frame callback it's for wrong parent surface,
645 if (wl_container
->frame_callback_handler
) {
646 MozClearPointer(wl_container
->frame_callback_handler
, wl_callback_destroy
);
648 wl_container
->frame_callback_handler
= wl_surface_frame(parent_surface
);
649 wl_callback_add_listener(wl_container
->frame_callback_handler
,
650 &moz_container_frame_listener
, container
);
652 " created frame callback ID %d\n",
653 wl_proxy_get_id((struct wl_proxy
*)wl_container
->frame_callback_handler
));
655 wl_surface_commit(wl_container
->surface
);
656 wl_display_flush(WaylandDisplayGet()->GetDisplay());
658 wl_container
->opaque_region_used
=
659 moz_gdk_wayland_window_add_frame_callback_surface_locked(aProofOfLock
,
662 LOGWAYLAND(" created surface %p ID %d\n", (void*)wl_container
->surface
,
663 wl_proxy_get_id((struct wl_proxy
*)wl_container
->surface
));
667 struct wl_surface
* moz_container_wayland_surface_lock(MozContainer
* container
)
668 MOZ_NO_THREAD_SAFETY_ANALYSIS
{
669 // LOGWAYLAND("%s [%p] surface %p ready_to_draw %d\n", __FUNCTION__,
670 // (void*)container, (void*)container->data.wl_container.surface,
671 // container->data.wl_container.ready_to_draw);
672 container
->data
.wl_container
.container_lock
.Lock();
673 if (!container
->data
.wl_container
.surface
||
674 !container
->data
.wl_container
.ready_to_draw
) {
677 return container
->data
.wl_container
.surface
;
680 void moz_container_wayland_surface_unlock(MozContainer
* container
,
681 struct wl_surface
** surface
)
682 MOZ_NO_THREAD_SAFETY_ANALYSIS
{
683 // Temporarily disabled to avoid log noise
684 // LOGWAYLAND("%s [%p] surface %p\n", __FUNCTION__, (void*)container,
685 // (void*)container->data.wl_container.surface);
689 container
->data
.wl_container
.container_lock
.Unlock();
692 struct wl_egl_window
* moz_container_wayland_get_egl_window(
693 MozContainer
* container
, double scale
) {
694 MozContainerWayland
* wl_container
= &container
->data
.wl_container
;
696 LOGCONTAINER("%s [%p] eglwindow %p scale %d\n", __FUNCTION__
,
697 (void*)moz_container_get_nsWindow(container
),
698 (void*)wl_container
->eglwindow
, (int)scale
);
700 MutexAutoLock
lock(wl_container
->container_lock
);
701 if (!wl_container
->surface
|| !wl_container
->ready_to_draw
) {
703 " quit, wl_container->surface %p wl_container->ready_to_draw %d\n",
704 wl_container
->surface
, wl_container
->ready_to_draw
);
708 GdkWindow
* window
= gtk_widget_get_window(GTK_WIDGET(container
));
709 nsIntSize
requestedSize((int)round(gdk_window_get_width(window
) * scale
),
710 (int)round(gdk_window_get_height(window
) * scale
));
712 if (!wl_container
->eglwindow
) {
713 wl_container
->eglwindow
= wl_egl_window_create(
714 wl_container
->surface
, requestedSize
.width
, requestedSize
.height
);
716 LOGCONTAINER("%s [%p] created eglwindow %p size %d x %d (with scale %f)\n",
717 __FUNCTION__
, (void*)moz_container_get_nsWindow(container
),
718 (void*)wl_container
->eglwindow
, requestedSize
.width
,
719 requestedSize
.height
, scale
);
721 nsIntSize recentSize
;
722 wl_egl_window_get_attached_size(wl_container
->eglwindow
, &recentSize
.width
,
724 if (requestedSize
!= recentSize
) {
725 LOGCONTAINER("%s [%p] resized to %d x %d (with scale %f)\n", __FUNCTION__
,
726 (void*)moz_container_get_nsWindow(container
),
727 requestedSize
.width
, requestedSize
.height
, scale
);
728 wl_egl_window_resize(wl_container
->eglwindow
, requestedSize
.width
,
729 requestedSize
.height
, 0, 0);
732 moz_container_wayland_surface_set_scale_locked(lock
, wl_container
,
733 static_cast<int>(scale
));
734 return wl_container
->eglwindow
;
737 gboolean
moz_container_wayland_has_egl_window(MozContainer
* container
) {
738 return !!container
->data
.wl_container
.eglwindow
;
741 void moz_container_wayland_update_opaque_region(MozContainer
* container
) {
742 MozContainerWayland
* wl_container
= &container
->data
.wl_container
;
743 MutexAutoLock
lock(wl_container
->container_lock
);
744 wl_container
->opaque_region_needs_updates
= true;
746 // When GL compositor / WebRender is used,
747 // moz_container_wayland_get_egl_window() is called only once when window
748 // is created or resized so update opaque region now.
749 if (moz_container_wayland_has_egl_window(container
)) {
750 nsWindow
* window
= moz_container_get_nsWindow(container
);
751 moz_container_wayland_set_opaque_region_locked(lock
, container
,
752 window
->GetOpaqueRegion());
756 gboolean
moz_container_wayland_can_draw(MozContainer
* container
) {
757 MozContainerWayland
* wl_container
= &container
->data
.wl_container
;
758 MutexAutoLock
lock(wl_container
->container_lock
);
759 return wl_container
->ready_to_draw
;
762 double moz_container_wayland_get_fractional_scale(MozContainer
* container
) {
763 return container
->data
.wl_container
.current_fractional_scale
;
766 double moz_container_wayland_get_scale(MozContainer
* container
) {
767 nsWindow
* window
= moz_container_get_nsWindow(container
);
768 return window
? window
->FractionalScaleFactor() : 1.0;
771 void moz_container_wayland_set_commit_to_parent(MozContainer
* container
) {
772 MozContainerWayland
* wl_container
= &container
->data
.wl_container
;
773 MOZ_DIAGNOSTIC_ASSERT(!wl_container
->surface
);
774 wl_container
->commit_to_parent
= true;
777 bool moz_container_wayland_is_commiting_to_parent(MozContainer
* container
) {
778 return container
->data
.wl_container
.commit_to_parent
;
781 bool moz_container_wayland_is_waiting_to_show(MozContainer
* container
) {
782 MOZ_DIAGNOSTIC_ASSERT(NS_IsMainThread());
783 return container
->data
.wl_container
.waiting_to_show
;
786 void moz_container_wayland_clear_waiting_to_show_flag(MozContainer
* container
) {
787 MOZ_DIAGNOSTIC_ASSERT(NS_IsMainThread());
788 container
->data
.wl_container
.waiting_to_show
= false;