Bug 1861709 replace AudioCallbackDriver::ThreadRunning() assertions that mean to...
[gecko.git] / widget / gtk / MozContainerWayland.cpp
blob71c053cb21849df4cadf17102d5a786adbafab82
1 /* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim:expandtab:shiftwidth=2:tabstop=2:
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/. */
7 /*
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+) |
15 * | |
16 * | --------------------------------------------------- |
17 * | | mContainer (contains wl_surface owned by Gtk+) | |
18 * | | | |
19 * | | --------------------------------------------- | |
20 * | | | wl_subsurface (attached to wl_surface | | |
21 * | | | of mContainer) | | |
22 * | | | | | |
23 * | | | | | |
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"
53 #include <dlfcn.h>
54 #include <glib.h>
55 #include <stdio.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"
64 #ifdef MOZ_LOGGING
66 # include "mozilla/Logging.h"
67 # include "nsTArray.h"
68 # include "Units.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__))
76 #else
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));
113 if (!window) {
114 LOGWAYLAND(" Failed - missing GdkWindow!\n");
115 return;
117 gdk_window_invalidate_rect(window, nullptr, true);
120 // Route input to parent wl_surface owned by Gtk+ so we get input
121 // events from Gtk+.
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,
132 int dy) {
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)) {
139 return;
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) {
159 return false;
162 if (wl_container->buffer_scale != aScale) {
163 moz_container_wayland_set_scale_factor_locked(lock, container, aScale);
166 LOGCONTAINER(
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) {
183 NS_WARNING(
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) {
197 NS_WARNING(
198 "moz_container_wayland_add_or_fire_initial_draw_callback: ready to "
199 "draw "
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);
204 return;
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.
211 initial_draw_cb();
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);
231 LOGWAYLAND(
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();
250 return;
252 if (wl_container->ready_to_draw) {
253 return;
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) {
263 cb();
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();
274 if (surface) {
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) {
287 return false;
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);
298 return true;
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) {
308 return;
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,
335 container);
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
366 // below.
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 {
377 LOGCONTAINER(
378 "[%p] moz_container_wayland_add_or_fire_initial_draw_callback set "
379 "visible",
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) {
389 return FALSE;
392 if (!wl_container->surface) {
393 if (!moz_container_wayland_surface_create_locked(lock,
394 MOZ_CONTAINER(widget))) {
395 return FALSE;
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));
405 return FALSE;
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,
430 allocation->height);
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) {
439 return;
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,
446 allocation->height);
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)) {
453 return;
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,
461 allocation->y);
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);
473 if (aCornerRadius) {
474 wl_region_subtract(region, aX, aY, aCornerRadius, aCornerRadius);
475 wl_region_subtract(region, aX + aWidth - aCornerRadius, aY, aCornerRadius,
476 aCornerRadius);
477 wl_region_subtract(region, aX, aY + aHeight - aCornerRadius, aCornerRadius,
478 aCornerRadius);
479 wl_region_subtract(region, aX + aWidth - aCornerRadius,
480 aY + aHeight - aCornerRadius, aCornerRadius,
481 aCornerRadius);
483 return region;
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) {
491 return;
494 if (!wl_container->opaque_region_used) {
495 wl_container->opaque_region_needs_updates = false;
496 return;
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,
520 int scale) {
521 if (!wl_container->surface) {
522 return;
524 if (wl_container->buffer_scale == scale) {
525 return;
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
562 return;
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,
576 container);
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));
592 return;
597 moz_container_wayland_surface_set_scale_locked(aProofOfLock, wl_container,
598 aScale);
601 bool moz_container_wayland_size_matches_scale_factor_locked(
602 const MutexAutoLock& aProofOfLock, MozContainer* container, int aWidth,
603 int aHeight) {
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!");
621 return false;
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));
632 return true;
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!");
640 return false;
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!");
649 return false;
651 wl_subsurface_set_desync(wl_container->subsurface);
653 // Try to guess subsurface offset to avoid potential flickering.
654 int dx, dy;
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,
663 // so delete it.
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);
670 LOGWAYLAND(
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,
679 container);
681 LOGWAYLAND(" created surface %p ID %d\n", (void*)wl_container->surface,
682 wl_proxy_get_id((struct wl_proxy*)wl_container->surface));
683 return true;
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) {
694 return nullptr;
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);
705 if (*surface) {
706 *surface = nullptr;
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) {
721 LOGCONTAINER(
722 " quit, wl_container->surface %p wl_container->ready_to_draw %d\n",
723 wl_container->surface, wl_container->ready_to_draw);
724 return nullptr;
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);
739 } else {
740 nsIntSize recentSize;
741 wl_egl_window_get_attached_size(wl_container->eglwindow, &recentSize.width,
742 &recentSize.height);
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,
761 int corner_radius) {
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;