lib: set cursor from guest provided texture data

This commit is contained in:
Christian Hergert 2023-02-15 12:02:03 -08:00
parent 8dd08014b1
commit 5c52047800
3 changed files with 91 additions and 8 deletions

View File

@ -26,6 +26,7 @@
#include "mks-display.h"
#include "mks-keyboard.h"
#include "mks-mouse.h"
#include "mks-paintable-private.h"
#include "mks-screen.h"
#include "mks-util-private.h"
@ -42,6 +43,7 @@ typedef struct
GdkPaintable *paintable;
gulong invalidate_contents_handler;
gulong invalidate_size_handler;
gulong notify_cursor_handler;
/* Tracking the last known positions of mouse events so that we may
* do something "reasonable" if the pointer is not absolute.
@ -98,24 +100,39 @@ mks_display_get_paintable_area (MksDisplay *self,
static void
mks_display_invalidate_contents_cb (MksDisplay *self,
GdkPaintable *paintable)
MksPaintable *paintable)
{
g_assert (MKS_IS_DISPLAY (self));
g_assert (GDK_IS_PAINTABLE (paintable));
g_assert (MKS_IS_PAINTABLE (paintable));
gtk_widget_queue_draw (GTK_WIDGET (self));
}
static void
mks_display_invalidate_size_cb (MksDisplay *self,
GdkPaintable *paintable)
MksPaintable *paintable)
{
g_assert (MKS_IS_DISPLAY (self));
g_assert (GDK_IS_PAINTABLE (paintable));
g_assert (MKS_IS_PAINTABLE (paintable));
gtk_widget_queue_resize (GTK_WIDGET (self));
}
static void
mks_display_notify_cursor_cb (MksDisplay *self,
GParamSpec *pspec,
MksPaintable *paintable)
{
GdkCursor *cursor;
g_assert (MKS_IS_DISPLAY (self));
g_assert (MKS_IS_PAINTABLE (paintable));
cursor = _mks_paintable_get_cursor (paintable);
gtk_widget_set_cursor (GTK_WIDGET (self), cursor);
}
static void
mks_display_set_paintable (MksDisplay *self,
GdkPaintable *paintable)
@ -132,6 +149,7 @@ mks_display_set_paintable (MksDisplay *self,
{
g_clear_signal_handler (&priv->invalidate_contents_handler, priv->paintable);
g_clear_signal_handler (&priv->invalidate_size_handler, priv->paintable);
g_clear_signal_handler (&priv->notify_cursor_handler, priv->paintable);
g_clear_object (&priv->paintable);
}
@ -150,6 +168,12 @@ mks_display_set_paintable (MksDisplay *self,
G_CALLBACK (mks_display_invalidate_size_cb),
self,
G_CONNECT_SWAPPED);
priv->notify_cursor_handler =
g_signal_connect_object (paintable,
"notify::cursor",
G_CALLBACK (mks_display_notify_cursor_cb),
self,
G_CONNECT_SWAPPED);
}
g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_PAINTABLE]);

View File

@ -29,8 +29,9 @@ G_BEGIN_DECLS
G_DECLARE_FINAL_TYPE (MksPaintable, mks_paintable, MKS, PAINTABLE, GObject)
GdkPaintable *_mks_paintable_new (GCancellable *cancellable,
int *peer_fd,
GError **error);
GdkPaintable *_mks_paintable_new (GCancellable *cancellable,
int *peer_fd,
GError **error);
GdkCursor *_mks_paintable_get_cursor (MksPaintable *self);
G_END_DECLS

View File

@ -39,10 +39,12 @@ struct _MksPaintable
MksQemuListener *listener;
GDBusConnection *connection;
GdkPaintable *child;
GdkCursor *cursor;
};
enum {
PROP_0,
PROP_CURSOR,
PROP_PAINTABLE,
N_PROPS
};
@ -149,6 +151,7 @@ mks_paintable_dispose (GObject *object)
g_clear_object (&self->listener);
g_clear_object (&self->child);
g_clear_object (&self->gl_context);
g_clear_object (&self->cursor);
G_OBJECT_CLASS (mks_paintable_parent_class)->dispose (object);
}
@ -163,6 +166,10 @@ mks_paintable_get_property (GObject *object,
switch (prop_id)
{
case PROP_CURSOR:
g_value_set_object (value, _mks_paintable_get_cursor (self));
break;
case PROP_PAINTABLE:
g_value_set_object (value, self->child);
break;
@ -180,6 +187,11 @@ mks_paintable_class_init (MksPaintableClass *klass)
object_class->dispose = mks_paintable_dispose;
object_class->get_property = mks_paintable_get_property;
properties [PROP_CURSOR] =
g_param_spec_object ("cursor", NULL, NULL,
GDK_TYPE_CURSOR,
(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
properties [PROP_PAINTABLE] =
g_param_spec_object ("paintable", NULL, NULL,
GDK_TYPE_PAINTABLE,
@ -497,13 +509,43 @@ mks_paintable_listener_cursor_define (MksPaintable *self,
int height,
int hot_x,
int hot_y,
GVariant *bytes,
GVariant *bytestring,
MksQemuListener *listener)
{
g_autoptr(GBytes) bytes = NULL;
g_autoptr(GdkTexture) texture = NULL;
g_autoptr(GdkCursor) cursor = NULL;
gsize data_len;
g_assert (MKS_IS_PAINTABLE (self));
g_assert (G_IS_DBUS_METHOD_INVOCATION (invocation));
g_assert (MKS_QEMU_IS_LISTENER (listener));
if (width < 1 || width > 512 ||
height < 1 || height > 512 ||
!(bytes = g_variant_get_data_as_bytes (bytestring)))
goto failure;
data_len = g_bytes_get_size (bytes);
if (data_len != (4 * width * height))
goto failure;
texture = gdk_memory_texture_new (width,
height,
#if G_BYTE_ORDER == G_LITTLE_ENDIAN
GDK_MEMORY_B8G8R8A8_PREMULTIPLIED,
#else
GDK_MEMORY_A8R8G8B8_PREMULTIPLIED,
#endif
bytes,
width * 4);
cursor = gdk_cursor_new_from_texture (texture, hot_x, hot_y, NULL);
if (g_set_object (&self->cursor, cursor))
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_CURSOR]);
failure:
mks_qemu_listener_complete_cursor_define (listener, invocation);
return TRUE;
@ -693,3 +735,19 @@ _mks_paintable_new (GCancellable *cancellable,
return GDK_PAINTABLE (g_steal_pointer (&self));
}
/**
* _mks_paintable_get_cursor:
* @self: a #MksPaintable
*
* Gets the cursor as defined by the Qemu instance.
*
* Returns: (transfer none) (nullable): a #GdkCursor or %NULL
*/
GdkCursor *
_mks_paintable_get_cursor (MksPaintable *self)
{
g_return_val_if_fail (MKS_IS_PAINTABLE (self), NULL);
return self->cursor;
}