Backed out changeset 2450366cf7ca (bug 1891629) for causing win msix mochitest failures
[gecko.git] / widget / gtk / MozContainerWayland.cpp
blob3a4660f4e061a2bc8528867bddcfead42f044c8d
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,
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));
114 if (!window) {
115 LOGWAYLAND(" Failed - missing GdkWindow!\n");
116 return;
118 gdk_window_invalidate_rect(window, nullptr, true);
121 // Route input to parent wl_surface owned by Gtk+ so we get input
122 // events from Gtk+.
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,
133 int dy) {
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)) {
140 return;
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) {
165 return true;
168 if (wl_container->buffer_scale != aScale) {
169 moz_container_wayland_set_scale_factor_locked(lock, container, aScale);
172 /* Enable for size changes logging
173 LOGCONTAINER(
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) {
191 NS_WARNING(
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) {
205 NS_WARNING(
206 "moz_container_wayland_add_or_fire_initial_draw_callback: ready to "
207 "draw "
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);
212 return;
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.
219 initial_draw_cb();
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);
239 LOGWAYLAND(
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();
258 return;
260 if (wl_container->ready_to_draw) {
261 return;
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) {
271 cb();
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();
282 if (surface) {
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) {
295 return false;
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);
306 return true;
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) {
316 return;
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,
348 container);
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)) {
376 return false;
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
381 // below.
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 {
392 LOGCONTAINER(
393 "[%p] moz_container_wayland_add_or_fire_initial_draw_callback set "
394 "visible",
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) {
404 return FALSE;
407 if (!wl_container->surface) {
408 if (!moz_container_wayland_surface_create_locked(lock,
409 MOZ_CONTAINER(widget))) {
410 return FALSE;
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));
423 return FALSE;
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,
435 allocation->height);
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) {
442 return;
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,
449 allocation->height);
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)) {
457 return;
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,
468 allocation->y);
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) {
481 return;
484 wl_container->opaque_region_needs_updates = false;
485 if (!wl_container->opaque_region_used) {
486 return;
489 wl_region* region =
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,
501 int scale) {
502 if (!wl_container->surface) {
503 return;
505 if (wl_container->buffer_scale == scale) {
506 return;
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
543 return;
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,
557 container);
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));
573 return;
578 moz_container_wayland_surface_set_scale_locked(aProofOfLock, wl_container,
579 aScale);
582 bool moz_container_wayland_size_matches_scale_factor_locked(
583 const MutexAutoLock& aProofOfLock, MozContainer* container, int aWidth,
584 int aHeight) {
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!");
602 return false;
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));
613 return true;
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!");
621 return false;
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!");
630 return false;
632 wl_subsurface_set_desync(wl_container->subsurface);
634 // Try to guess subsurface offset to avoid potential flickering.
635 int dx, dy;
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,
644 // so delete it.
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);
651 LOGWAYLAND(
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,
660 container);
662 LOGWAYLAND(" created surface %p ID %d\n", (void*)wl_container->surface,
663 wl_proxy_get_id((struct wl_proxy*)wl_container->surface));
664 return true;
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) {
675 return nullptr;
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);
686 if (*surface) {
687 *surface = nullptr;
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) {
702 LOGCONTAINER(
703 " quit, wl_container->surface %p wl_container->ready_to_draw %d\n",
704 wl_container->surface, wl_container->ready_to_draw);
705 return nullptr;
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);
720 } else {
721 nsIntSize recentSize;
722 wl_egl_window_get_attached_size(wl_container->eglwindow, &recentSize.width,
723 &recentSize.height);
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;