From d1aa0a74e473bf06be090a84e394ee0cb799d0a0 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Wed, 20 Mar 2024 20:31:59 -0400 Subject: [PATCH] gdk: Add callback cursors Add a variant of GdkCursor that obtains the texture for the cursor via a callback. The callback gives us the flexibility to handle fractional scales and update the cursor for cursor theme size changes as well as scale changes. --- gdk/gdkcursor.c | 70 ++++++++++++++++++++++++++++++++++++++++++ gdk/gdkcursor.h | 46 +++++++++++++++++++++++++++ gdk/gdkcursorprivate.h | 12 ++++++++ 3 files changed, 128 insertions(+) diff --git a/gdk/gdkcursor.c b/gdk/gdkcursor.c index e12a465b4e..1c9ab5ed4e 100644 --- a/gdk/gdkcursor.c +++ b/gdk/gdkcursor.c @@ -156,6 +156,9 @@ gdk_cursor_finalize (GObject *object) g_clear_object (&cursor->texture); g_clear_object (&cursor->fallback); + if (cursor->destroy) + cursor->destroy (cursor->data); + G_OBJECT_CLASS (gdk_cursor_parent_class)->finalize (object); } @@ -253,6 +256,11 @@ gdk_cursor_hash (gconstpointer pointer) hash ^= g_str_hash (cursor->name); else if (cursor->texture) hash ^= g_direct_hash (cursor->texture); + else if (cursor->callback) + { + hash ^= g_direct_hash (cursor->callback); + hash ^= g_direct_hash (cursor->data); + } hash ^= (cursor->hotspot_x << 8) | cursor->hotspot_y; @@ -281,6 +289,10 @@ gdk_cursor_equal (gconstpointer a, ca->hotspot_y != cb->hotspot_y) return FALSE; + if (ca->callback != cb->callback || + ca->data != cb->data) + return FALSE; + return TRUE; } @@ -355,6 +367,45 @@ gdk_cursor_new_from_texture (GdkTexture *texture, NULL); } +/** + * gdk_cursor_new_from_callback: + * @callback: the `GdkCursorGetTextureCallback` + * @data: data to pass to @callback + * @destroy: destroy notify for @data + * @fallback: (nullable): the `GdkCursor` to fall back to when + * this one cannot be supported + * + * Creates a new callback-based cursor object. + * + * Cursors of this kind produce textures for the cursor + * image on demand, when the @callback is called. + * + * Returns: (nullable): a new `GdkCursor` + * + * Since: 4.16 + */ +GdkCursor * +gdk_cursor_new_from_callback (GdkCursorGetTextureCallback callback, + gpointer data, + GDestroyNotify destroy, + GdkCursor *fallback) +{ + GdkCursor *cursor; + + g_return_val_if_fail (callback != NULL, NULL); + g_return_val_if_fail (fallback == NULL || GDK_IS_CURSOR (fallback), NULL); + + cursor = g_object_new (GDK_TYPE_CURSOR, + "fallback", fallback, + NULL); + + cursor->callback = callback; + cursor->data = data; + cursor->destroy = destroy; + + return cursor; +} + /** * gdk_cursor_get_fallback: (attributes org.gtk.Method.get_property=fallback) * @cursor: a `GdkCursor` @@ -459,3 +510,22 @@ gdk_cursor_get_hotspot_y (GdkCursor *cursor) return cursor->hotspot_y; } + +GdkTexture * +gdk_cursor_get_texture_for_size (GdkCursor *cursor, + int cursor_size, + double scale, + int *width, + int *height, + int *hotspot_x, + int *hotspot_y) +{ + if (cursor->callback == NULL) + return NULL; + + return cursor->callback (cursor, + cursor_size, scale, + width, height, + hotspot_x, hotspot_y, + cursor->data); +} diff --git a/gdk/gdkcursor.h b/gdk/gdkcursor.h index 64240db7e2..a28840e7e9 100644 --- a/gdk/gdkcursor.h +++ b/gdk/gdkcursor.h @@ -51,6 +51,52 @@ GDK_AVAILABLE_IN_ALL GdkCursor* gdk_cursor_new_from_name (const char *name, GdkCursor *fallback); +/** + * GdkCursorGetTestureCallback: + * @cursor: the `GdkCursor` + * @cursor_size: the nominal cursor size, in application pixels + * @scale: the device scale + * @width: (out): return location for the actual cursor width, + * in application pixels + * @height: (out): return location for the actual cursor height, + * in application pixels + * @hotspot_x: (out): return location for the hotspot X position, + * in application pixels + * @hotspot_y: (out): return location for the hotspot Y position, + * in application pixels + * @data: User data for the callback + * + * The type of callback used by a dynamic `GdkCursor` to generate + * a texture for the cursor image at the given @cursor_size + * and @scale. + * + * The actual cursor size in application pixels may be different + * from @cursor_size x @cursor_size, and will be returned in + * @width, @height. The returned texture should have a size that + * corresponds to the actual cursor size, in device pixels (i.e. + * application pixels, multiplied by @scale). + * + * This function may fail and return `NULL`, in which case + * the fallback cursor will be used. + * + * Returns: (nullable) (transfer full): the cursor image, or + * `NULL` if none could be produced. + */ +typedef GdkTexture * (* GdkCursorGetTextureCallback) (GdkCursor *cursor, + int cursor_size, + double scale, + int *width, + int *height, + int *hotspot_x, + int *hotspot_y, + gpointer data); + +GDK_AVAILABLE_IN_4_16 +GdkCursor * gdk_cursor_new_from_callback (GdkCursorGetTextureCallback callback, + gpointer data, + GDestroyNotify destroy, + GdkCursor *fallback); + GDK_AVAILABLE_IN_ALL GdkCursor * gdk_cursor_get_fallback (GdkCursor *cursor); GDK_AVAILABLE_IN_ALL diff --git a/gdk/gdkcursorprivate.h b/gdk/gdkcursorprivate.h index 7eeb6278ca..9d85c05815 100644 --- a/gdk/gdkcursorprivate.h +++ b/gdk/gdkcursorprivate.h @@ -44,6 +44,10 @@ struct _GdkCursor GdkTexture *texture; int hotspot_x; int hotspot_y; + + GdkCursorGetTextureCallback callback; + gpointer data; + GDestroyNotify destroy; }; struct _GdkCursorClass @@ -55,5 +59,13 @@ guint gdk_cursor_hash (gconstpointer gboolean gdk_cursor_equal (gconstpointer a, gconstpointer b); +GdkTexture * gdk_cursor_get_texture_for_size (GdkCursor *cursor, + int cursor_size, + double scale, + int *width, + int *height, + int *hotspot_x, + int *hotspot_y); + G_END_DECLS