From 2aa4a82499d4becd2284cdb482213d541b8804dd Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 28 Apr 2024 16:29:10 +0200 Subject: Adding upstream version 86.0.1. Signed-off-by: Daniel Baumann --- widget/gtk/MozContainerWayland.cpp | 514 +++++++++++++++++++++++++++++++++++++ 1 file changed, 514 insertions(+) create mode 100644 widget/gtk/MozContainerWayland.cpp (limited to 'widget/gtk/MozContainerWayland.cpp') diff --git a/widget/gtk/MozContainerWayland.cpp b/widget/gtk/MozContainerWayland.cpp new file mode 100644 index 0000000000..88fbef7277 --- /dev/null +++ b/widget/gtk/MozContainerWayland.cpp @@ -0,0 +1,514 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:expandtab:shiftwidth=4:tabstop=4: + */ +/* 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/. */ + +#include "MozContainer.h" + +#include +#include +#include +#include "nsWaylandDisplay.h" +#include "gfxPlatformGtk.h" +#include +#include +#include + +#undef LOG +#ifdef MOZ_LOGGING + +# include "mozilla/Logging.h" +# include "nsTArray.h" +# include "Units.h" +# include "nsWindow.h" +extern mozilla::LazyLogModule gWidgetWaylandLog; +# define LOGWAYLAND(args) \ + MOZ_LOG(gWidgetWaylandLog, mozilla::LogLevel::Debug, args) +#else +# define LOGWAYLAND(args) +#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_unmap(GtkWidget* widget); +static void moz_container_wayland_size_allocate(GtkWidget* widget, + GtkAllocation* allocation); + +// Imlemented in MozContainer.cpp +void moz_container_realize(GtkWidget* widget); + +static void moz_container_wayland_move_locked(MozContainer* container, int dx, + int dy) { + LOGWAYLAND(("moz_container_wayland_move_locked [%p] %d,%d\n", + (void*)container, dx, dy)); + + MozContainerWayland* wl_container = &container->wl_container; + + wl_container->subsurface_dx = dx; + wl_container->subsurface_dy = dy; + wl_container->surface_position_needs_update = true; + + // Wayland subsurface is not created yet. + if (!wl_container->subsurface) { + return; + } + + // wl_subsurface_set_position is actually property of parent surface + // which is effective when parent surface is commited. + wl_subsurface_set_position(wl_container->subsurface, + wl_container->subsurface_dx, + wl_container->subsurface_dy); + wl_container->surface_position_needs_update = false; + + GdkWindow* window = gtk_widget_get_window(GTK_WIDGET(container)); + if (window) { + GdkRectangle rect = (GdkRectangle){0, 0, gdk_window_get_width(window), + gdk_window_get_height(window)}; + gdk_window_invalidate_rect(window, &rect, false); + } +} + +static void moz_container_wayland_move(MozContainer* container, int dx, + int dy) { + MutexAutoLock lock(*container->wl_container.container_lock); + LOGWAYLAND( + ("moz_container_wayland_move [%p] %d,%d\n", (void*)container, dx, dy)); + moz_container_wayland_move_locked(container, dx, 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, + int width, int height) { + MozContainerWayland* wl_container = &container->wl_container; + MutexAutoLock lock(*wl_container->container_lock); + if (wl_container->eglwindow) { + wl_egl_window_resize(wl_container->eglwindow, width, 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->unmap = moz_container_wayland_unmap; + 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->frame_callback_handler_surface_id = -1; + container->ready_to_draw = false; + container->opaque_region_needs_update = false; + container->opaque_region_subtract_corners = false; + container->surface_needs_clear = true; + container->subsurface_dx = 0; + container->subsurface_dy = 0; + container->surface_position_needs_update = 0; + container->initial_draw_cbs.clear(); + container->container_lock = new mozilla::Mutex("MozContainer lock"); +} + +static void moz_container_wayland_destroy(GtkWidget* widget) { + MozContainerWayland* container = &MOZ_CONTAINER(widget)->wl_container; + delete container->container_lock; + container->container_lock = nullptr; +} + +void moz_container_wayland_add_initial_draw_callback( + MozContainer* container, const std::function& initial_draw_cb) { + container->wl_container.initial_draw_cbs.push_back(initial_draw_cb); +} + +wl_surface* moz_gtk_widget_get_wl_surface(GtkWidget* aWidget) { + GdkWindow* window = gtk_widget_get_window(aWidget); + wl_surface* surface = gdk_wayland_window_get_wl_surface(window); + + LOGWAYLAND(("moz_gtk_widget_get_wl_surface [%p] wl_surface %p ID %d\n", + (void*)aWidget, (void*)surface, + surface ? wl_proxy_get_id((struct wl_proxy*)surface) : -1)); + + return surface; +} + +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(data), + (void*)wl_container->frame_callback_handler, wl_container->ready_to_draw, + wl_container->initial_draw_cbs.size())); + + g_clear_pointer(&wl_container->frame_callback_handler, wl_callback_destroy); + wl_container->frame_callback_handler_surface_id = -1; + + if (!wl_container->ready_to_draw) { + wl_container->ready_to_draw = true; + for (auto const& cb : wl_container->initial_draw_cbs) { + cb(); + } + wl_container->initial_draw_cbs.clear(); + } +} + +static const struct wl_callback_listener moz_container_frame_listener = { + moz_container_wayland_frame_callback_handler}; + +static void moz_container_wayland_request_parent_frame_callback( + MozContainer* container) { + MozContainerWayland* wl_container = &container->wl_container; + + wl_surface* gtk_container_surface = + moz_gtk_widget_get_wl_surface(GTK_WIDGET(container)); + int gtk_container_surface_id = + gtk_container_surface + ? wl_proxy_get_id((struct wl_proxy*)gtk_container_surface) + : -1; + + LOGWAYLAND( + ("%s [%p] frame_callback_handler %p " + "frame_callback_handler_surface_id %d\n", + __FUNCTION__, (void*)container, wl_container->frame_callback_handler, + wl_container->frame_callback_handler_surface_id)); + + if (wl_container->frame_callback_handler && + wl_container->frame_callback_handler_surface_id == + gtk_container_surface_id) { + return; + } + + // If there's pending frame callback, delete it. + if (wl_container->frame_callback_handler) { + g_clear_pointer(&wl_container->frame_callback_handler, wl_callback_destroy); + } + + if (gtk_container_surface) { + wl_container->frame_callback_handler_surface_id = gtk_container_surface_id; + wl_container->frame_callback_handler = + wl_surface_frame(gtk_container_surface); + wl_callback_add_listener(wl_container->frame_callback_handler, + &moz_container_frame_listener, container); + } else { + wl_container->frame_callback_handler_surface_id = -1; + } +} + +static gboolean moz_container_wayland_map_event(GtkWidget* widget, + GdkEventAny* event) { + MozContainerWayland* wl_container = &MOZ_CONTAINER(widget)->wl_container; + + LOGWAYLAND(("%s begin [%p] ready_to_draw %d\n", __FUNCTION__, + (void*)MOZ_CONTAINER(widget), wl_container->ready_to_draw)); + + if (wl_container->ready_to_draw) { + return FALSE; + } + + moz_container_wayland_request_parent_frame_callback(MOZ_CONTAINER(widget)); + return FALSE; +} + +static void moz_container_wayland_unmap_internal(MozContainer* container) { + MozContainerWayland* wl_container = &container->wl_container; + MutexAutoLock lock(*wl_container->container_lock); + + g_clear_pointer(&wl_container->eglwindow, wl_egl_window_destroy); + g_clear_pointer(&wl_container->subsurface, wl_subsurface_destroy); + g_clear_pointer(&wl_container->surface, wl_surface_destroy); + g_clear_pointer(&wl_container->frame_callback_handler, wl_callback_destroy); + wl_container->frame_callback_handler_surface_id = -1; + + wl_container->surface_needs_clear = true; + wl_container->ready_to_draw = false; + + LOGWAYLAND(("%s [%p]\n", __FUNCTION__, (void*)container)); +} + +void moz_container_wayland_map(GtkWidget* 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)); + moz_container_wayland_map_event(widget, nullptr); + } +} + +void moz_container_wayland_unmap(GtkWidget* widget) { + g_return_if_fail(IS_MOZ_CONTAINER(widget)); + + gtk_widget_set_mapped(widget, FALSE); + + if (gtk_widget_get_has_window(widget)) { + gdk_window_hide(gtk_widget_get_window(widget)); + moz_container_wayland_unmap_internal(MOZ_CONTAINER(widget)); + } +} + +void moz_container_wayland_size_allocate(GtkWidget* widget, + GtkAllocation* allocation) { + MozContainer* container; + GtkAllocation tmp_allocation; + + g_return_if_fail(IS_MOZ_CONTAINER(widget)); + + LOGWAYLAND(("moz_container_wayland_size_allocate [%p] %d,%d -> %d x %d\n", + (void*)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. + if (gfxPlatformGtk::GetPlatform()->IsWaylandDisplay()) { + moz_container_wayland_move(MOZ_CONTAINER(widget), allocation->x, + allocation->y); + } + } +} + +static wl_region* moz_container_wayland_create_opaque_region( + int aX, int aY, int aWidth, int aHeight, bool aSubtractCorners) { + struct wl_compositor* compositor = WaylandDisplayGet()->GetCompositor(); + wl_region* region = wl_compositor_create_region(compositor); + wl_region_add(region, aX, aY, aWidth, aHeight); + if (aSubtractCorners) { + wl_region_subtract(region, aX, aY, TITLEBAR_SHAPE_MASK_HEIGHT, + TITLEBAR_SHAPE_MASK_HEIGHT); + wl_region_subtract(region, aX + aWidth - TITLEBAR_SHAPE_MASK_HEIGHT, aY, + TITLEBAR_SHAPE_MASK_HEIGHT, TITLEBAR_SHAPE_MASK_HEIGHT); + } + return region; +} + +static void moz_container_wayland_set_opaque_region_locked( + MozContainer* container) { + MozContainerWayland* wl_container = &container->wl_container; + + if (!wl_container->opaque_region_needs_update || !wl_container->surface) { + 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_subtract_corners); + wl_surface_set_opaque_region(wl_container->surface, region); + wl_region_destroy(region); + + wl_container->opaque_region_needs_update = false; +} + +static void moz_container_wayland_set_opaque_region(MozContainer* container) { + MutexAutoLock lock(*container->wl_container.container_lock); + moz_container_wayland_set_opaque_region_locked(container); +} + +static void moz_container_wayland_set_scale_factor_locked( + MozContainer* container) { + if (!container->wl_container.surface) { + return; + } + gpointer user_data = g_object_get_data(G_OBJECT(container), "nsWindow"); + nsWindow* wnd = static_cast(user_data); + + int scale = 1; + if (wnd) { + scale = wnd->GdkScaleFactor(); + } + wl_surface_set_buffer_scale(container->wl_container.surface, scale); +} + +void moz_container_wayland_set_scale_factor(MozContainer* container) { + MutexAutoLock lock(*container->wl_container.container_lock); + moz_container_wayland_set_scale_factor_locked(container); +} + +static struct wl_surface* moz_container_wayland_get_surface_locked( + MozContainer* container, nsWaylandDisplay* aWaylandDisplay) { + MozContainerWayland* wl_container = &container->wl_container; + + LOGWAYLAND(("%s [%p] surface %p ready_to_draw %d\n", __FUNCTION__, + (void*)container, (void*)wl_container->surface, + wl_container->ready_to_draw)); + + if (!wl_container->surface) { + if (!wl_container->ready_to_draw) { + moz_container_wayland_request_parent_frame_callback(container); + return nullptr; + } + wl_surface* parent_surface = + moz_gtk_widget_get_wl_surface(GTK_WIDGET(container)); + if (!parent_surface) { + return nullptr; + } + + // Available as of GTK 3.8+ + struct wl_compositor* compositor = aWaylandDisplay->GetCompositor(); + wl_container->surface = wl_compositor_create_surface(compositor); + if (!wl_container->surface) { + return nullptr; + } + + wl_container->subsurface = + wl_subcompositor_get_subsurface(aWaylandDisplay->GetSubcompositor(), + wl_container->surface, parent_surface); + if (!wl_container->subsurface) { + g_clear_pointer(&wl_container->surface, wl_surface_destroy); + return nullptr; + } + + GdkWindow* window = gtk_widget_get_window(GTK_WIDGET(container)); + gint x, y; + gdk_window_get_position(window, &x, &y); + moz_container_wayland_move_locked(container, x, y); + wl_subsurface_set_desync(wl_container->subsurface); + + // Route input to parent wl_surface owned by Gtk+ so we get input + // events from Gtk+. + wl_region* region = wl_compositor_create_region(compositor); + wl_surface_set_input_region(wl_container->surface, region); + wl_region_destroy(region); + + wl_surface_commit(wl_container->surface); + wl_display_flush(aWaylandDisplay->GetDisplay()); + + LOGWAYLAND(("%s [%p] created surface %p\n", __FUNCTION__, (void*)container, + (void*)wl_container->surface)); + } + + if (wl_container->surface_position_needs_update) { + moz_container_wayland_move_locked(container, wl_container->subsurface_dx, + wl_container->subsurface_dy); + } + + moz_container_wayland_set_opaque_region_locked(container); + moz_container_wayland_set_scale_factor_locked(container); + + return wl_container->surface; +} + +struct wl_surface* moz_container_wayland_surface_lock(MozContainer* container) { + GdkDisplay* display = gtk_widget_get_display(GTK_WIDGET(container)); + RefPtr waylandDisplay = WaylandDisplayGet(display); + + LOGWAYLAND(("%s [%p] surface %p\n", __FUNCTION__, (void*)container, + (void*)container->wl_container.surface)); + + container->wl_container.container_lock->Lock(); + struct wl_surface* surface = + moz_container_wayland_get_surface_locked(container, waylandDisplay); + if (surface == nullptr) { + container->wl_container.container_lock->Unlock(); + } + return surface; +} + +void moz_container_wayland_surface_unlock(MozContainer* container, + struct wl_surface** surface) { + LOGWAYLAND(("%s [%p] surface %p\n", __FUNCTION__, (void*)container, + (void*)container->wl_container.surface)); + if (*surface) { + container->wl_container.container_lock->Unlock(); + *surface = nullptr; + } +} + +struct wl_egl_window* moz_container_wayland_get_egl_window( + MozContainer* container, int scale) { + GdkDisplay* display = gtk_widget_get_display(GTK_WIDGET(container)); + RefPtr waylandDisplay = WaylandDisplayGet(display); + MozContainerWayland* wl_container = &container->wl_container; + + LOGWAYLAND(("%s [%p] eglwindow %p\n", __FUNCTION__, (void*)container, + (void*)wl_container->eglwindow)); + + MutexAutoLock lock(*wl_container->container_lock); + + // Always call moz_container_get_wl_surface() to ensure underlying + // container->surface has correct scale and position. + wl_surface* surface = + moz_container_wayland_get_surface_locked(container, waylandDisplay); + if (!surface) { + return nullptr; + } + if (!wl_container->eglwindow) { + GdkWindow* window = gtk_widget_get_window(GTK_WIDGET(container)); + wl_container->eglwindow = + wl_egl_window_create(surface, gdk_window_get_width(window) * scale, + gdk_window_get_height(window) * scale); + + LOGWAYLAND(("%s [%p] created eglwindow %p\n", __FUNCTION__, + (void*)container, (void*)wl_container->eglwindow)); + } + + return wl_container->eglwindow; +} + +gboolean moz_container_wayland_has_egl_window(MozContainer* container) { + return container->wl_container.eglwindow ? true : false; +} + +gboolean moz_container_wayland_surface_needs_clear(MozContainer* container) { + int ret = container->wl_container.surface_needs_clear; + container->wl_container.surface_needs_clear = false; + return ret; +} + +void moz_container_wayland_update_opaque_region(MozContainer* container, + bool aSubtractCorners) { + MozContainerWayland* wl_container = &container->wl_container; + wl_container->opaque_region_needs_update = true; + wl_container->opaque_region_subtract_corners = aSubtractCorners; + + // 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) { + return container ? container->wl_container.ready_to_draw : false; +} -- cgit v1.2.3