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
);
89 // Lock mozcontainer and get wayland surface of it. You need to pair with
90 // moz_container_wayland_surface_unlock() even
91 // if moz_container_wayland_surface_lock() fails and returns nullptr.
92 static struct wl_surface
* moz_container_wayland_surface_lock(
93 MozContainer
* container
);
94 static void moz_container_wayland_surface_unlock(MozContainer
* container
,
95 struct wl_surface
** surface
);
97 MozContainerSurfaceLock::MozContainerSurfaceLock(MozContainer
* aContainer
) {
98 mContainer
= aContainer
;
99 mSurface
= moz_container_wayland_surface_lock(aContainer
);
101 MozContainerSurfaceLock::~MozContainerSurfaceLock() {
102 moz_container_wayland_surface_unlock(mContainer
, &mSurface
);
104 struct wl_surface
* MozContainerSurfaceLock::GetSurface() { return mSurface
; }
106 // Invalidate gtk wl_surface to commit changes to wl_subsurface.
107 // wl_subsurface changes are effective when parent surface is commited.
108 static void moz_container_wayland_invalidate(MozContainer
* container
) {
109 LOGWAYLAND("moz_container_wayland_invalidate [%p]\n",
110 (void*)moz_container_get_nsWindow(container
));
112 GdkWindow
* window
= gtk_widget_get_window(GTK_WIDGET(container
));
114 LOGWAYLAND(" Failed - missing GdkWindow!\n");
117 gdk_window_invalidate_rect(window
, nullptr, true);
120 // Route input to parent wl_surface owned by Gtk+ so we get input
122 static void moz_container_clear_input_region(MozContainer
* container
) {
123 struct wl_compositor
* compositor
= WaylandDisplayGet()->GetCompositor();
124 MozContainerWayland
* wl_container
= &container
->data
.wl_container
;
125 wl_region
* region
= wl_compositor_create_region(compositor
);
126 wl_surface_set_input_region(wl_container
->surface
, region
);
127 wl_region_destroy(region
);
130 static void moz_container_wayland_move_locked(const MutexAutoLock
& aProofOfLock
,
131 MozContainer
* container
, int dx
,
133 LOGCONTAINER("moz_container_wayland_move [%p] %d,%d\n",
134 (void*)moz_container_get_nsWindow(container
), dx
, dy
);
136 MozContainerWayland
* wl_container
= &container
->data
.wl_container
;
137 if (!wl_container
->subsurface
|| (wl_container
->subsurface_dx
== dx
&&
138 wl_container
->subsurface_dy
== dy
)) {
142 wl_container
->subsurface_dx
= dx
;
143 wl_container
->subsurface_dy
= dy
;
144 wl_subsurface_set_position(wl_container
->subsurface
,
145 wl_container
->subsurface_dx
,
146 wl_container
->subsurface_dy
);
149 // This is called from layout/compositor code only with
150 // size equal to GL rendering context.
152 // Return false if scale factor doesn't match buffer size.
153 // We need to skip painting in such case do avoid Wayland compositor freaking.
154 bool moz_container_wayland_egl_window_set_size(MozContainer
* container
,
155 nsIntSize aSize
, int aScale
) {
156 MozContainerWayland
* wl_container
= &container
->data
.wl_container
;
157 MutexAutoLock
lock(wl_container
->container_lock
);
158 if (!wl_container
->eglwindow
) {
162 if (wl_container
->buffer_scale
!= aScale
) {
163 moz_container_wayland_set_scale_factor_locked(lock
, container
, aScale
);
167 "moz_container_wayland_egl_window_set_size [%p] %d x %d scale %d "
168 "(unscaled %d x %d)",
169 (void*)moz_container_get_nsWindow(container
), aSize
.width
, aSize
.height
,
170 aScale
, aSize
.width
/ aScale
, aSize
.height
/ aScale
);
171 wl_egl_window_resize(wl_container
->eglwindow
, aSize
.width
, aSize
.height
, 0,
174 return moz_container_wayland_size_matches_scale_factor_locked(
175 lock
, container
, aSize
.width
, aSize
.height
);
178 void moz_container_wayland_add_initial_draw_callback_locked(
179 MozContainer
* container
, const std::function
<void(void)>& initial_draw_cb
) {
180 MozContainerWayland
* wl_container
= &container
->data
.wl_container
;
182 if (wl_container
->ready_to_draw
&& !wl_container
->surface
) {
184 "moz_container_wayland_add_or_fire_initial_draw_callback:"
185 " ready to draw without wayland surface!");
187 MOZ_DIAGNOSTIC_ASSERT(!wl_container
->ready_to_draw
|| !wl_container
->surface
);
188 wl_container
->initial_draw_cbs
.push_back(initial_draw_cb
);
191 void moz_container_wayland_add_or_fire_initial_draw_callback(
192 MozContainer
* container
, const std::function
<void(void)>& initial_draw_cb
) {
193 MozContainerWayland
* wl_container
= &container
->data
.wl_container
;
195 MutexAutoLock
lock(wl_container
->container_lock
);
196 if (wl_container
->ready_to_draw
&& !wl_container
->surface
) {
198 "moz_container_wayland_add_or_fire_initial_draw_callback: ready to "
200 "without wayland surface!");
202 if (!wl_container
->ready_to_draw
|| !wl_container
->surface
) {
203 wl_container
->initial_draw_cbs
.push_back(initial_draw_cb
);
208 // We're ready to draw as
209 // wl_container->ready_to_draw && wl_container->surface
210 // call the callback directly instead of store them.
214 static void moz_container_wayland_clear_initial_draw_callback_locked(
215 const MutexAutoLock
& aProofOfLock
, MozContainer
* container
) {
216 MozContainerWayland
* wl_container
= &container
->data
.wl_container
;
217 MozClearPointer(wl_container
->frame_callback_handler
, wl_callback_destroy
);
218 wl_container
->initial_draw_cbs
.clear();
221 void moz_container_wayland_clear_initial_draw_callback(
222 MozContainer
* container
) {
223 MutexAutoLock
lock(container
->data
.wl_container
.container_lock
);
224 moz_container_wayland_clear_initial_draw_callback_locked(lock
, container
);
227 static void moz_container_wayland_frame_callback_handler(
228 void* data
, struct wl_callback
* callback
, uint32_t time
) {
229 MozContainerWayland
* wl_container
= MOZ_WL_CONTAINER(data
);
232 "%s [%p] frame_callback_handler %p ready_to_draw %d (set to true)"
233 " initial_draw callback %zd\n",
234 __FUNCTION__
, (void*)moz_container_get_nsWindow(MOZ_CONTAINER(data
)),
235 (void*)wl_container
->frame_callback_handler
, wl_container
->ready_to_draw
,
236 wl_container
->initial_draw_cbs
.size());
238 std::vector
<std::function
<void(void)>> cbs
;
240 // Protect mozcontainer internals changes by container_lock.
241 MutexAutoLock
lock(wl_container
->container_lock
);
242 MozClearPointer(wl_container
->frame_callback_handler
, wl_callback_destroy
);
243 // It's possible that container is already unmapped so quit in such case.
244 if (!wl_container
->surface
) {
245 LOGWAYLAND(" container is unmapped, quit.");
246 if (!wl_container
->initial_draw_cbs
.empty()) {
247 NS_WARNING("Unmapping MozContainer with active draw callback!");
248 wl_container
->initial_draw_cbs
.clear();
252 if (wl_container
->ready_to_draw
) {
255 wl_container
->ready_to_draw
= true;
256 cbs
= std::move(wl_container
->initial_draw_cbs
);
259 // Call the callbacks registered by
260 // moz_container_wayland_add_or_fire_initial_draw_callback().
261 // and we can't do that under mozcontainer lock.
262 for (auto const& cb
: cbs
) {
267 static const struct wl_callback_listener moz_container_frame_listener
= {
268 moz_container_wayland_frame_callback_handler
};
270 static void after_frame_clock_after_paint(GdkFrameClock
* clock
,
271 MozContainer
* container
) {
272 MozContainerSurfaceLock
lock(container
);
273 struct wl_surface
* surface
= lock
.GetSurface();
275 wl_surface_commit(surface
);
279 static bool moz_gdk_wayland_window_add_frame_callback_surface_locked(
280 const MutexAutoLock
& aProofOfLock
, MozContainer
* container
) {
281 static auto sGdkWaylandWindowAddCallbackSurface
=
282 (void (*)(GdkWindow
*, struct wl_surface
*))dlsym(
283 RTLD_DEFAULT
, "gdk_wayland_window_add_frame_callback_surface");
285 if (!StaticPrefs::widget_wayland_opaque_region_enabled_AtStartup() ||
286 !sGdkWaylandWindowAddCallbackSurface
) {
290 GdkWindow
* window
= gtk_widget_get_window(GTK_WIDGET(container
));
291 MozContainerWayland
* wl_container
= &container
->data
.wl_container
;
293 sGdkWaylandWindowAddCallbackSurface(window
, wl_container
->surface
);
295 GdkFrameClock
* frame_clock
= gdk_window_get_frame_clock(window
);
296 g_signal_connect_after(frame_clock
, "after-paint",
297 G_CALLBACK(after_frame_clock_after_paint
), container
);
301 static void moz_gdk_wayland_window_remove_frame_callback_surface_locked(
302 const MutexAutoLock
& aProofOfLock
, MozContainer
* container
) {
303 static auto sGdkWaylandWindowRemoveCallbackSurface
=
304 (void (*)(GdkWindow
*, struct wl_surface
*))dlsym(
305 RTLD_DEFAULT
, "gdk_wayland_window_remove_frame_callback_surface");
307 if (!sGdkWaylandWindowRemoveCallbackSurface
) {
311 GdkWindow
* window
= gtk_widget_get_window(GTK_WIDGET(container
));
312 MozContainerWayland
* wl_container
= &container
->data
.wl_container
;
314 if (wl_container
->surface
) {
315 sGdkWaylandWindowRemoveCallbackSurface(window
, wl_container
->surface
);
318 GdkFrameClock
* frame_clock
= gdk_window_get_frame_clock(window
);
319 g_signal_handlers_disconnect_by_func(
320 frame_clock
, FuncToGpointer(after_frame_clock_after_paint
), container
);
323 void moz_container_wayland_unmap(GtkWidget
* widget
) {
324 MozContainer
* container
= MOZ_CONTAINER(widget
);
325 MozContainerWayland
* wl_container
= &container
->data
.wl_container
;
326 MutexAutoLock
lock(wl_container
->container_lock
);
328 LOGCONTAINER("%s [%p]\n", __FUNCTION__
,
329 (void*)moz_container_get_nsWindow(container
));
331 moz_container_wayland_clear_initial_draw_callback_locked(lock
, container
);
333 if (wl_container
->opaque_region_used
) {
334 moz_gdk_wayland_window_remove_frame_callback_surface_locked(lock
,
337 if (wl_container
->commit_to_parent
) {
338 wl_container
->surface
= nullptr;
341 MozClearPointer(wl_container
->eglwindow
, wl_egl_window_destroy
);
342 MozClearPointer(wl_container
->subsurface
, wl_subsurface_destroy
);
343 MozClearPointer(wl_container
->surface
, wl_surface_destroy
);
344 MozClearPointer(wl_container
->viewport
, wp_viewport_destroy
);
345 MozClearPointer(wl_container
->fractional_scale
,
346 wp_fractional_scale_v1_destroy
);
348 wl_container
->ready_to_draw
= false;
349 wl_container
->buffer_scale
= 1;
350 wl_container
->current_fractional_scale
= 0.0;
353 gboolean
moz_container_wayland_map_event(GtkWidget
* widget
,
354 GdkEventAny
* event
) {
355 MozContainerWayland
* wl_container
= &MOZ_CONTAINER(widget
)->data
.wl_container
;
357 LOGCONTAINER("%s [%p]\n", __FUNCTION__
,
358 (void*)moz_container_get_nsWindow(MOZ_CONTAINER(widget
)));
360 // We need to mark MozContainer as mapped to make sure
361 // moz_container_wayland_unmap() is called on hide/withdraw.
362 gtk_widget_set_mapped(widget
, TRUE
);
364 // Make sure we're on main thread as we can't lock mozContainer here
365 // due to moz_container_wayland_add_or_fire_initial_draw_callback() call
367 MOZ_DIAGNOSTIC_ASSERT(NS_IsMainThread());
369 // Set waiting_to_show flag. It means the mozcontainer is cofigured/mapped
370 // and it's supposed to be visible. *But* it's really visible when we get
371 // moz_container_wayland_add_or_fire_initial_draw_callback() which means
372 // wayland compositor makes it live.
373 wl_container
->waiting_to_show
= true;
374 MozContainer
* container
= MOZ_CONTAINER(widget
);
375 moz_container_wayland_add_or_fire_initial_draw_callback(
376 container
, [container
]() -> void {
378 "[%p] moz_container_wayland_add_or_fire_initial_draw_callback set "
380 moz_container_get_nsWindow(container
));
381 moz_container_wayland_clear_waiting_to_show_flag(container
);
384 MutexAutoLock
lock(wl_container
->container_lock
);
386 // Don't create wl_subsurface in map_event when it's already created or
387 // if we create it for the first time.
388 if (wl_container
->ready_to_draw
|| wl_container
->before_first_size_alloc
) {
392 if (!wl_container
->surface
) {
393 if (!moz_container_wayland_surface_create_locked(lock
,
394 MOZ_CONTAINER(widget
))) {
399 nsWindow
* window
= moz_container_get_nsWindow(MOZ_CONTAINER(widget
));
400 moz_container_wayland_set_scale_factor_locked(lock
, MOZ_CONTAINER(widget
),
401 window
->GdkCeiledScaleFactor());
402 moz_container_wayland_set_opaque_region_locked(lock
, MOZ_CONTAINER(widget
));
403 moz_container_clear_input_region(MOZ_CONTAINER(widget
));
404 moz_container_wayland_invalidate(MOZ_CONTAINER(widget
));
408 void moz_container_wayland_map(GtkWidget
* widget
) {
409 LOGCONTAINER("%s [%p]\n", __FUNCTION__
,
410 (void*)moz_container_get_nsWindow(MOZ_CONTAINER(widget
)));
412 g_return_if_fail(IS_MOZ_CONTAINER(widget
));
413 gtk_widget_set_mapped(widget
, TRUE
);
415 if (gtk_widget_get_has_window(widget
)) {
416 gdk_window_show(gtk_widget_get_window(widget
));
420 void moz_container_wayland_size_allocate(GtkWidget
* widget
,
421 GtkAllocation
* allocation
) {
422 MozContainer
* container
;
423 GtkAllocation tmp_allocation
;
425 g_return_if_fail(IS_MOZ_CONTAINER(widget
));
427 LOGCONTAINER("moz_container_wayland_size_allocate [%p] %d,%d -> %d x %d\n",
428 (void*)moz_container_get_nsWindow(MOZ_CONTAINER(widget
)),
429 allocation
->x
, allocation
->y
, allocation
->width
,
432 /* short circuit if you can */
433 container
= MOZ_CONTAINER(widget
);
434 gtk_widget_get_allocation(widget
, &tmp_allocation
);
435 if (!container
->data
.children
&& tmp_allocation
.x
== allocation
->x
&&
436 tmp_allocation
.y
== allocation
->y
&&
437 tmp_allocation
.width
== allocation
->width
&&
438 tmp_allocation
.height
== allocation
->height
) {
441 gtk_widget_set_allocation(widget
, allocation
);
443 if (gtk_widget_get_has_window(widget
) && gtk_widget_get_realized(widget
)) {
444 gdk_window_move_resize(gtk_widget_get_window(widget
), allocation
->x
,
445 allocation
->y
, allocation
->width
,
447 // We need to position our subsurface according to GdkWindow
448 // when offset changes (GdkWindow is maximized for instance).
449 // see gtk-clutter-embed.c for reference.
450 MutexAutoLock
lock(container
->data
.wl_container
.container_lock
);
451 if (!container
->data
.wl_container
.surface
) {
452 if (!moz_container_wayland_surface_create_locked(lock
, container
)) {
456 nsWindow
* window
= moz_container_get_nsWindow(container
);
457 moz_container_wayland_set_scale_factor_locked(
458 lock
, container
, window
->GdkCeiledScaleFactor());
459 moz_container_wayland_set_opaque_region_locked(lock
, container
);
460 moz_container_wayland_move_locked(lock
, container
, allocation
->x
,
462 moz_container_clear_input_region(container
);
463 moz_container_wayland_invalidate(MOZ_CONTAINER(widget
));
464 container
->data
.wl_container
.before_first_size_alloc
= false;
468 static wl_region
* moz_container_wayland_create_opaque_region(
469 int aX
, int aY
, int aWidth
, int aHeight
, int aCornerRadius
) {
470 struct wl_compositor
* compositor
= WaylandDisplayGet()->GetCompositor();
471 wl_region
* region
= wl_compositor_create_region(compositor
);
472 wl_region_add(region
, aX
, aY
, aWidth
, aHeight
);
474 wl_region_subtract(region
, aX
, aY
, aCornerRadius
, aCornerRadius
);
475 wl_region_subtract(region
, aX
+ aWidth
- aCornerRadius
, aY
, aCornerRadius
,
477 wl_region_subtract(region
, aX
, aY
+ aHeight
- aCornerRadius
, aCornerRadius
,
479 wl_region_subtract(region
, aX
+ aWidth
- aCornerRadius
,
480 aY
+ aHeight
- aCornerRadius
, aCornerRadius
,
486 static void moz_container_wayland_set_opaque_region_locked(
487 const MutexAutoLock
& aProofOfLock
, MozContainer
* container
) {
488 MozContainerWayland
* wl_container
= &container
->data
.wl_container
;
490 if (!wl_container
->opaque_region_needs_updates
) {
494 if (!wl_container
->opaque_region_used
) {
495 wl_container
->opaque_region_needs_updates
= false;
499 GtkAllocation allocation
;
500 gtk_widget_get_allocation(GTK_WIDGET(container
), &allocation
);
502 wl_region
* region
= moz_container_wayland_create_opaque_region(
503 0, 0, allocation
.width
, allocation
.height
,
504 wl_container
->opaque_region_corner_radius
);
505 wl_surface_set_opaque_region(wl_container
->surface
, region
);
506 wl_region_destroy(region
);
507 wl_container
->opaque_region_needs_updates
= false;
510 static void moz_container_wayland_set_opaque_region(MozContainer
* container
) {
511 MozContainerWayland
* wl_container
= &container
->data
.wl_container
;
512 MutexAutoLock
lock(wl_container
->container_lock
);
513 if (wl_container
->surface
) {
514 moz_container_wayland_set_opaque_region_locked(lock
, container
);
518 static void moz_container_wayland_surface_set_scale_locked(
519 const MutexAutoLock
& aProofOfLock
, MozContainerWayland
* wl_container
,
521 if (!wl_container
->surface
) {
524 if (wl_container
->buffer_scale
== scale
) {
528 LOGCONTAINER("%s scale %d\n", __FUNCTION__
, scale
);
530 // There is a chance that the attached wl_buffer has not yet been doubled
531 // on the main thread when scale factor changed to 2. This leads to
532 // crash with the following message:
533 // Buffer size (AxB) must be an integer multiple of the buffer_scale (2)
534 // Removing the possibly wrong wl_buffer to prevent that crash:
535 wl_surface_attach(wl_container
->surface
, nullptr, 0, 0);
536 wl_surface_set_buffer_scale(wl_container
->surface
, scale
);
537 wl_container
->buffer_scale
= scale
;
540 static void fractional_scale_handle_preferred_scale(
541 void* data
, struct wp_fractional_scale_v1
* info
, uint32_t wire_scale
) {
542 MozContainer
* container
= MOZ_CONTAINER(data
);
543 MozContainerWayland
* wl_container
= &container
->data
.wl_container
;
544 wl_container
->current_fractional_scale
= wire_scale
/ 120.0;
546 RefPtr
<nsWindow
> window
= moz_container_get_nsWindow(container
);
547 LOGWAYLAND("%s [%p] scale: %f\n", __func__
, window
.get(),
548 wl_container
->current_fractional_scale
);
549 MOZ_DIAGNOSTIC_ASSERT(window
);
550 window
->OnScaleChanged(/* aNotify = */ true);
553 static const struct wp_fractional_scale_v1_listener fractional_scale_listener
=
555 .preferred_scale
= fractional_scale_handle_preferred_scale
,
558 void moz_container_wayland_set_scale_factor_locked(
559 const MutexAutoLock
& aProofOfLock
, MozContainer
* container
, int aScale
) {
560 if (gfx::gfxVars::UseWebRenderCompositor()) {
561 // the compositor backend handles scaling itself
565 MozContainerWayland
* wl_container
= &container
->data
.wl_container
;
566 wl_container
->container_lock
.AssertCurrentThreadOwns();
568 if (StaticPrefs::widget_wayland_fractional_scale_enabled_AtStartup()) {
569 if (!wl_container
->fractional_scale
) {
570 if (auto* manager
= WaylandDisplayGet()->GetFractionalScaleManager()) {
571 wl_container
->fractional_scale
=
572 wp_fractional_scale_manager_v1_get_fractional_scale(
573 manager
, wl_container
->surface
);
574 wp_fractional_scale_v1_add_listener(wl_container
->fractional_scale
,
575 &fractional_scale_listener
,
580 if (wl_container
->fractional_scale
) {
581 if (!wl_container
->viewport
) {
582 if (auto* viewporter
= WaylandDisplayGet()->GetViewporter()) {
583 wl_container
->viewport
=
584 wp_viewporter_get_viewport(viewporter
, wl_container
->surface
);
587 if (wl_container
->viewport
) {
588 GdkWindow
* gdkWindow
= gtk_widget_get_window(GTK_WIDGET(container
));
589 wp_viewport_set_destination(wl_container
->viewport
,
590 gdk_window_get_width(gdkWindow
),
591 gdk_window_get_height(gdkWindow
));
597 moz_container_wayland_surface_set_scale_locked(aProofOfLock
, wl_container
,
601 bool moz_container_wayland_size_matches_scale_factor_locked(
602 const MutexAutoLock
& aProofOfLock
, MozContainer
* container
, int aWidth
,
604 return aWidth
% container
->data
.wl_container
.buffer_scale
== 0 &&
605 aHeight
% container
->data
.wl_container
.buffer_scale
== 0;
608 static bool moz_container_wayland_surface_create_locked(
609 const MutexAutoLock
& aProofOfLock
, MozContainer
* container
) {
610 MozContainerWayland
* wl_container
= &container
->data
.wl_container
;
612 LOGWAYLAND("%s [%p]\n", __FUNCTION__
,
613 (void*)moz_container_get_nsWindow(container
));
615 GdkWindow
* window
= gtk_widget_get_window(GTK_WIDGET(container
));
616 MOZ_DIAGNOSTIC_ASSERT(window
);
618 wl_surface
* parent_surface
= gdk_wayland_window_get_wl_surface(window
);
619 if (!parent_surface
) {
620 LOGWAYLAND(" Failed - missing parent surface!");
623 LOGWAYLAND(" gtk wl_surface %p ID %d\n", (void*)parent_surface
,
624 wl_proxy_get_id((struct wl_proxy
*)parent_surface
));
626 if (wl_container
->commit_to_parent
) {
627 LOGWAYLAND(" commit to parent");
628 wl_container
->surface
= parent_surface
;
629 NS_DispatchToCurrentThread(NewRunnableFunction(
630 "moz_container_wayland_frame_callback_handler",
631 &moz_container_wayland_frame_callback_handler
, container
, nullptr, 0));
635 // Available as of GTK 3.8+
636 struct wl_compositor
* compositor
= WaylandDisplayGet()->GetCompositor();
637 wl_container
->surface
= wl_compositor_create_surface(compositor
);
638 if (!wl_container
->surface
) {
639 LOGWAYLAND(" Failed - can't create surface!");
643 wl_container
->subsurface
=
644 wl_subcompositor_get_subsurface(WaylandDisplayGet()->GetSubcompositor(),
645 wl_container
->surface
, parent_surface
);
646 if (!wl_container
->subsurface
) {
647 MozClearPointer(wl_container
->surface
, wl_surface_destroy
);
648 LOGWAYLAND(" Failed - can't create sub-surface!");
651 wl_subsurface_set_desync(wl_container
->subsurface
);
653 // Try to guess subsurface offset to avoid potential flickering.
655 if (moz_container_get_nsWindow(container
)->GetCSDDecorationOffset(&dx
, &dy
)) {
656 wl_container
->subsurface_dx
= dx
;
657 wl_container
->subsurface_dy
= dy
;
658 wl_subsurface_set_position(wl_container
->subsurface
, dx
, dy
);
659 LOGWAYLAND(" guessing subsurface position %d %d\n", dx
, dy
);
662 // If there's pending frame callback it's for wrong parent surface,
664 if (wl_container
->frame_callback_handler
) {
665 MozClearPointer(wl_container
->frame_callback_handler
, wl_callback_destroy
);
667 wl_container
->frame_callback_handler
= wl_surface_frame(parent_surface
);
668 wl_callback_add_listener(wl_container
->frame_callback_handler
,
669 &moz_container_frame_listener
, container
);
671 " created frame callback ID %d\n",
672 wl_proxy_get_id((struct wl_proxy
*)wl_container
->frame_callback_handler
));
674 wl_surface_commit(wl_container
->surface
);
675 wl_display_flush(WaylandDisplayGet()->GetDisplay());
677 wl_container
->opaque_region_used
=
678 moz_gdk_wayland_window_add_frame_callback_surface_locked(aProofOfLock
,
681 LOGWAYLAND(" created surface %p ID %d\n", (void*)wl_container
->surface
,
682 wl_proxy_get_id((struct wl_proxy
*)wl_container
->surface
));
686 struct wl_surface
* moz_container_wayland_surface_lock(MozContainer
* container
)
687 MOZ_NO_THREAD_SAFETY_ANALYSIS
{
688 // LOGWAYLAND("%s [%p] surface %p ready_to_draw %d\n", __FUNCTION__,
689 // (void*)container, (void*)container->data.wl_container.surface,
690 // container->data.wl_container.ready_to_draw);
691 container
->data
.wl_container
.container_lock
.Lock();
692 if (!container
->data
.wl_container
.surface
||
693 !container
->data
.wl_container
.ready_to_draw
) {
696 return container
->data
.wl_container
.surface
;
699 void moz_container_wayland_surface_unlock(MozContainer
* container
,
700 struct wl_surface
** surface
)
701 MOZ_NO_THREAD_SAFETY_ANALYSIS
{
702 // Temporarily disabled to avoid log noise
703 // LOGWAYLAND("%s [%p] surface %p\n", __FUNCTION__, (void*)container,
704 // (void*)container->data.wl_container.surface);
708 container
->data
.wl_container
.container_lock
.Unlock();
711 struct wl_egl_window
* moz_container_wayland_get_egl_window(
712 MozContainer
* container
, double scale
) {
713 MozContainerWayland
* wl_container
= &container
->data
.wl_container
;
715 LOGCONTAINER("%s [%p] eglwindow %p scale %d\n", __FUNCTION__
,
716 (void*)moz_container_get_nsWindow(container
),
717 (void*)wl_container
->eglwindow
, (int)scale
);
719 MutexAutoLock
lock(wl_container
->container_lock
);
720 if (!wl_container
->surface
|| !wl_container
->ready_to_draw
) {
722 " quit, wl_container->surface %p wl_container->ready_to_draw %d\n",
723 wl_container
->surface
, wl_container
->ready_to_draw
);
727 GdkWindow
* window
= gtk_widget_get_window(GTK_WIDGET(container
));
728 nsIntSize
requestedSize((int)round(gdk_window_get_width(window
) * scale
),
729 (int)round(gdk_window_get_height(window
) * scale
));
731 if (!wl_container
->eglwindow
) {
732 wl_container
->eglwindow
= wl_egl_window_create(
733 wl_container
->surface
, requestedSize
.width
, requestedSize
.height
);
735 LOGCONTAINER("%s [%p] created eglwindow %p size %d x %d (with scale %f)\n",
736 __FUNCTION__
, (void*)moz_container_get_nsWindow(container
),
737 (void*)wl_container
->eglwindow
, requestedSize
.width
,
738 requestedSize
.height
, scale
);
740 nsIntSize recentSize
;
741 wl_egl_window_get_attached_size(wl_container
->eglwindow
, &recentSize
.width
,
743 if (requestedSize
!= recentSize
) {
744 LOGCONTAINER("%s [%p] resized to %d x %d (with scale %f)\n", __FUNCTION__
,
745 (void*)moz_container_get_nsWindow(container
),
746 requestedSize
.width
, requestedSize
.height
, scale
);
747 wl_egl_window_resize(wl_container
->eglwindow
, requestedSize
.width
,
748 requestedSize
.height
, 0, 0);
751 moz_container_wayland_surface_set_scale_locked(lock
, wl_container
,
752 static_cast<int>(scale
));
753 return wl_container
->eglwindow
;
756 gboolean
moz_container_wayland_has_egl_window(MozContainer
* container
) {
757 return !!container
->data
.wl_container
.eglwindow
;
760 void moz_container_wayland_update_opaque_region(MozContainer
* container
,
762 MozContainerWayland
* wl_container
= &container
->data
.wl_container
;
763 wl_container
->opaque_region_needs_updates
= true;
764 wl_container
->opaque_region_corner_radius
= corner_radius
;
766 // When GL compositor / WebRender is used,
767 // moz_container_wayland_get_egl_window() is called only once when window
768 // is created or resized so update opaque region now.
769 if (moz_container_wayland_has_egl_window(container
)) {
770 moz_container_wayland_set_opaque_region(container
);
774 gboolean
moz_container_wayland_can_draw(MozContainer
* container
) {
775 MozContainerWayland
* wl_container
= &container
->data
.wl_container
;
776 MutexAutoLock
lock(wl_container
->container_lock
);
777 return wl_container
->ready_to_draw
;
780 double moz_container_wayland_get_fractional_scale(MozContainer
* container
) {
781 return container
->data
.wl_container
.current_fractional_scale
;
784 double moz_container_wayland_get_scale(MozContainer
* container
) {
785 nsWindow
* window
= moz_container_get_nsWindow(container
);
786 return window
? window
->FractionalScaleFactor() : 1.0;
789 void moz_container_wayland_set_commit_to_parent(MozContainer
* container
) {
790 MozContainerWayland
* wl_container
= &container
->data
.wl_container
;
791 MOZ_DIAGNOSTIC_ASSERT(!wl_container
->surface
);
792 wl_container
->commit_to_parent
= true;
795 bool moz_container_wayland_is_commiting_to_parent(MozContainer
* container
) {
796 return container
->data
.wl_container
.commit_to_parent
;
799 bool moz_container_wayland_is_waiting_to_show(MozContainer
* container
) {
800 MOZ_DIAGNOSTIC_ASSERT(NS_IsMainThread());
801 return container
->data
.wl_container
.waiting_to_show
;
804 void moz_container_wayland_clear_waiting_to_show_flag(MozContainer
* container
) {
805 MOZ_DIAGNOSTIC_ASSERT(NS_IsMainThread());
806 container
->data
.wl_container
.waiting_to_show
= false;