frames: Update _MUTTER_FRAME_EXTENTS from frame size_allocate

Relying on the content size_allocate() to determine the content position
can fail in situations where the position of the content has changed,
but not its size.

This happens for example when the window initially is sized fullscreen
height + headerbar height while not considered fullscreen yet. Then when
the window is resized to just the fullscreen height and marked as
fullscreen, the content size has not changed and size_allocate() is not
called on the content. Thus the previous position which assumes the
presence of a headerbar still applies. As a result the window is shifted
down, revealing a headerbar sized area showing the gtk window background
color.

This issue can be avoided by using the frame's size_allocate(), which
gets called in response to all relevant events, such as any headerbar
size changes, headerbar visibility changes, window resizes and
fullscreen status changes.

Closes: https://gitlab.gnome.org/GNOME/mutter/-/issues/2937
Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/3608>
This commit is contained in:
Sebastian Keller 2024-02-21 23:38:58 +01:00 committed by Marge Bot
parent 87c43ad15e
commit bcdd282e9c

View File

@ -30,6 +30,8 @@
struct _MetaFrame
{
GtkWindow parent_instance;
GtkBorder extents;
Atom atom__NET_WM_VISIBLE_NAME;
Atom atom__NET_WM_NAME;
Atom atom__MOTIF_WM_HINTS;
@ -187,20 +189,8 @@ update_extents (MetaFrame *frame,
(guchar *) &data, 4);
gdk_x11_display_error_trap_pop_ignored (display);
}
static void
on_border_changed (GObject *object,
GParamSpec *pspec,
gpointer user_data)
{
MetaFrame *frame = user_data;
GtkWidget *content;
GtkBorder border;
content = gtk_window_get_child (GTK_WINDOW (frame));
border = meta_frame_content_get_border (META_FRAME_CONTENT (content));
update_extents (frame, border);
frame->extents = border;
}
static char *
@ -484,11 +474,47 @@ meta_frame_finalize (GObject *object)
G_OBJECT_CLASS (meta_frame_parent_class)->finalize (object);
}
static void
meta_frame_size_allocate (GtkWidget *widget,
int width,
int height,
int baseline)
{
MetaFrame *frame = META_FRAME (widget);
GtkWidget *content;
GtkBorder extents;
graphene_point_t point = {};
double scale;
GTK_WIDGET_CLASS (meta_frame_parent_class)->size_allocate (widget, width, height, baseline);
content = gtk_window_get_child (GTK_WINDOW (frame));
if (!content)
return;
if (!gtk_widget_compute_point (content, widget, &point, &point))
return;
scale = gdk_surface_get_scale_factor (gtk_native_get_surface (GTK_NATIVE (widget)));
/* FIXME: right/bottom are broken, if they are ever other than 0. */
extents = (GtkBorder) { point.x * scale, 0, point.y * scale, 0 };
if (frame->extents.left == extents.left &&
frame->extents.right == extents.right &&
frame->extents.top == extents.top &&
frame->extents.bottom == extents.bottom)
return;
update_extents (frame, extents);
}
static void
meta_frame_class_init (MetaFrameClass *klass)
{
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
GObjectClass *object_class = G_OBJECT_CLASS (klass);
widget_class->size_allocate = meta_frame_size_allocate;
object_class->constructed = meta_frame_constructed;
object_class->finalize = meta_frame_finalize;
}
@ -518,9 +544,6 @@ meta_frame_new (Window window)
content = meta_frame_content_new (window);
gtk_window_set_child (GTK_WINDOW (frame), content);
g_signal_connect (content, "notify::border",
G_CALLBACK (on_border_changed), frame);
gtk_widget_realize (GTK_WIDGET (frame));
surface = gtk_native_get_surface (GTK_NATIVE (frame));
gdk_x11_surface_set_frame_sync_enabled (surface, TRUE);