display: Reconfigure screen when size change

Otherwise the VM would have no idea about the size of the target widget.
To do so, we introduce a ScreenResizer object that remembers the previous
screen attributes as well the next attributes to configure.
Taking care of scheduling resize operations and avoids submitting
the same attributes twice.
This commit is contained in:
Bilal Elmoussaoui 2023-05-08 16:37:49 +02:00
parent 188c63a183
commit a804f766f9
6 changed files with 318 additions and 0 deletions

View File

@ -34,6 +34,7 @@ libmks_private_sources = [
'mks-gl-context.c',
'mks-inhibitor.c',
'mks-read-only-list-model.c',
'mks-screen-resizer.c',
'mks-util.c',
gnome.gdbus_codegen('mks-qemu',

View File

@ -30,6 +30,8 @@
#include "mks-mouse.h"
#include "mks-paintable-private.h"
#include "mks-screen.h"
#include "mks-screen-attributes.h"
#include "mks-screen-resizer-private.h"
#include "mks-util-private.h"
#define DEFAULT_UNGRAB_TRIGGER "<Control><Alt>g"
@ -37,6 +39,7 @@
typedef struct
{
MksScreen *screen;
MksScreenResizer *resizer;
MksDisplayPicture *picture;
MksInhibitor *inhibitor;
GtkShortcutTrigger *ungrab_trigger;
@ -135,6 +138,7 @@ mks_display_connect (MksDisplay *self,
{
mks_display_picture_set_keyboard (priv->picture, mks_screen_get_keyboard (screen));
mks_display_picture_set_mouse (priv->picture, mks_screen_get_mouse (screen));
mks_screen_resizer_set_screen (priv->resizer, screen);
mks_screen_attach (screen,
NULL,
@ -157,6 +161,7 @@ mks_display_disconnect (MksDisplay *self)
g_assert (MKS_IS_DISPLAY (self));
g_clear_object (&priv->screen);
mks_screen_resizer_set_screen (priv->resizer, NULL);
g_clear_object (&priv->inhibitor);
if (priv->picture != NULL)
@ -228,6 +233,7 @@ mks_display_dispose (GObject *object)
mks_display_disconnect (self);
g_clear_pointer ((GtkWidget **)&priv->picture, gtk_widget_unparent);
g_clear_object (&priv->resizer);
G_OBJECT_CLASS (mks_display_parent_class)->dispose (object);
}
@ -288,6 +294,7 @@ mks_display_size_allocate (GtkWidget *widget,
MksDisplay *self = (MksDisplay *)widget;
MksDisplayPrivate *priv = mks_display_get_instance_private (self);
graphene_rect_t area;
MksScreenAttributes *attributes;
g_assert (MKS_IS_DISPLAY (self));
@ -295,6 +302,13 @@ mks_display_size_allocate (GtkWidget *widget,
mks_display_get_paintable_area (self, &area);
attributes = mks_screen_attributes_new ();
mks_screen_attributes_set_width (attributes, width);
mks_screen_attributes_set_height (attributes, height);
mks_screen_resizer_queue_resize (priv->resizer,
g_steal_pointer (&attributes));
gtk_widget_size_allocate (GTK_WIDGET (priv->picture),
&(GtkAllocation) {
area.origin.x,
@ -303,6 +317,7 @@ mks_display_size_allocate (GtkWidget *widget,
area.size.height
},
-1);
mks_screen_attributes_free (attributes);
}
static void
@ -391,6 +406,7 @@ mks_display_init (MksDisplay *self)
GtkEventController *controller;
priv->picture = g_object_new (MKS_TYPE_DISPLAY_PICTURE, NULL);
priv->resizer = mks_screen_resizer_new ();
gtk_widget_set_parent (GTK_WIDGET (priv->picture), GTK_WIDGET (self));
controller = gtk_event_controller_legacy_new ();

View File

@ -72,6 +72,28 @@ mks_screen_attributes_free (MksScreenAttributes *self)
g_free (self);
}
/**
* mks_screen_attributes_equal:
* @self: a #MksScreenAttributes
* @other: a #MksScreenAttributes
*
* Returns `true` if the two attributes are equal, `false` otherwise.
*/
gboolean
mks_screen_attributes_equal (MksScreenAttributes *self,
MksScreenAttributes *other)
{
if (self == NULL || other == NULL)
return FALSE;
return (self->width == other->width &&
self->height == other->height &&
self->x_offset == other->x_offset &&
self->y_offset == other->y_offset &&
self->width_mm == other->width_mm &&
self->height_mm == other->height_mm);
}
void
mks_screen_attributes_set_width_mm (MksScreenAttributes *self,
guint16 width_mm)

View File

@ -43,6 +43,9 @@ MksScreenAttributes *mks_screen_attributes_copy (MksScreenAttributes *s
MKS_AVAILABLE_IN_ALL
void mks_screen_attributes_free (MksScreenAttributes *self);
MKS_AVAILABLE_IN_ALL
gboolean mks_screen_attributes_equal (MksScreenAttributes *self,
MksScreenAttributes *other);
MKS_AVAILABLE_IN_ALL
void mks_screen_attributes_set_width_mm (MksScreenAttributes *self,
guint16 width_mm);
MKS_AVAILABLE_IN_ALL

View File

@ -0,0 +1,38 @@
/*
* mks-screen-resizer-private.h
*
* Copyright 2023 Bilal Elmoussaoui <belmouss@redhat.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#pragma once
#include "mks-screen.h"
G_BEGIN_DECLS
#define MKS_TYPE_SCREEN_RESIZER (mks_screen_resizer_get_type())
G_DECLARE_FINAL_TYPE (MksScreenResizer, mks_screen_resizer, MKS, SCREEN_RESIZER, GObject)
MksScreenResizer *mks_screen_resizer_new (void);
void mks_screen_resizer_set_screen (MksScreenResizer *self,
MksScreen *screen);
void mks_screen_resizer_queue_resize (MksScreenResizer *self,
MksScreenAttributes *attributes);
G_END_DECLS

238
lib/mks-screen-resizer.c Normal file
View File

@ -0,0 +1,238 @@
/*
* mks-screen-resizer.c
*
* Copyright 2023 Bilal Elmoussaoui <belmouss@redhat.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#include "mks-screen-attributes.h"
#include "mks-screen-resizer-private.h"
#include "mks-util-private.h"
static void
mks_screen_resizer_reconfigure (MksScreenResizer *self,
MksScreenAttributes *attributes);
struct _MksScreenResizer
{
GObject parent_instance;
MksScreen *screen;
/* Remember our last operation */
MksScreenAttributes *next_op;
MksScreenAttributes *previous_op;
gboolean in_progress;
};
enum {
PROP_0,
PROP_SCREEN,
N_PROPS
};
G_DEFINE_FINAL_TYPE (MksScreenResizer, mks_screen_resizer, G_TYPE_OBJECT)
static GParamSpec *properties [N_PROPS];
static void
mks_screen_resizer_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
MksScreenResizer *self = MKS_SCREEN_RESIZER (object);
switch (prop_id)
{
case PROP_SCREEN:
g_value_set_object (value, self->screen);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
mks_screen_resizer_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
MksScreenResizer *self = MKS_SCREEN_RESIZER (object);
switch (prop_id)
{
case PROP_SCREEN:
mks_screen_resizer_set_screen (self, g_value_get_object (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
mks_screen_resizer_dispose (GObject *object)
{
MksScreenResizer *self = (MksScreenResizer *)object;
g_clear_object (&self->screen);
g_clear_pointer (&self->next_op, mks_screen_attributes_free);
g_clear_pointer (&self->previous_op, mks_screen_attributes_free);
G_OBJECT_CLASS (mks_screen_resizer_parent_class)->dispose (object);
}
static void
mks_screen_resizer_class_init (MksScreenResizerClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->dispose = mks_screen_resizer_dispose;
object_class->get_property = mks_screen_resizer_get_property;
object_class->set_property = mks_screen_resizer_set_property;
properties[PROP_SCREEN] =
g_param_spec_object ("screen", NULL, NULL,
MKS_TYPE_SCREEN,
(G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
g_object_class_install_properties (object_class, N_PROPS, properties);
}
static void
on_screen_configure_cb (GObject *object,
GAsyncResult *result,
gpointer user_data)
{
MksScreen *screen = (MksScreen *)object;
g_autoptr(MksScreenResizer) self = user_data;
g_autoptr(GError) error = NULL;
g_autoptr(MksScreenAttributes) attributes = NULL;
MKS_ENTRY;
g_assert (MKS_IS_SCREEN (screen));
g_assert (G_IS_ASYNC_RESULT (result));
g_assert (MKS_IS_SCREEN_RESIZER (self));
if (!mks_screen_configure_finish (screen, result, &error))
g_warning ("Screen configure failed: %s", error->message);
self->in_progress = FALSE;
attributes = g_steal_pointer (&self->next_op);
if (attributes != NULL &&
!mks_screen_attributes_equal (attributes, self->previous_op))
{
mks_screen_resizer_reconfigure (self,
g_steal_pointer (&attributes));
}
MKS_EXIT;
}
/**
* mks_screen_resizer_new:
*
* Returns: (transfer full): a new #MksScreenResizer
*/
MksScreenResizer *
mks_screen_resizer_new (void)
{
MksScreenResizer *self;
self = g_object_new (MKS_TYPE_SCREEN_RESIZER, NULL);
self->next_op = NULL;
self->previous_op = NULL;
self->in_progress = FALSE;
return self;
}
static void
mks_screen_resizer_init (MksScreenResizer *self)
{
}
/**
* mks_screen_resizer_set_screen:
* @self: A `MksScreenResizer`
* @screen: A `MksScreen`
*
* Sets the screen to resize when a resize is queued.
*/
void
mks_screen_resizer_set_screen (MksScreenResizer *self,
MksScreen *screen)
{
g_return_if_fail (MKS_IS_SCREEN_RESIZER (self));
g_return_if_fail (!screen || MKS_IS_SCREEN (screen));
if (g_set_object (&self->screen, screen))
{
g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_SCREEN]);
}
}
/**
* mks_screen_resizer_queue_resize:
* @self: A `MksScreenResizer`
* @attributes: (transfer full): The new attributes to queue
*
* Schedule the VM display configuration with the passed attributes if
* there is no ongoing operation. Otherwise, add the attributes to
* a queue of updates.
*/
void
mks_screen_resizer_queue_resize (MksScreenResizer *self,
MksScreenAttributes *attributes)
{
g_return_if_fail (MKS_IS_SCREEN_RESIZER (self));
if (mks_screen_attributes_equal (attributes, self->previous_op))
return;
if (self->in_progress)
{
g_clear_pointer (&self->next_op, mks_screen_attributes_free);
self->next_op = g_steal_pointer (&attributes);
return;
}
mks_screen_resizer_reconfigure (self, attributes);
}
/**
* mks_screen_resizer_reconfigure:
* @self: A `MksScreenResizer`
* @attributes: (transfer full): The attributes to reconfigure
*
* Configure the screen with the passed attributes.
*/
static void
mks_screen_resizer_reconfigure (MksScreenResizer *self,
MksScreenAttributes *attributes)
{
g_assert (MKS_IS_SCREEN_RESIZER (self));
self->in_progress = TRUE;
mks_screen_configure (self->screen,
g_steal_pointer (&attributes),
NULL,
on_screen_configure_cb,
g_object_ref (self));
g_clear_pointer (&self->previous_op, mks_screen_attributes_free);
self->previous_op = g_steal_pointer (&attributes);
}