diff options
Diffstat (limited to 'widget/gtk/MozContainerWayland.cpp')
-rw-r--r-- | widget/gtk/MozContainerWayland.cpp | 819 |
1 files changed, 819 insertions, 0 deletions
diff --git a/widget/gtk/MozContainerWayland.cpp b/widget/gtk/MozContainerWayland.cpp new file mode 100644 index 0000000000..0d9aacc954 --- /dev/null +++ b/widget/gtk/MozContainerWayland.cpp @@ -0,0 +1,819 @@ +/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:expandtab:shiftwidth=2:tabstop=2: + */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +/* + * MozContainerWayland is a wrapper over MozContainer which provides + * wl_surface for MozContainer widget. + * + * The widget scheme looks like: + * + * --------------------------------------------------------- + * | mShell Gtk widget (contains wl_surface owned by Gtk+) | + * | | + * | --------------------------------------------------- | + * | | mContainer (contains wl_surface owned by Gtk+) | | + * | | | | + * | | --------------------------------------------- | | + * | | | wl_subsurface (attached to wl_surface | | | + * | | | of mContainer) | | | + * | | | | | | + * | | | | | | + * | | --------------------------------------------- | | + * | --------------------------------------------------- | + * --------------------------------------------------------- + * + * We draw to wl_subsurface owned by MozContainerWayland. + * We need to wait until wl_surface of mContainer is created + * and then we create and attach our wl_subsurface to it. + * + * First wl_subsurface creation has these steps: + * + * 1) moz_container_wayland_size_allocate() handler is called when + * mContainer size/position is known. + * It calls moz_container_wayland_surface_create_locked(), registers + * a frame callback handler + * moz_container_wayland_frame_callback_handler(). + * + * 2) moz_container_wayland_frame_callback_handler() is called + * when wl_surface owned by mozContainer is ready. + * We call initial_draw_cbs() handler and we can create our wl_subsurface + * on top of wl_surface owned by mozContainer. + * + * When MozContainer hides/show again, moz_container_wayland_size_allocate() + * handler may not be called as MozContainer size is set. So after first + * show/hide sequence use moz_container_wayland_map_event() to create + * wl_subsurface of MozContainer. + */ + +#include "MozContainer.h" + +#include <dlfcn.h> +#include <glib.h> +#include <stdio.h> +#include <wayland-egl.h> + +#include "mozilla/gfx/gfxVars.h" +#include "mozilla/StaticPrefs_widget.h" +#include "nsGtkUtils.h" +#include "nsWaylandDisplay.h" +#include "base/task.h" + +#ifdef MOZ_LOGGING + +# include "mozilla/Logging.h" +# include "nsTArray.h" +# include "Units.h" +# include "nsWindow.h" +extern mozilla::LazyLogModule gWidgetWaylandLog; +extern mozilla::LazyLogModule gWidgetLog; +# define LOGWAYLAND(...) \ + MOZ_LOG(gWidgetWaylandLog, mozilla::LogLevel::Debug, (__VA_ARGS__)) +# define LOGCONTAINER(...) \ + MOZ_LOG(gWidgetLog, mozilla::LogLevel::Debug, (__VA_ARGS__)) +#else +# define LOGWAYLAND(...) +# define LOGCONTAINER(...) +#endif /* MOZ_LOGGING */ + +using namespace mozilla; +using namespace mozilla::widget; + +/* init methods */ +static void moz_container_wayland_destroy(GtkWidget* widget); + +/* widget class methods */ +static void moz_container_wayland_map(GtkWidget* widget); +static gboolean moz_container_wayland_map_event(GtkWidget* widget, + GdkEventAny* event); +static void moz_container_wayland_size_allocate(GtkWidget* widget, + GtkAllocation* allocation); +static bool moz_container_wayland_surface_create_locked( + const MutexAutoLock& aProofOfLock, MozContainer* container); +static void moz_container_wayland_set_opaque_region_locked( + const MutexAutoLock& aProofOfLock, MozContainer* container); + +// Lock mozcontainer and get wayland surface of it. You need to pair with +// moz_container_wayland_surface_unlock() even +// if moz_container_wayland_surface_lock() fails and returns nullptr. +static struct wl_surface* moz_container_wayland_surface_lock( + MozContainer* container); +static void moz_container_wayland_surface_unlock(MozContainer* container, + struct wl_surface** surface); + +MozContainerSurfaceLock::MozContainerSurfaceLock(MozContainer* aContainer) { + mContainer = aContainer; + mSurface = moz_container_wayland_surface_lock(aContainer); +} +MozContainerSurfaceLock::~MozContainerSurfaceLock() { + moz_container_wayland_surface_unlock(mContainer, &mSurface); +} +struct wl_surface* MozContainerSurfaceLock::GetSurface() { return mSurface; } + +// Imlemented in MozContainer.cpp +void moz_container_realize(GtkWidget* widget); + +// Invalidate gtk wl_surface to commit changes to wl_subsurface. +// wl_subsurface changes are effective when parent surface is commited. +static void moz_container_wayland_invalidate(MozContainer* container) { + LOGWAYLAND("moz_container_wayland_invalidate [%p]\n", + (void*)moz_container_get_nsWindow(container)); + + GdkWindow* window = gtk_widget_get_window(GTK_WIDGET(container)); + if (!window) { + LOGWAYLAND(" Failed - missing GdkWindow!\n"); + return; + } + gdk_window_invalidate_rect(window, nullptr, true); +} + +// Route input to parent wl_surface owned by Gtk+ so we get input +// events from Gtk+. +static void moz_container_clear_input_region(MozContainer* container) { + struct wl_compositor* compositor = WaylandDisplayGet()->GetCompositor(); + MozContainerWayland* wl_container = &container->wl_container; + wl_region* region = wl_compositor_create_region(compositor); + wl_surface_set_input_region(wl_container->surface, region); + wl_region_destroy(region); +} + +static void moz_container_wayland_move_locked(const MutexAutoLock& aProofOfLock, + MozContainer* container, int dx, + int dy) { + LOGCONTAINER("moz_container_wayland_move [%p] %d,%d\n", + (void*)moz_container_get_nsWindow(container), dx, dy); + + MozContainerWayland* wl_container = &container->wl_container; + if (!wl_container->subsurface || (wl_container->subsurface_dx == dx && + wl_container->subsurface_dy == dy)) { + return; + } + + wl_container->subsurface_dx = dx; + wl_container->subsurface_dy = dy; + wl_subsurface_set_position(wl_container->subsurface, + wl_container->subsurface_dx, + wl_container->subsurface_dy); +} + +// This is called from layout/compositor code only with +// size equal to GL rendering context. Otherwise there are +// rendering artifacts as wl_egl_window size does not match +// GL rendering pipeline setup. +void moz_container_wayland_egl_window_set_size(MozContainer* container, + nsIntSize aSize) { + MozContainerWayland* wl_container = &container->wl_container; + MutexAutoLock lock(*wl_container->container_lock); + if (wl_container->eglwindow) { + wl_egl_window_resize(wl_container->eglwindow, aSize.width, aSize.height, 0, + 0); + } +} + +void moz_container_wayland_class_init(MozContainerClass* klass) { + /*GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + GtkObjectClass *object_class = GTK_OBJECT_CLASS (klass); */ + GtkWidgetClass* widget_class = GTK_WIDGET_CLASS(klass); + + widget_class->map = moz_container_wayland_map; + widget_class->map_event = moz_container_wayland_map_event; + widget_class->destroy = moz_container_wayland_destroy; + widget_class->realize = moz_container_realize; + widget_class->size_allocate = moz_container_wayland_size_allocate; +} + +void moz_container_wayland_init(MozContainerWayland* container) { + container->surface = nullptr; + container->subsurface = nullptr; + container->eglwindow = nullptr; + container->frame_callback_handler = nullptr; + container->viewport = nullptr; + container->ready_to_draw = false; + container->opaque_region_needs_updates = false; + container->opaque_region_corner_radius = 0; + container->opaque_region_used = false; + container->subsurface_dx = 0; + container->subsurface_dy = 0; + container->before_first_size_alloc = true; + container->buffer_scale = 1; + container->initial_draw_cbs.clear(); + container->container_lock = new mozilla::Mutex("MozContainer lock"); + container->commit_to_parent = false; + container->waiting_to_show = false; +} + +static void moz_container_wayland_destroy(GtkWidget* widget) { + MozContainerWayland* container = &MOZ_CONTAINER(widget)->wl_container; + if (!container->container_lock) { + // moz_container_wayland_init was not called - it's a hidden container. + return; + } + moz_container_wayland_clear_initial_draw_callback(MOZ_CONTAINER(widget)); + delete container->container_lock; + container->container_lock = nullptr; +} + +void moz_container_wayland_add_initial_draw_callback_locked( + MozContainer* container, const std::function<void(void)>& initial_draw_cb) { + MozContainerWayland* wl_container = &MOZ_CONTAINER(container)->wl_container; + + if (wl_container->ready_to_draw && !wl_container->surface) { + NS_WARNING( + "moz_container_wayland_add_or_fire_initial_draw_callback:" + " ready to draw without wayland surface!"); + } + MOZ_DIAGNOSTIC_ASSERT(!wl_container->ready_to_draw || !wl_container->surface); + wl_container->initial_draw_cbs.push_back(initial_draw_cb); +} + +void moz_container_wayland_add_or_fire_initial_draw_callback( + MozContainer* container, const std::function<void(void)>& initial_draw_cb) { + MozContainerWayland* wl_container = &MOZ_CONTAINER(container)->wl_container; + { + MutexAutoLock lock(*container->wl_container.container_lock); + if (wl_container->ready_to_draw && !wl_container->surface) { + NS_WARNING( + "moz_container_wayland_add_or_fire_initial_draw_callback: ready to " + "draw " + "without wayland surface!"); + } + if (!wl_container->ready_to_draw || !wl_container->surface) { + wl_container->initial_draw_cbs.push_back(initial_draw_cb); + return; + } + } + + // We're ready to draw as + // wl_container->ready_to_draw && wl_container->surface + // call the callback directly instead of store them. + initial_draw_cb(); +} + +static void moz_container_wayland_clear_initial_draw_callback_locked( + const MutexAutoLock& aProofOfLock, MozContainer* container) { + MozContainerWayland* wl_container = &MOZ_CONTAINER(container)->wl_container; + MozClearPointer(wl_container->frame_callback_handler, wl_callback_destroy); + wl_container->initial_draw_cbs.clear(); +} + +void moz_container_wayland_clear_initial_draw_callback( + MozContainer* container) { + MutexAutoLock lock(*container->wl_container.container_lock); + moz_container_wayland_clear_initial_draw_callback_locked(lock, container); +} + +static void moz_container_wayland_frame_callback_handler( + void* data, struct wl_callback* callback, uint32_t time) { + MozContainerWayland* wl_container = &MOZ_CONTAINER(data)->wl_container; + + LOGWAYLAND( + "%s [%p] frame_callback_handler %p ready_to_draw %d (set to true)" + " initial_draw callback %zd\n", + __FUNCTION__, (void*)moz_container_get_nsWindow(MOZ_CONTAINER(data)), + (void*)wl_container->frame_callback_handler, wl_container->ready_to_draw, + wl_container->initial_draw_cbs.size()); + + std::vector<std::function<void(void)>> cbs; + { + // Protect mozcontainer internals changes by container_lock. + MutexAutoLock lock(*wl_container->container_lock); + MozClearPointer(wl_container->frame_callback_handler, wl_callback_destroy); + // It's possible that container is already unmapped so quit in such case. + if (!wl_container->surface) { + LOGWAYLAND(" container is unmapped, quit."); + if (!wl_container->initial_draw_cbs.empty()) { + NS_WARNING("Unmapping MozContainer with active draw callback!"); + wl_container->initial_draw_cbs.clear(); + } + return; + } + if (wl_container->ready_to_draw) { + return; + } + wl_container->ready_to_draw = true; + cbs = std::move(wl_container->initial_draw_cbs); + } + + // Call the callbacks registered by + // moz_container_wayland_add_or_fire_initial_draw_callback(). + // and we can't do that under mozcontainer lock. + for (auto const& cb : cbs) { + cb(); + } +} + +static const struct wl_callback_listener moz_container_frame_listener = { + moz_container_wayland_frame_callback_handler}; + +static void after_frame_clock_after_paint(GdkFrameClock* clock, + MozContainer* container) { + MozContainerSurfaceLock lock(container); + struct wl_surface* surface = lock.GetSurface(); + if (surface) { + wl_surface_commit(surface); + } +} + +static bool moz_gdk_wayland_window_add_frame_callback_surface_locked( + const MutexAutoLock& aProofOfLock, MozContainer* container) { + static auto sGdkWaylandWindowAddCallbackSurface = + (void (*)(GdkWindow*, struct wl_surface*))dlsym( + RTLD_DEFAULT, "gdk_wayland_window_add_frame_callback_surface"); + + if (!StaticPrefs::widget_wayland_opaque_region_enabled_AtStartup() || + !sGdkWaylandWindowAddCallbackSurface) { + return false; + } + + GdkWindow* window = gtk_widget_get_window(GTK_WIDGET(container)); + MozContainerWayland* wl_container = &container->wl_container; + + sGdkWaylandWindowAddCallbackSurface(window, wl_container->surface); + + GdkFrameClock* frame_clock = gdk_window_get_frame_clock(window); + g_signal_connect_after(frame_clock, "after-paint", + G_CALLBACK(after_frame_clock_after_paint), container); + return true; +} + +static void moz_gdk_wayland_window_remove_frame_callback_surface_locked( + const MutexAutoLock& aProofOfLock, MozContainer* container) { + static auto sGdkWaylandWindowRemoveCallbackSurface = + (void (*)(GdkWindow*, struct wl_surface*))dlsym( + RTLD_DEFAULT, "gdk_wayland_window_remove_frame_callback_surface"); + + if (!sGdkWaylandWindowRemoveCallbackSurface) { + return; + } + + GdkWindow* window = gtk_widget_get_window(GTK_WIDGET(container)); + MozContainerWayland* wl_container = &container->wl_container; + + if (wl_container->surface) { + sGdkWaylandWindowRemoveCallbackSurface(window, wl_container->surface); + } + + GdkFrameClock* frame_clock = gdk_window_get_frame_clock(window); + g_signal_handlers_disconnect_by_func( + frame_clock, FuncToGpointer(after_frame_clock_after_paint), container); +} + +void moz_container_wayland_unmap(GtkWidget* widget) { + MozContainer* container = MOZ_CONTAINER(widget); + MozContainerWayland* wl_container = &container->wl_container; + MutexAutoLock lock(*wl_container->container_lock); + + LOGCONTAINER("%s [%p]\n", __FUNCTION__, + (void*)moz_container_get_nsWindow(container)); + + moz_container_wayland_clear_initial_draw_callback_locked(lock, container); + + if (wl_container->opaque_region_used) { + moz_gdk_wayland_window_remove_frame_callback_surface_locked(lock, + container); + } + if (wl_container->commit_to_parent) { + wl_container->surface = nullptr; + } + + MozClearPointer(wl_container->eglwindow, wl_egl_window_destroy); + MozClearPointer(wl_container->subsurface, wl_subsurface_destroy); + MozClearPointer(wl_container->surface, wl_surface_destroy); + MozClearPointer(wl_container->viewport, wp_viewport_destroy); + + wl_container->ready_to_draw = false; + wl_container->buffer_scale = 1; +} + +static gboolean moz_container_wayland_map_event(GtkWidget* widget, + GdkEventAny* event) { + MozContainerWayland* wl_container = &MOZ_CONTAINER(widget)->wl_container; + + LOGCONTAINER("%s [%p]\n", __FUNCTION__, + (void*)moz_container_get_nsWindow(MOZ_CONTAINER(widget))); + + // We need to mark MozContainer as mapped to make sure + // moz_container_wayland_unmap() is called on hide/withdraw. + gtk_widget_set_mapped(widget, TRUE); + + // Make sure we're on main thread as we can't lock mozContainer here + // due to moz_container_wayland_add_or_fire_initial_draw_callback() call + // below. + MOZ_DIAGNOSTIC_ASSERT(NS_IsMainThread()); + + // Set waiting_to_show flag. It means the mozcontainer is cofigured/mapped + // and it's supposed to be visible. *But* it's really visible when we get + // moz_container_wayland_add_or_fire_initial_draw_callback() which means + // wayland compositor makes it live. + wl_container->waiting_to_show = true; + MozContainer* container = MOZ_CONTAINER(widget); + moz_container_wayland_add_or_fire_initial_draw_callback( + container, [container]() -> void { + LOGCONTAINER( + "[%p] moz_container_wayland_add_or_fire_initial_draw_callback set " + "visible", + moz_container_get_nsWindow(container)); + moz_container_wayland_clear_waiting_to_show_flag(container); + }); + + MutexAutoLock lock(*wl_container->container_lock); + + // Don't create wl_subsurface in map_event when it's already created or + // if we create it for the first time. + if (wl_container->ready_to_draw || wl_container->before_first_size_alloc) { + return FALSE; + } + + if (!wl_container->surface) { + if (!moz_container_wayland_surface_create_locked(lock, + MOZ_CONTAINER(widget))) { + return FALSE; + } + } + + moz_container_wayland_set_scale_factor_locked(lock, MOZ_CONTAINER(widget)); + moz_container_wayland_set_opaque_region_locked(lock, MOZ_CONTAINER(widget)); + moz_container_clear_input_region(MOZ_CONTAINER(widget)); + moz_container_wayland_invalidate(MOZ_CONTAINER(widget)); + return FALSE; +} + +void moz_container_wayland_map(GtkWidget* widget) { + LOGCONTAINER("%s [%p]\n", __FUNCTION__, + (void*)moz_container_get_nsWindow(MOZ_CONTAINER(widget))); + + g_return_if_fail(IS_MOZ_CONTAINER(widget)); + gtk_widget_set_mapped(widget, TRUE); + + if (gtk_widget_get_has_window(widget)) { + gdk_window_show(gtk_widget_get_window(widget)); + } +} + +void moz_container_wayland_size_allocate(GtkWidget* widget, + GtkAllocation* allocation) { + MozContainer* container; + GtkAllocation tmp_allocation; + + g_return_if_fail(IS_MOZ_CONTAINER(widget)); + + LOGCONTAINER("moz_container_wayland_size_allocate [%p] %d,%d -> %d x %d\n", + (void*)moz_container_get_nsWindow(MOZ_CONTAINER(widget)), + allocation->x, allocation->y, allocation->width, + allocation->height); + + /* short circuit if you can */ + container = MOZ_CONTAINER(widget); + gtk_widget_get_allocation(widget, &tmp_allocation); + if (!container->children && tmp_allocation.x == allocation->x && + tmp_allocation.y == allocation->y && + tmp_allocation.width == allocation->width && + tmp_allocation.height == allocation->height) { + return; + } + gtk_widget_set_allocation(widget, allocation); + + if (gtk_widget_get_has_window(widget) && gtk_widget_get_realized(widget)) { + gdk_window_move_resize(gtk_widget_get_window(widget), allocation->x, + allocation->y, allocation->width, + allocation->height); + // We need to position our subsurface according to GdkWindow + // when offset changes (GdkWindow is maximized for instance). + // see gtk-clutter-embed.c for reference. + MutexAutoLock lock(*container->wl_container.container_lock); + if (!container->wl_container.surface) { + if (!moz_container_wayland_surface_create_locked(lock, container)) { + return; + } + } + moz_container_wayland_set_scale_factor_locked(lock, container); + moz_container_wayland_set_opaque_region_locked(lock, container); + moz_container_wayland_move_locked(lock, container, allocation->x, + allocation->y); + moz_container_clear_input_region(container); + moz_container_wayland_invalidate(MOZ_CONTAINER(widget)); + container->wl_container.before_first_size_alloc = false; + } +} + +static wl_region* moz_container_wayland_create_opaque_region( + int aX, int aY, int aWidth, int aHeight, int aCornerRadius) { + struct wl_compositor* compositor = WaylandDisplayGet()->GetCompositor(); + wl_region* region = wl_compositor_create_region(compositor); + wl_region_add(region, aX, aY, aWidth, aHeight); + if (aCornerRadius) { + wl_region_subtract(region, aX, aY, aCornerRadius, aCornerRadius); + wl_region_subtract(region, aX + aWidth - aCornerRadius, aY, aCornerRadius, + aCornerRadius); + } + return region; +} + +static void moz_container_wayland_set_opaque_region_locked( + const MutexAutoLock& aProofOfLock, MozContainer* container) { + MozContainerWayland* wl_container = &container->wl_container; + + if (!wl_container->opaque_region_needs_updates) { + return; + } + + if (!wl_container->opaque_region_used) { + wl_container->opaque_region_needs_updates = false; + return; + } + + GtkAllocation allocation; + gtk_widget_get_allocation(GTK_WIDGET(container), &allocation); + + wl_region* region = moz_container_wayland_create_opaque_region( + 0, 0, allocation.width, allocation.height, + wl_container->opaque_region_corner_radius); + wl_surface_set_opaque_region(wl_container->surface, region); + wl_region_destroy(region); + wl_container->opaque_region_needs_updates = false; +} + +static void moz_container_wayland_set_opaque_region(MozContainer* container) { + MozContainerWayland* wl_container = &container->wl_container; + MutexAutoLock lock(*wl_container->container_lock); + if (wl_container->surface) { + moz_container_wayland_set_opaque_region_locked(lock, container); + } +} + +static void moz_container_wayland_surface_set_scale_locked( + const MutexAutoLock& aProofOfLock, MozContainerWayland* wl_container, + int scale) { + if (wl_container->buffer_scale == scale) { + return; + } + + LOGCONTAINER("%s scale %d\n", __FUNCTION__, scale); + + // There is a chance that the attached wl_buffer has not yet been doubled + // on the main thread when scale factor changed to 2. This leads to + // crash with the following message: + // Buffer size (AxB) must be an integer multiple of the buffer_scale (2) + // Removing the possibly wrong wl_buffer to prevent that crash: + wl_surface_attach(wl_container->surface, nullptr, 0, 0); + wl_surface_set_buffer_scale(wl_container->surface, scale); + wl_container->buffer_scale = scale; +} + +void moz_container_wayland_set_scale_factor_locked( + const MutexAutoLock& aProofOfLock, MozContainer* container) { + if (gfx::gfxVars::UseWebRenderCompositor()) { + // the compositor backend handles scaling itself + return; + } + + MozContainerWayland* wl_container = &container->wl_container; + wl_container->container_lock->AssertCurrentThreadOwns(); + + nsWindow* window = moz_container_get_nsWindow(container); + MOZ_DIAGNOSTIC_ASSERT(window); + if (window->UseFractionalScale()) { + if (!wl_container->viewport) { + wl_container->viewport = wp_viewporter_get_viewport( + WaylandDisplayGet()->GetViewporter(), wl_container->surface); + } + + GdkWindow* gdkWindow = gtk_widget_get_window(GTK_WIDGET(container)); + wp_viewport_set_destination(wl_container->viewport, + gdk_window_get_width(gdkWindow), + gdk_window_get_height(gdkWindow)); + } else { + moz_container_wayland_surface_set_scale_locked( + aProofOfLock, wl_container, window->GdkCeiledScaleFactor()); + } +} + +void moz_container_wayland_set_scale_factor(MozContainer* container) { + MutexAutoLock lock(*container->wl_container.container_lock); + if (container->wl_container.surface) { + moz_container_wayland_set_scale_factor_locked(lock, container); + } +} + +bool moz_container_wayland_size_matches_scale_factor_locked( + const MutexAutoLock& aProofOfLock, MozContainer* container, int aWidth, + int aHeight) { + return aWidth % container->wl_container.buffer_scale == 0 && + aHeight % container->wl_container.buffer_scale == 0; +} + +static bool moz_container_wayland_surface_create_locked( + const MutexAutoLock& aProofOfLock, MozContainer* container) { + MozContainerWayland* wl_container = &container->wl_container; + + LOGWAYLAND("%s [%p]\n", __FUNCTION__, + (void*)moz_container_get_nsWindow(container)); + + GdkWindow* window = gtk_widget_get_window(GTK_WIDGET(container)); + MOZ_DIAGNOSTIC_ASSERT(window); + + wl_surface* parent_surface = gdk_wayland_window_get_wl_surface(window); + if (!parent_surface) { + LOGWAYLAND(" Failed - missing parent surface!"); + return false; + } + LOGWAYLAND(" gtk wl_surface %p ID %d\n", (void*)parent_surface, + wl_proxy_get_id((struct wl_proxy*)parent_surface)); + + if (wl_container->commit_to_parent) { + LOGWAYLAND(" commit to parent"); + wl_container->surface = parent_surface; + NS_DispatchToCurrentThread(NewRunnableFunction( + "moz_container_wayland_frame_callback_handler", + &moz_container_wayland_frame_callback_handler, container, nullptr, 0)); + return true; + } + + // Available as of GTK 3.8+ + struct wl_compositor* compositor = WaylandDisplayGet()->GetCompositor(); + wl_container->surface = wl_compositor_create_surface(compositor); + if (!wl_container->surface) { + LOGWAYLAND(" Failed - can't create surface!"); + return false; + } + + wl_container->subsurface = + wl_subcompositor_get_subsurface(WaylandDisplayGet()->GetSubcompositor(), + wl_container->surface, parent_surface); + if (!wl_container->subsurface) { + MozClearPointer(wl_container->surface, wl_surface_destroy); + LOGWAYLAND(" Failed - can't create sub-surface!"); + return false; + } + wl_subsurface_set_desync(wl_container->subsurface); + + // Try to guess subsurface offset to avoid potential flickering. + int dx, dy; + if (moz_container_get_nsWindow(container)->GetCSDDecorationOffset(&dx, &dy)) { + wl_container->subsurface_dx = dx; + wl_container->subsurface_dy = dy; + wl_subsurface_set_position(wl_container->subsurface, dx, dy); + LOGWAYLAND(" guessing subsurface position %d %d\n", dx, dy); + } + + // If there's pending frame callback it's for wrong parent surface, + // so delete it. + if (wl_container->frame_callback_handler) { + MozClearPointer(wl_container->frame_callback_handler, wl_callback_destroy); + } + wl_container->frame_callback_handler = wl_surface_frame(parent_surface); + wl_callback_add_listener(wl_container->frame_callback_handler, + &moz_container_frame_listener, container); + LOGWAYLAND( + " created frame callback ID %d\n", + wl_proxy_get_id((struct wl_proxy*)wl_container->frame_callback_handler)); + + wl_surface_commit(wl_container->surface); + wl_display_flush(WaylandDisplayGet()->GetDisplay()); + + wl_container->opaque_region_used = + moz_gdk_wayland_window_add_frame_callback_surface_locked(aProofOfLock, + container); + + LOGWAYLAND(" created surface %p ID %d\n", (void*)wl_container->surface, + wl_proxy_get_id((struct wl_proxy*)wl_container->surface)); + return true; +} + +struct wl_surface* moz_container_wayland_surface_lock(MozContainer* container) + MOZ_NO_THREAD_SAFETY_ANALYSIS { + // LOGWAYLAND("%s [%p] surface %p ready_to_draw %d\n", __FUNCTION__, + // (void*)container, (void*)container->wl_container.surface, + // container->wl_container.ready_to_draw); + container->wl_container.container_lock->Lock(); + if (!container->wl_container.surface || + !container->wl_container.ready_to_draw) { + return nullptr; + } + return container->wl_container.surface; +} + +void moz_container_wayland_surface_unlock(MozContainer* container, + struct wl_surface** surface) + MOZ_NO_THREAD_SAFETY_ANALYSIS { + // Temporarily disabled to avoid log noise + // LOGWAYLAND("%s [%p] surface %p\n", __FUNCTION__, (void*)container, + // (void*)container->wl_container.surface); + if (*surface) { + *surface = nullptr; + } + container->wl_container.container_lock->Unlock(); +} + +bool moz_container_wayland_egl_window_needs_size_update(MozContainer* container, + nsIntSize aSize, + int aScale) { + MozContainerWayland* wl_container = &container->wl_container; + if (!wl_container->eglwindow) { + return false; + } + if (wl_container->buffer_scale != aScale) { + return true; + } + nsIntSize recentSize; + wl_egl_window_get_attached_size(wl_container->eglwindow, &recentSize.width, + &recentSize.height); + return aSize != recentSize; +} + +struct wl_egl_window* moz_container_wayland_get_egl_window( + MozContainer* container, double scale) { + MozContainerWayland* wl_container = &container->wl_container; + + LOGCONTAINER("%s [%p] eglwindow %p scale %d\n", __FUNCTION__, + (void*)moz_container_get_nsWindow(container), + (void*)wl_container->eglwindow, (int)scale); + + MutexAutoLock lock(*wl_container->container_lock); + if (!wl_container->surface || !wl_container->ready_to_draw) { + LOGCONTAINER( + " quit, wl_container->surface %p wl_container->ready_to_draw %d\n", + wl_container->surface, wl_container->ready_to_draw); + return nullptr; + } + + GdkWindow* window = gtk_widget_get_window(GTK_WIDGET(container)); + nsIntSize requestedSize((int)round(gdk_window_get_width(window) * scale), + (int)round(gdk_window_get_height(window) * scale)); + + if (!wl_container->eglwindow) { + wl_container->eglwindow = wl_egl_window_create( + wl_container->surface, requestedSize.width, requestedSize.height); + + LOGCONTAINER("%s [%p] created eglwindow %p size %d x %d (with scale %f)\n", + __FUNCTION__, (void*)moz_container_get_nsWindow(container), + (void*)wl_container->eglwindow, requestedSize.width, + requestedSize.height, scale); + } else { + nsIntSize recentSize; + wl_egl_window_get_attached_size(wl_container->eglwindow, &recentSize.width, + &recentSize.height); + if (requestedSize != recentSize) { + LOGCONTAINER("%s [%p] resized to %d x %d (with scale %f)\n", __FUNCTION__, + (void*)moz_container_get_nsWindow(container), + requestedSize.width, requestedSize.height, scale); + wl_egl_window_resize(wl_container->eglwindow, requestedSize.width, + requestedSize.height, 0, 0); + } + } + moz_container_wayland_surface_set_scale_locked(lock, wl_container, + static_cast<int>(scale)); + return wl_container->eglwindow; +} + +gboolean moz_container_wayland_has_egl_window(MozContainer* container) { + return container->wl_container.eglwindow != nullptr; +} + +void moz_container_wayland_update_opaque_region(MozContainer* container, + int corner_radius) { + MozContainerWayland* wl_container = &container->wl_container; + wl_container->opaque_region_needs_updates = true; + wl_container->opaque_region_corner_radius = corner_radius; + + // When GL compositor / WebRender is used, + // moz_container_wayland_get_egl_window() is called only once when window + // is created or resized so update opaque region now. + if (moz_container_wayland_has_egl_window(container)) { + moz_container_wayland_set_opaque_region(container); + } +} + +gboolean moz_container_wayland_can_draw(MozContainer* container) { + MozContainerWayland* wl_container = &container->wl_container; + MutexAutoLock lock(*wl_container->container_lock); + return wl_container->ready_to_draw; +} + +double moz_container_wayland_get_scale(MozContainer* container) { + nsWindow* window = moz_container_get_nsWindow(container); + return window ? window->FractionalScaleFactor() : 1; +} + +void moz_container_wayland_set_commit_to_parent(MozContainer* container) { + MozContainerWayland* wl_container = &container->wl_container; + MOZ_DIAGNOSTIC_ASSERT(!wl_container->surface); + wl_container->commit_to_parent = true; +} + +bool moz_container_wayland_is_commiting_to_parent(MozContainer* container) { + MozContainerWayland* wl_container = &container->wl_container; + return wl_container->commit_to_parent; +} + +bool moz_container_wayland_is_waiting_to_show(MozContainer* container) { + MOZ_DIAGNOSTIC_ASSERT(NS_IsMainThread()); + return container->wl_container.waiting_to_show; +} + +void moz_container_wayland_clear_waiting_to_show_flag(MozContainer* container) { + MOZ_DIAGNOSTIC_ASSERT(NS_IsMainThread()); + container->wl_container.waiting_to_show = false; +} |