Compare commits
11 Commits
rendering-
...
cursor-fro
Author | SHA1 | Date | |
---|---|---|---|
|
2346bb4087 | ||
|
0f9ed94a85 | ||
|
11ddd5ed40 | ||
|
160b8a0f7e | ||
|
ce8cf6866b | ||
|
e5fb0dd3d2 | ||
|
c2cae1d0fb | ||
|
d1c7d5eac7 | ||
|
24e3ad54a7 | ||
|
7c357e7da5 | ||
|
667e2fc73a |
329
gdk/gdkcursor.c
329
gdk/gdkcursor.c
@@ -30,11 +30,14 @@
|
||||
#include "gdkcursor.h"
|
||||
#include "gdkcursorprivate.h"
|
||||
#include "gdktexture.h"
|
||||
#include "gdkdisplay.h"
|
||||
#include <glib/gi18n-lib.h>
|
||||
|
||||
#include <math.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <graphene.h>
|
||||
|
||||
/**
|
||||
* GdkCursor:
|
||||
*
|
||||
@@ -81,8 +84,11 @@ enum {
|
||||
PROP_HOTSPOT_Y,
|
||||
PROP_NAME,
|
||||
PROP_TEXTURE,
|
||||
PROP_PAINTABLE,
|
||||
};
|
||||
|
||||
static guint invalidate_signal;
|
||||
|
||||
G_DEFINE_TYPE (GdkCursor, gdk_cursor, G_TYPE_OBJECT)
|
||||
|
||||
static void
|
||||
@@ -110,6 +116,9 @@ gdk_cursor_get_property (GObject *object,
|
||||
case PROP_TEXTURE:
|
||||
g_value_set_object (value, cursor->texture);
|
||||
break;
|
||||
case PROP_PAINTABLE:
|
||||
g_value_set_object (value, cursor->paintable);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
@@ -141,6 +150,9 @@ gdk_cursor_set_property (GObject *object,
|
||||
case PROP_TEXTURE:
|
||||
cursor->texture = g_value_dup_object (value);
|
||||
break;
|
||||
case PROP_PAINTABLE:
|
||||
cursor->paintable = g_value_dup_object (value);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
@@ -155,6 +167,10 @@ gdk_cursor_finalize (GObject *object)
|
||||
g_free (cursor->name);
|
||||
g_clear_object (&cursor->texture);
|
||||
g_clear_object (&cursor->fallback);
|
||||
g_clear_object (&cursor->paintable);
|
||||
|
||||
if (cursor->destroy)
|
||||
cursor->destroy (cursor->data);
|
||||
|
||||
G_OBJECT_CLASS (gdk_cursor_parent_class)->finalize (object);
|
||||
}
|
||||
@@ -231,6 +247,37 @@ gdk_cursor_class_init (GdkCursorClass *cursor_class)
|
||||
GDK_TYPE_TEXTURE,
|
||||
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
|
||||
G_PARAM_STATIC_STRINGS));
|
||||
|
||||
/**
|
||||
* GdkCursor:paintable:
|
||||
*
|
||||
* The paintable displayed by this cursor.
|
||||
*
|
||||
* The intrinsic size of the paintable determines
|
||||
* the size of the cursor.
|
||||
*
|
||||
* If the GDK backend has the capability, non-static paintables
|
||||
* may create animated cursors.
|
||||
*
|
||||
* The paintable will be %NULL if the cursor was created
|
||||
* from a name or texture.
|
||||
*
|
||||
* Since: 4.16
|
||||
*/
|
||||
g_object_class_install_property (object_class,
|
||||
PROP_PAINTABLE,
|
||||
g_param_spec_object ("paintable", NULL, NULL,
|
||||
GDK_TYPE_PAINTABLE,
|
||||
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
|
||||
G_PARAM_STATIC_STRINGS));
|
||||
|
||||
invalidate_signal = g_signal_new ("invalidate",
|
||||
GDK_TYPE_CURSOR,
|
||||
G_SIGNAL_RUN_LAST,
|
||||
0,
|
||||
NULL, NULL,
|
||||
NULL,
|
||||
G_TYPE_NONE, 0);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -253,6 +300,8 @@ gdk_cursor_hash (gconstpointer pointer)
|
||||
hash ^= g_str_hash (cursor->name);
|
||||
else if (cursor->texture)
|
||||
hash ^= g_direct_hash (cursor->texture);
|
||||
else if (cursor->paintable)
|
||||
hash ^= g_direct_hash (cursor->paintable);
|
||||
|
||||
hash ^= (cursor->hotspot_x << 8) | cursor->hotspot_y;
|
||||
|
||||
@@ -277,6 +326,9 @@ gdk_cursor_equal (gconstpointer a,
|
||||
if (ca->texture != cb->texture)
|
||||
return FALSE;
|
||||
|
||||
if (ca->paintable != cb->paintable)
|
||||
return FALSE;
|
||||
|
||||
if (ca->hotspot_x != cb->hotspot_x ||
|
||||
ca->hotspot_y != cb->hotspot_y)
|
||||
return FALSE;
|
||||
@@ -355,6 +407,45 @@ gdk_cursor_new_from_texture (GdkTexture *texture,
|
||||
NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* gdk_cursor_new_from_paintable:
|
||||
* @paintable: the paintable providing the pixel data
|
||||
* @hotspot_x: the horizontal offset of the “hotspot” of the cursor
|
||||
* @hotspot_y: the vertical offset of the “hotspot” of the cursor
|
||||
* @fallback: (nullable): the `GdkCursor` to fall back to when
|
||||
* this one cannot be supported
|
||||
*
|
||||
* Creates a new cursor from a `GdkPaintable`.
|
||||
*
|
||||
* The intrinsic size of the paintable determines
|
||||
* the size of the cursor.
|
||||
*
|
||||
* If the GDK backend has the capability, non-static paintables
|
||||
* may create animated cursors.
|
||||
*
|
||||
* Returns: a new `GdkCursor`
|
||||
*
|
||||
* Since: 4.16
|
||||
*/
|
||||
GdkCursor *
|
||||
gdk_cursor_new_from_paintable (GdkPaintable *paintable,
|
||||
int hotspot_x,
|
||||
int hotspot_y,
|
||||
GdkCursor *fallback)
|
||||
{
|
||||
g_return_val_if_fail (GDK_IS_PAINTABLE (paintable), NULL);
|
||||
g_return_val_if_fail (0 <= hotspot_x && hotspot_x < gdk_paintable_get_intrinsic_width (paintable), NULL);
|
||||
g_return_val_if_fail (0 <= hotspot_y && hotspot_y < gdk_paintable_get_intrinsic_height (paintable), NULL);
|
||||
g_return_val_if_fail (fallback == NULL || GDK_IS_CURSOR (fallback), NULL);
|
||||
|
||||
return g_object_new (GDK_TYPE_CURSOR,
|
||||
"paintable", paintable,
|
||||
"hotspot-x", hotspot_x,
|
||||
"hotspot-y", hotspot_y,
|
||||
"fallback", fallback,
|
||||
NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* gdk_cursor_get_fallback: (attributes org.gtk.Method.get_property=fallback)
|
||||
* @cursor: a `GdkCursor`
|
||||
@@ -416,6 +507,27 @@ gdk_cursor_get_texture (GdkCursor *cursor)
|
||||
return cursor->texture;
|
||||
}
|
||||
|
||||
/**
|
||||
* gdk_cursor_get_paintable:
|
||||
* @cursor: a `GdkCursor`
|
||||
*
|
||||
* Returns the paintable for the cursor.
|
||||
*
|
||||
* If the cursor is a named or texture cursor, %NULL will be returned.
|
||||
*
|
||||
* Returns: (transfer none) (nullable): the paintable for @cursor
|
||||
* or %NULL if it is a named or texture cursor
|
||||
*
|
||||
* Since: 4.16
|
||||
*/
|
||||
GdkPaintable *
|
||||
gdk_cursor_get_paintable (GdkCursor *cursor)
|
||||
{
|
||||
g_return_val_if_fail (GDK_IS_CURSOR (cursor), NULL);
|
||||
|
||||
return cursor->paintable;
|
||||
}
|
||||
|
||||
/**
|
||||
* gdk_cursor_get_hotspot_x: (attributes org.gtk.Method.get_property=hotspot-x)
|
||||
* @cursor: a `GdkCursor`
|
||||
@@ -459,3 +571,220 @@ gdk_cursor_get_hotspot_y (GdkCursor *cursor)
|
||||
|
||||
return cursor->hotspot_y;
|
||||
}
|
||||
|
||||
/* Hack. We don't include gsk/gsk.h here to avoid a build order problem
|
||||
* with the generated header gskenumtypes.h, so we need to hack around
|
||||
* a bit to access the gsk and gtk api we need.
|
||||
*/
|
||||
|
||||
typedef struct _GskRenderer GskRenderer;
|
||||
typedef struct _GskRenderNode GskRenderNode;
|
||||
typedef struct _GdkSnapshot GtkSnapshot;
|
||||
|
||||
extern GskRenderer * gsk_cairo_renderer_new (void);
|
||||
extern gboolean gsk_renderer_realize_for_display (GskRenderer *renderer,
|
||||
GdkDisplay *display,
|
||||
GError **error);
|
||||
extern void gsk_renderer_unrealize (GskRenderer *renderer);
|
||||
extern GdkTexture * gsk_renderer_render_texture (GskRenderer *renderer,
|
||||
GskRenderNode *root,
|
||||
const graphene_rect_t *viewport);
|
||||
|
||||
extern void gsk_render_node_get_bounds (GskRenderNode *node,
|
||||
graphene_rect_t *bounds);
|
||||
|
||||
extern void gsk_render_node_unref (GskRenderNode *node);
|
||||
|
||||
extern GtkSnapshot * gtk_snapshot_new (void);
|
||||
extern GskRenderNode * gtk_snapshot_free_to_node (GtkSnapshot *snapshot);
|
||||
|
||||
static GdkTexture *
|
||||
paintable_to_texture (GdkPaintable *paintable,
|
||||
double scale)
|
||||
{
|
||||
GtkSnapshot *snapshot;
|
||||
GskRenderNode *node;
|
||||
GskRenderer *renderer;
|
||||
int width, height;
|
||||
GdkTexture *texture;
|
||||
graphene_rect_t bounds;
|
||||
|
||||
width = gdk_paintable_get_intrinsic_width (paintable);
|
||||
height = gdk_paintable_get_intrinsic_height (paintable);
|
||||
|
||||
snapshot = gtk_snapshot_new ();
|
||||
gdk_paintable_snapshot (paintable, snapshot, width * scale, height * scale);
|
||||
node = gtk_snapshot_free_to_node (snapshot);
|
||||
|
||||
/* FIXME: use a proper renderer, and cache it */
|
||||
renderer = gsk_cairo_renderer_new ();
|
||||
gsk_renderer_realize_for_display (renderer, gdk_display_get_default (), NULL);
|
||||
|
||||
gsk_render_node_get_bounds (node, &bounds);
|
||||
texture = gsk_renderer_render_texture (renderer, node, &bounds);
|
||||
|
||||
gsk_renderer_unrealize (renderer);
|
||||
g_object_unref (renderer);
|
||||
|
||||
gsk_render_node_unref (node);
|
||||
|
||||
return texture;
|
||||
}
|
||||
|
||||
GdkTexture *
|
||||
gdk_cursor_create_texture (GdkCursor *cursor,
|
||||
double scale)
|
||||
{
|
||||
if (cursor->texture)
|
||||
return g_object_ref (cursor->texture);
|
||||
else if (cursor->paintable)
|
||||
return paintable_to_texture (cursor->paintable, scale);
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* This function may fail and return `NULL`, in which case
|
||||
* the backend should use the fallback cursor.
|
||||
*
|
||||
* Returns: (nullable) (transfer full): the cursor image, or
|
||||
* `NULL` if none could be produced.
|
||||
*/
|
||||
|
||||
/**
|
||||
* gdk_cursor_new_dynamic:
|
||||
* @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 dynamic cursor object.
|
||||
*
|
||||
* Dynamic cursors 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_dynamic (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_invalidate:
|
||||
* @cursor: a `GdkCursor`
|
||||
*
|
||||
* Emits the invalidate signal to indicate that the cursor
|
||||
* has changed and backends should recreate their cursor
|
||||
* image.
|
||||
*
|
||||
* This function does nothing unless the cursor has been
|
||||
* created with [constructor@Gdk.Cursor.new_dynamic].
|
||||
*
|
||||
* Since: 4.16
|
||||
*/
|
||||
void
|
||||
gdk_cursor_invalidate (GdkCursor *cursor)
|
||||
{
|
||||
if (cursor->callback == NULL)
|
||||
return;
|
||||
|
||||
g_signal_emit (cursor, invalidate_signal, 0);
|
||||
}
|
||||
|
||||
/*< private >
|
||||
* gdk_cursor_get_dynamic_texture:
|
||||
* @cursor: a dynamic `GdkCursor`
|
||||
* @cursor_size: the nominal size of the cursor image
|
||||
* to produce, in application pixels
|
||||
* @scale: the device scale to use
|
||||
* @width: return location for the cursor width,
|
||||
* in application pixels
|
||||
* @height : return location for the cursor height,
|
||||
* in application pixels
|
||||
* @hotspot_x: return location for the hotspot X position,
|
||||
* in application pixels
|
||||
* @hotspot_y: return location for the hotspot X position,
|
||||
* in application pixels
|
||||
*
|
||||
* Call the callback of a dynamic cursor 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.
|
||||
*
|
||||
* This function may fail and return `NULL`, in which case
|
||||
* the backend should use the fallback cursor.
|
||||
*
|
||||
* Returns: (nullable) (transfer full): the cursor image, or
|
||||
* `NULL` if none could be produced.
|
||||
*/
|
||||
GdkTexture *
|
||||
gdk_cursor_get_dynamic_texture (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);
|
||||
}
|
||||
|
||||
|
@@ -29,6 +29,7 @@
|
||||
#endif
|
||||
|
||||
#include <gdk/gdktypes.h>
|
||||
#include <gdk/gdkpaintable.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
@@ -47,6 +48,13 @@ GdkCursor* gdk_cursor_new_from_texture (GdkTexture *texture,
|
||||
int hotspot_x,
|
||||
int hotspot_y,
|
||||
GdkCursor *fallback);
|
||||
|
||||
GDK_AVAILABLE_IN_4_14
|
||||
GdkCursor * gdk_cursor_new_from_paintable (GdkPaintable *paintable,
|
||||
int hotspot_x,
|
||||
int hotspot_y,
|
||||
GdkCursor *fallback);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GdkCursor* gdk_cursor_new_from_name (const char *name,
|
||||
GdkCursor *fallback);
|
||||
@@ -57,11 +65,32 @@ GDK_AVAILABLE_IN_ALL
|
||||
const char *gdk_cursor_get_name (GdkCursor *cursor);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GdkTexture *gdk_cursor_get_texture (GdkCursor *cursor);
|
||||
GDK_AVAILABLE_IN_4_14
|
||||
GdkPaintable *
|
||||
gdk_cursor_get_paintable (GdkCursor *cursor);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
int gdk_cursor_get_hotspot_x (GdkCursor *cursor);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
int gdk_cursor_get_hotspot_y (GdkCursor *cursor);
|
||||
|
||||
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_14
|
||||
GdkCursor * gdk_cursor_new_dynamic (GdkCursorGetTextureCallback callback,
|
||||
gpointer data,
|
||||
GDestroyNotify destroy,
|
||||
GdkCursor *fallback);
|
||||
|
||||
GDK_AVAILABLE_IN_4_14
|
||||
void gdk_cursor_invalidate (GdkCursor *cursor);
|
||||
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GdkCursor, g_object_unref)
|
||||
|
||||
G_END_DECLS
|
||||
|
@@ -42,8 +42,13 @@ struct _GdkCursor
|
||||
GdkCursor *fallback;
|
||||
char *name;
|
||||
GdkTexture *texture;
|
||||
GdkPaintable *paintable;
|
||||
int hotspot_x;
|
||||
int hotspot_y;
|
||||
|
||||
GdkCursorGetTextureCallback callback;
|
||||
gpointer data;
|
||||
GDestroyNotify destroy;
|
||||
};
|
||||
|
||||
struct _GdkCursorClass
|
||||
@@ -55,5 +60,16 @@ guint gdk_cursor_hash (gconstpointer
|
||||
gboolean gdk_cursor_equal (gconstpointer a,
|
||||
gconstpointer b);
|
||||
|
||||
GdkTexture * gdk_cursor_create_texture (GdkCursor *cursor,
|
||||
double scale);
|
||||
|
||||
GdkTexture * gdk_cursor_get_dynamic_texture (GdkCursor *cursor,
|
||||
int cursor_size,
|
||||
double scale,
|
||||
int *width,
|
||||
int *height,
|
||||
int *hotspot_x,
|
||||
int *hotspot_y);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
|
@@ -217,9 +217,11 @@ _gdk_macos_cursor_get_ns_cursor (GdkCursor *cursor)
|
||||
|
||||
if (name == NULL)
|
||||
{
|
||||
nscursor = create_cursor_from_texture (gdk_cursor_get_texture (cursor),
|
||||
GdkTexture *texture = gdk_cursor_create_texture (cursor, 1);
|
||||
nscursor = create_cursor_from_texture (texture,
|
||||
gdk_cursor_get_hotspot_x (cursor),
|
||||
gdk_cursor_get_hotspot_y (cursor));
|
||||
g_object_unref (texture);
|
||||
return nscursor;
|
||||
}
|
||||
}
|
||||
|
@@ -35,6 +35,10 @@
|
||||
#include <gdk-pixbuf/gdk-pixbuf.h>
|
||||
#include "cursor/wayland-cursor.h"
|
||||
|
||||
#include "gtk/gtksnapshot.h"
|
||||
#include "gsk/gskrenderer.h"
|
||||
#include "gsk/gskrendernodeprivate.h"
|
||||
|
||||
static void
|
||||
gdk_wayland_cursor_remove_from_cache (gpointer data, GObject *cursor)
|
||||
{
|
||||
@@ -154,15 +158,20 @@ static const struct wl_buffer_listener buffer_listener = {
|
||||
struct wl_buffer *
|
||||
_gdk_wayland_cursor_get_buffer (GdkWaylandDisplay *display,
|
||||
GdkCursor *cursor,
|
||||
guint desired_scale,
|
||||
double desired_scale,
|
||||
gboolean use_viewporter,
|
||||
guint image_index,
|
||||
int *hotspot_x,
|
||||
int *hotspot_y,
|
||||
int *width,
|
||||
int *height,
|
||||
int *scale)
|
||||
double *scale)
|
||||
{
|
||||
GdkTexture *texture;
|
||||
int desired_scale_factor;
|
||||
gboolean can_cache = TRUE;
|
||||
|
||||
desired_scale_factor = (int) ceil (desired_scale);
|
||||
|
||||
if (gdk_cursor_get_name (cursor))
|
||||
{
|
||||
@@ -173,7 +182,7 @@ _gdk_wayland_cursor_get_buffer (GdkWaylandDisplay *display,
|
||||
|
||||
c = gdk_wayland_cursor_load_for_name (display,
|
||||
_gdk_wayland_display_get_cursor_theme (display),
|
||||
desired_scale,
|
||||
desired_scale_factor,
|
||||
gdk_cursor_get_name (cursor));
|
||||
if (c && c->image_count > 0)
|
||||
{
|
||||
@@ -190,7 +199,7 @@ _gdk_wayland_cursor_get_buffer (GdkWaylandDisplay *display,
|
||||
|
||||
image = c->images[image_index];
|
||||
|
||||
cursor_scale = desired_scale;
|
||||
cursor_scale = desired_scale_factor;
|
||||
if ((image->width % cursor_scale != 0) ||
|
||||
(image->height % cursor_scale != 0))
|
||||
{
|
||||
@@ -210,36 +219,44 @@ _gdk_wayland_cursor_get_buffer (GdkWaylandDisplay *display,
|
||||
return wl_cursor_image_get_buffer (image);
|
||||
}
|
||||
}
|
||||
else
|
||||
else if (gdk_cursor_get_texture (cursor))
|
||||
{
|
||||
cairo_surface_t *surface;
|
||||
cairo_surface_t *surface = NULL;
|
||||
struct wl_buffer *buffer;
|
||||
|
||||
texture = g_object_ref (gdk_cursor_get_texture (cursor));
|
||||
|
||||
from_texture:
|
||||
surface = g_hash_table_lookup (display->cursor_surface_cache, cursor);
|
||||
*width = gdk_texture_get_width (texture);
|
||||
*height = gdk_texture_get_height (texture);
|
||||
*scale = 1;
|
||||
|
||||
from_texture2:
|
||||
*hotspot_x = gdk_cursor_get_hotspot_x (cursor);
|
||||
*hotspot_y = gdk_cursor_get_hotspot_y (cursor);
|
||||
|
||||
if (can_cache)
|
||||
surface = g_hash_table_lookup (display->cursor_surface_cache, cursor);
|
||||
|
||||
if (surface == NULL)
|
||||
{
|
||||
surface = gdk_wayland_display_create_shm_surface (display,
|
||||
gdk_texture_get_width (texture),
|
||||
gdk_texture_get_height (texture),
|
||||
&GDK_FRACTIONAL_SCALE_INIT_INT (1));
|
||||
|
||||
|
||||
gdk_texture_download (texture,
|
||||
cairo_image_surface_get_data (surface),
|
||||
cairo_image_surface_get_stride (surface));
|
||||
cairo_surface_mark_dirty (surface);
|
||||
|
||||
g_object_weak_ref (G_OBJECT (cursor), gdk_wayland_cursor_remove_from_cache, display);
|
||||
g_hash_table_insert (display->cursor_surface_cache, cursor, surface);
|
||||
}
|
||||
if (can_cache)
|
||||
{
|
||||
g_object_weak_ref (G_OBJECT (cursor), gdk_wayland_cursor_remove_from_cache, display);
|
||||
|
||||
*hotspot_x = gdk_cursor_get_hotspot_x (cursor);
|
||||
*hotspot_y = gdk_cursor_get_hotspot_y (cursor);
|
||||
*width = gdk_texture_get_width (texture);
|
||||
*height = gdk_texture_get_height (texture);
|
||||
*scale = 1;
|
||||
g_hash_table_insert (display->cursor_surface_cache, cursor, surface);
|
||||
}
|
||||
}
|
||||
|
||||
cairo_surface_reference (surface);
|
||||
buffer = _gdk_wayland_shm_surface_get_wl_buffer (surface);
|
||||
@@ -249,11 +266,33 @@ from_texture:
|
||||
|
||||
return buffer;
|
||||
}
|
||||
else
|
||||
{
|
||||
GdkPaintable *paintable;
|
||||
|
||||
paintable = gdk_cursor_get_paintable (cursor);
|
||||
|
||||
*width = gdk_paintable_get_intrinsic_width (paintable);
|
||||
*height = gdk_paintable_get_intrinsic_height (paintable);
|
||||
if (!use_viewporter)
|
||||
*scale = desired_scale_factor;
|
||||
else
|
||||
*scale = desired_scale;
|
||||
|
||||
texture = gdk_cursor_create_texture (cursor, *scale);
|
||||
|
||||
can_cache = (gdk_paintable_get_flags (paintable) & GDK_PAINTABLE_STATIC_CONTENTS) != 0;
|
||||
|
||||
can_cache = (gdk_paintable_get_flags (paintable) & GDK_PAINTABLE_STATIC_CONTENTS) != 0;
|
||||
|
||||
goto from_texture2;
|
||||
}
|
||||
|
||||
if (gdk_cursor_get_fallback (cursor))
|
||||
return _gdk_wayland_cursor_get_buffer (display,
|
||||
gdk_cursor_get_fallback (cursor),
|
||||
gdk_cursor_get_fallback (cursor),
|
||||
desired_scale,
|
||||
use_viewporter,
|
||||
image_index,
|
||||
hotspot_x, hotspot_y,
|
||||
width, height,
|
||||
|
@@ -62,14 +62,16 @@ struct _GdkWaylandPointerData {
|
||||
uint32_t grab_time;
|
||||
|
||||
struct wl_surface *pointer_surface;
|
||||
struct wp_viewport *pointer_surface_viewport;
|
||||
guint cursor_is_default: 1;
|
||||
GdkCursor *cursor;
|
||||
guint cursor_timeout_id;
|
||||
guint cursor_image_index;
|
||||
guint cursor_image_delay;
|
||||
gulong cursor_invalidated_handler;
|
||||
guint touchpad_event_sequence;
|
||||
|
||||
guint current_output_scale;
|
||||
double current_output_scale;
|
||||
GSList *pointer_surface_outputs;
|
||||
|
||||
/* Accumulated event data for a pointer frame */
|
||||
|
@@ -58,11 +58,12 @@ gdk_wayland_device_set_surface_cursor (GdkDevice *device,
|
||||
{
|
||||
if (!pointer->cursor_is_default)
|
||||
{
|
||||
gdk_wayland_seat_stop_cursor_animation (seat, pointer);
|
||||
|
||||
g_clear_object (&pointer->cursor);
|
||||
pointer->cursor = gdk_cursor_new_from_name ("default", NULL);
|
||||
pointer->cursor_is_default = TRUE;
|
||||
|
||||
gdk_wayland_seat_stop_cursor_animation (seat, pointer);
|
||||
gdk_wayland_device_update_surface_cursor (device);
|
||||
}
|
||||
else
|
||||
@@ -72,10 +73,11 @@ gdk_wayland_device_set_surface_cursor (GdkDevice *device,
|
||||
}
|
||||
else
|
||||
{
|
||||
gdk_wayland_seat_stop_cursor_animation (seat, pointer);
|
||||
|
||||
g_set_object (&pointer->cursor, cursor);
|
||||
pointer->cursor_is_default = FALSE;
|
||||
|
||||
gdk_wayland_seat_stop_cursor_animation (seat, pointer);
|
||||
gdk_wayland_device_update_surface_cursor (device);
|
||||
}
|
||||
}
|
||||
@@ -260,7 +262,8 @@ gdk_wayland_device_update_surface_cursor (GdkDevice *device)
|
||||
GdkWaylandPointerData *pointer =
|
||||
gdk_wayland_device_get_pointer (wayland_device);
|
||||
struct wl_buffer *buffer;
|
||||
int x, y, w, h, scale;
|
||||
int x, y, w, h;
|
||||
double scale;
|
||||
guint next_image_index, next_image_delay;
|
||||
gboolean retval = G_SOURCE_REMOVE;
|
||||
GdkWaylandTabletData *tablet;
|
||||
@@ -272,6 +275,7 @@ gdk_wayland_device_update_surface_cursor (GdkDevice *device)
|
||||
buffer = _gdk_wayland_cursor_get_buffer (GDK_WAYLAND_DISPLAY (seat->display),
|
||||
pointer->cursor,
|
||||
pointer->current_output_scale,
|
||||
pointer->pointer_surface_viewport != NULL,
|
||||
pointer->cursor_image_index,
|
||||
&x, &y, &w, &h, &scale);
|
||||
}
|
||||
@@ -310,7 +314,16 @@ gdk_wayland_device_update_surface_cursor (GdkDevice *device)
|
||||
if (buffer)
|
||||
{
|
||||
wl_surface_attach (pointer->pointer_surface, buffer, 0, 0);
|
||||
if (wl_surface_get_version (pointer->pointer_surface) >= WL_SURFACE_SET_BUFFER_SCALE_SINCE_VERSION)
|
||||
if (pointer->pointer_surface_viewport)
|
||||
{
|
||||
wp_viewport_set_source (pointer->pointer_surface_viewport,
|
||||
wl_fixed_from_int (0),
|
||||
wl_fixed_from_int (0),
|
||||
wl_fixed_from_double (w * scale),
|
||||
wl_fixed_from_double (h * scale));
|
||||
wp_viewport_set_destination (pointer->pointer_surface_viewport, w, h);
|
||||
}
|
||||
else if (wl_surface_get_version (pointer->pointer_surface) >= WL_SURFACE_SET_BUFFER_SCALE_SINCE_VERSION)
|
||||
wl_surface_set_buffer_scale (pointer->pointer_surface, scale);
|
||||
wl_surface_damage (pointer->pointer_surface, 0, 0, w, h);
|
||||
wl_surface_commit (pointer->pointer_surface);
|
||||
@@ -321,6 +334,23 @@ gdk_wayland_device_update_surface_cursor (GdkDevice *device)
|
||||
wl_surface_commit (pointer->pointer_surface);
|
||||
}
|
||||
|
||||
GdkPaintable *paintable = gdk_cursor_get_paintable (pointer->cursor);
|
||||
|
||||
if (paintable)
|
||||
{
|
||||
if ((gdk_paintable_get_flags (paintable) & GDK_PAINTABLE_STATIC_CONTENTS) == 0)
|
||||
{
|
||||
if (pointer->cursor_invalidated_handler == 0)
|
||||
{
|
||||
pointer->cursor_invalidated_handler =
|
||||
g_signal_connect_swapped (paintable, "invalidate-contents",
|
||||
G_CALLBACK (gdk_wayland_device_update_surface_cursor), device);
|
||||
}
|
||||
}
|
||||
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
next_image_index =
|
||||
_gdk_wayland_cursor_get_next_image_index (GDK_WAYLAND_DISPLAY (seat->display),
|
||||
pointer->cursor,
|
||||
|
@@ -115,13 +115,14 @@ void gdk_wayland_display_system_bell (GdkDisplay *display,
|
||||
|
||||
struct wl_buffer *_gdk_wayland_cursor_get_buffer (GdkWaylandDisplay *display,
|
||||
GdkCursor *cursor,
|
||||
guint desired_scale,
|
||||
double desired_scale,
|
||||
gboolean use_viewporter,
|
||||
guint image_index,
|
||||
int *hotspot_x,
|
||||
int *hotspot_y,
|
||||
int *w,
|
||||
int *h,
|
||||
int *scale);
|
||||
double *scale);
|
||||
guint _gdk_wayland_cursor_get_next_image_index (GdkWaylandDisplay *display,
|
||||
GdkCursor *cursor,
|
||||
guint scale,
|
||||
|
@@ -117,6 +117,15 @@ gdk_wayland_seat_stop_cursor_animation (GdkWaylandSeat *seat,
|
||||
}
|
||||
|
||||
pointer->cursor_image_index = 0;
|
||||
|
||||
if (pointer->cursor_invalidated_handler != 0)
|
||||
{
|
||||
GdkPaintable *paintable;
|
||||
|
||||
paintable = gdk_cursor_get_paintable (pointer->cursor);
|
||||
g_signal_handler_disconnect (paintable, pointer->cursor_invalidated_handler);
|
||||
pointer->cursor_invalidated_handler = 0;
|
||||
}
|
||||
}
|
||||
|
||||
GdkWaylandTabletData *
|
||||
@@ -3766,8 +3775,7 @@ pointer_surface_update_scale (GdkDevice *device)
|
||||
GdkWaylandDevice *wayland_device = GDK_WAYLAND_DEVICE (device);
|
||||
GdkWaylandPointerData *pointer =
|
||||
gdk_wayland_device_get_pointer (wayland_device);
|
||||
GdkWaylandDisplay *display_wayland = GDK_WAYLAND_DISPLAY (seat->display);
|
||||
guint32 scale;
|
||||
double scale;
|
||||
GSList *l;
|
||||
|
||||
if (wl_surface_get_version (pointer->pointer_surface) < WL_SURFACE_SET_BUFFER_SCALE_SINCE_VERSION)
|
||||
@@ -3782,8 +3790,8 @@ pointer_surface_update_scale (GdkDevice *device)
|
||||
scale = 1;
|
||||
for (l = pointer->pointer_surface_outputs; l != NULL; l = l->next)
|
||||
{
|
||||
guint32 output_scale = gdk_wayland_display_get_output_scale (display_wayland, l->data);
|
||||
scale = MAX (scale, output_scale);
|
||||
GdkMonitor *monitor = gdk_wayland_display_get_monitor_for_output (seat->display, l->data);
|
||||
scale = MAX (scale, gdk_monitor_get_scale (monitor));
|
||||
}
|
||||
|
||||
if (pointer->current_output_scale == scale)
|
||||
@@ -3878,6 +3886,7 @@ gdk_wayland_pointer_data_finalize (GdkWaylandPointerData *pointer)
|
||||
g_clear_object (&pointer->cursor);
|
||||
wl_surface_destroy (pointer->pointer_surface);
|
||||
g_slist_free (pointer->pointer_surface_outputs);
|
||||
g_clear_pointer (&pointer->pointer_surface_viewport, wp_viewport_destroy);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -4241,6 +4250,9 @@ init_pointer_data (GdkWaylandPointerData *pointer_data,
|
||||
wl_surface_add_listener (pointer_data->pointer_surface,
|
||||
&pointer_surface_listener,
|
||||
logical_device);
|
||||
|
||||
if (display_wayland->viewporter && g_getenv ("POINTER_USE_VIEWPORT"))
|
||||
pointer_data->pointer_surface_viewport = wp_viewporter_get_viewport (display_wayland->viewporter, pointer_data->pointer_surface);
|
||||
}
|
||||
|
||||
void
|
||||
|
@@ -1498,10 +1498,14 @@ gdk_win32_display_get_win32hcursor (GdkWin32Display *display,
|
||||
if (cursor_name)
|
||||
win32hcursor = gdk_win32hcursor_create_for_name (display, cursor_name);
|
||||
else
|
||||
win32hcursor = gdk_win32hcursor_create_for_texture (display,
|
||||
gdk_cursor_get_texture (cursor),
|
||||
gdk_cursor_get_hotspot_x (cursor),
|
||||
gdk_cursor_get_hotspot_y (cursor));
|
||||
{
|
||||
GdkTexture *texture = gdk_cursor_create_texture (cursor, 1);
|
||||
win32hcursor = gdk_win32hcursor_create_for_texture (display,
|
||||
texture,
|
||||
gdk_cursor_get_hotspot_x (cursor),
|
||||
gdk_cursor_get_hotspot_y (cursor));
|
||||
g_object_unref (texture);
|
||||
}
|
||||
|
||||
if (win32hcursor != NULL)
|
||||
{
|
||||
|
@@ -43,6 +43,10 @@
|
||||
#endif
|
||||
#include <string.h>
|
||||
|
||||
#include "gtk/gtksnapshot.h"
|
||||
#include "gsk/gskrenderer.h"
|
||||
#include "gsk/gskrendernodeprivate.h"
|
||||
|
||||
static void
|
||||
gdk_x11_cursor_remove_from_cache (gpointer data, GObject *cursor)
|
||||
{
|
||||
@@ -371,10 +375,15 @@ gdk_x11_display_get_xcursor (GdkDisplay *display,
|
||||
if (gdk_cursor_get_name (cursor))
|
||||
xcursor = gdk_x11_cursor_create_for_name (display, gdk_cursor_get_name (cursor));
|
||||
else
|
||||
xcursor = gdk_x11_cursor_create_for_texture (display,
|
||||
gdk_cursor_get_texture (cursor),
|
||||
gdk_cursor_get_hotspot_x (cursor),
|
||||
gdk_cursor_get_hotspot_y (cursor));
|
||||
{
|
||||
GdkTexture *texture = gdk_cursor_create_texture (cursor, 1);
|
||||
|
||||
xcursor = gdk_x11_cursor_create_for_texture (display,
|
||||
texture,
|
||||
gdk_cursor_get_hotspot_x (cursor),
|
||||
gdk_cursor_get_hotspot_y (cursor));
|
||||
g_object_unref (texture);
|
||||
}
|
||||
|
||||
if (xcursor != None)
|
||||
{
|
||||
|
BIN
tests/all-scroll.png
Normal file
BIN
tests/all-scroll.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.3 KiB |
@@ -1,5 +1,6 @@
|
||||
gtk_tests = [
|
||||
# testname, optional extra sources
|
||||
['testpaintablecursor'],
|
||||
['devicepixels'],
|
||||
['testsections'],
|
||||
['testfilelauncher'],
|
||||
|
186
tests/testpaintablecursor.c
Normal file
186
tests/testpaintablecursor.c
Normal file
@@ -0,0 +1,186 @@
|
||||
/* testpaintablecursor.c
|
||||
* Copyright (C) 2024 Red Hat, Inc.
|
||||
* Author: Matthias Clasen
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
G_DECLARE_FINAL_TYPE (MyPaintable, my_paintable, MY, PAINTABLE, GObject)
|
||||
|
||||
struct _MyPaintable
|
||||
{
|
||||
GObject parent_instance;
|
||||
GdkTexture *texture;
|
||||
int width;
|
||||
int height;
|
||||
double angle;
|
||||
|
||||
guint timeout_id;
|
||||
};
|
||||
|
||||
struct _MyPaintableClass
|
||||
{
|
||||
GObjectClass parent_class;
|
||||
};
|
||||
|
||||
|
||||
static void
|
||||
my_paintable_snapshot (GdkPaintable *paintable,
|
||||
GdkSnapshot *snapshot,
|
||||
double width,
|
||||
double height)
|
||||
{
|
||||
MyPaintable *self = MY_PAINTABLE (paintable);
|
||||
|
||||
gtk_snapshot_push_opacity (GTK_SNAPSHOT (snapshot), 0.5 * sin (self->angle) + 0.5);
|
||||
gtk_snapshot_append_texture (GTK_SNAPSHOT (snapshot), self->texture,
|
||||
&GRAPHENE_RECT_INIT (0, 0, width, height));
|
||||
gtk_snapshot_pop (GTK_SNAPSHOT (snapshot));
|
||||
}
|
||||
|
||||
static int
|
||||
my_paintable_get_intrinsic_width (GdkPaintable *paintable)
|
||||
{
|
||||
MyPaintable *self = MY_PAINTABLE (paintable);
|
||||
|
||||
return self->width;
|
||||
}
|
||||
|
||||
static int
|
||||
my_paintable_get_intrinsic_height (GdkPaintable *paintable)
|
||||
{
|
||||
MyPaintable *self = MY_PAINTABLE (paintable);
|
||||
|
||||
return self->height;
|
||||
}
|
||||
|
||||
static void
|
||||
my_paintable_init_interface (GdkPaintableInterface *iface)
|
||||
{
|
||||
iface->snapshot = my_paintable_snapshot;
|
||||
iface->get_intrinsic_width = my_paintable_get_intrinsic_width;
|
||||
iface->get_intrinsic_height = my_paintable_get_intrinsic_height;
|
||||
}
|
||||
|
||||
G_DEFINE_TYPE_WITH_CODE (MyPaintable, my_paintable, G_TYPE_OBJECT,
|
||||
G_IMPLEMENT_INTERFACE (GDK_TYPE_PAINTABLE,
|
||||
my_paintable_init_interface))
|
||||
|
||||
static void
|
||||
my_paintable_init (MyPaintable *self)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
my_paintable_dispose (GObject *object)
|
||||
{
|
||||
MyPaintable *self = MY_PAINTABLE (object);
|
||||
|
||||
g_clear_object (&self->texture);
|
||||
|
||||
g_source_remove (self->timeout_id);
|
||||
self->timeout_id = 0;
|
||||
|
||||
G_OBJECT_CLASS (my_paintable_parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
static void
|
||||
my_paintable_class_init (MyPaintableClass *class)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (class);
|
||||
|
||||
object_class->dispose = my_paintable_dispose;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
rotate_cb (gpointer data)
|
||||
{
|
||||
MyPaintable *self = data;
|
||||
|
||||
self->angle += 20;
|
||||
|
||||
g_signal_emit_by_name (self, "invalidate-contents", 0);
|
||||
|
||||
return G_SOURCE_CONTINUE;
|
||||
}
|
||||
|
||||
static GdkPaintable *
|
||||
my_paintable_new (GdkTexture *texture, int width, int height)
|
||||
{
|
||||
MyPaintable *self;
|
||||
|
||||
self = g_object_new (my_paintable_get_type (), NULL);
|
||||
self->texture = g_object_ref (texture);
|
||||
self->width = width;
|
||||
self->height = height;
|
||||
|
||||
self->angle = 0;
|
||||
|
||||
self->timeout_id = g_timeout_add (100, rotate_cb, self);
|
||||
|
||||
return GDK_PAINTABLE (self);
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
GtkWidget *window, *button;
|
||||
gboolean done = FALSE;
|
||||
GdkTexture *texture;
|
||||
GdkCursor *cursor;
|
||||
GdkPaintable *paintable;
|
||||
|
||||
gtk_init ();
|
||||
|
||||
window = gtk_window_new ();
|
||||
gtk_window_set_title (GTK_WINDOW (window), "hello world");
|
||||
gtk_window_set_resizable (GTK_WINDOW (window), FALSE);
|
||||
|
||||
button = gtk_button_new ();
|
||||
gtk_button_set_label (GTK_BUTTON (button), "hello world");
|
||||
gtk_widget_set_margin_top (button, 10);
|
||||
gtk_widget_set_margin_bottom (button, 10);
|
||||
gtk_widget_set_margin_start (button, 10);
|
||||
gtk_widget_set_margin_end (button, 10);
|
||||
|
||||
/* A 96x96 image */
|
||||
texture = gdk_texture_new_from_filename ("tests/all-scroll.png", NULL);
|
||||
|
||||
#if 0
|
||||
cursor = gdk_cursor_new_from_name ("all-scroll", NULL);
|
||||
#elif 0
|
||||
cursor = gdk_cursor_new_from_texture (texture, gdk_texture_ge t_width (texture) / 2, gdk_texture_get_height (texture) / 2, NULL);
|
||||
#else
|
||||
/* We create a cursor of size 32x32 */
|
||||
paintable = my_paintable_new (texture, 32, 32);
|
||||
cursor = gdk_cursor_new_from_paintable (paintable, 16, 16, NULL);
|
||||
g_object_unref (paintable);
|
||||
#endif
|
||||
|
||||
g_object_unref (texture);
|
||||
|
||||
gtk_widget_set_cursor (button, cursor);
|
||||
g_object_unref (cursor);
|
||||
|
||||
gtk_window_set_child (GTK_WINDOW (window), button);
|
||||
|
||||
gtk_window_present (GTK_WINDOW (window));
|
||||
|
||||
while (!done)
|
||||
g_main_context_iteration (NULL, TRUE);
|
||||
|
||||
return 0;
|
||||
}
|
Reference in New Issue
Block a user