diff --git a/gdk/broadway/gdkcursor-broadway.c b/gdk/broadway/gdkcursor-broadway.c index 92acbdaee6..72fc84e1e2 100644 --- a/gdk/broadway/gdkcursor-broadway.c +++ b/gdk/broadway/gdkcursor-broadway.c @@ -51,7 +51,9 @@ struct _GdkBroadwayCursorClass G_DEFINE_TYPE (GdkBroadwayCursor, gdk_broadway_cursor, GDK_TYPE_CURSOR) -static GdkPixbuf* gdk_broadway_cursor_get_image (GdkCursor *cursor); +static cairo_surface_t * gdk_broadway_cursor_get_surface (GdkCursor *cursor, + gdouble *x_hot, + gdouble *y_hot); static void gdk_broadway_cursor_finalize (GObject *object) @@ -67,7 +69,7 @@ gdk_broadway_cursor_class_init (GdkBroadwayCursorClass *xcursor_class) object_class->finalize = gdk_broadway_cursor_finalize; - cursor_class->get_image = gdk_broadway_cursor_get_image; + cursor_class->get_surface = gdk_broadway_cursor_get_surface; } static void @@ -99,8 +101,10 @@ _gdk_broadway_display_get_cursor_for_type (GdkDisplay *display, return GDK_CURSOR (private); } -static GdkPixbuf* -gdk_broadway_cursor_get_image (GdkCursor *cursor) +static cairo_surface_t * +gdk_broadway_cursor_get_surface (GdkCursor *cursor, + gdouble *x_hot, + gdouble *y_hot) { g_return_val_if_fail (cursor != NULL, NULL); diff --git a/gdk/gdkcursor.c b/gdk/gdkcursor.c index 0b2b80d9aa..d1825913d6 100644 --- a/gdk/gdkcursor.c +++ b/gdk/gdkcursor.c @@ -24,12 +24,16 @@ #include "config.h" +#define GDK_PIXBUF_ENABLE_BACKEND +#include + #include "gdkcursor.h" #include "gdkcursorprivate.h" #include "gdkdisplayprivate.h" #include "gdkintl.h" #include "gdkinternals.h" +#include /** * SECTION:cursors @@ -389,7 +393,74 @@ gdk_cursor_get_display (GdkCursor *cursor) GdkPixbuf* gdk_cursor_get_image (GdkCursor *cursor) { + int w, h; + cairo_surface_t *surface; + GdkPixbuf *pixbuf; + gchar buf[32]; + double x_hot, y_hot; + double x_scale, y_scale; + g_return_val_if_fail (GDK_IS_CURSOR (cursor), NULL); - return GDK_CURSOR_GET_CLASS (cursor)->get_image (cursor); + surface = gdk_cursor_get_surface (cursor, &x_hot, &y_hot); + if (surface == NULL) + return NULL; + + w = cairo_image_surface_get_width (surface); + h = cairo_image_surface_get_height (surface); + + x_scale = y_scale = 1; +#ifdef HAVE_CAIRO_SURFACE_SET_DEVICE_SCALE + cairo_surface_get_device_scale (surface, &x_scale, &y_scale); +#endif + + pixbuf = gdk_pixbuf_get_from_surface (surface, 0, 0, w, h); + cairo_surface_destroy (surface); + + if (x_scale != 1) + { + GdkPixbuf *old; + + old = pixbuf; + pixbuf = gdk_pixbuf_scale_simple (old, + w / x_scale, h / y_scale, + GDK_INTERP_HYPER); + g_object_unref (old); + } + + + g_snprintf (buf, 32, "%d", (int)x_hot); + gdk_pixbuf_set_option (pixbuf, "x_hot", buf); + + g_snprintf (buf, 32, "%d", (int)y_hot); + gdk_pixbuf_set_option (pixbuf, "y_hot", buf); + + return pixbuf; +} + +/** + * gdk_cursor_get_surface: + * @cursor: a #GdkCursor + * @x_hot: Location to store the hotspot x position, or %NULL + * @y_hot: Location to store the hotspot y position, or %NULL + * + * Returns a #cairo_surface_t (image surface) with the image used to display the cursor. + * + * Note that depending on the capabilities of the windowing system and + * on the cursor, GDK may not be able to obtain the image data. In this + * case, %NULL is returned. + * + * Returns: (transfer full): a #cairo_surface_t representing @cursor, or %NULL + * + * Since: 3.10 + */ +cairo_surface_t * +gdk_cursor_get_surface (GdkCursor *cursor, + gdouble *x_hot, + gdouble *y_hot) +{ + g_return_val_if_fail (GDK_IS_CURSOR (cursor), NULL); + + return GDK_CURSOR_GET_CLASS (cursor)->get_surface (cursor, + x_hot, y_hot); } diff --git a/gdk/gdkcursor.h b/gdk/gdkcursor.h index c526101975..50880b3e66 100644 --- a/gdk/gdkcursor.h +++ b/gdk/gdkcursor.h @@ -239,6 +239,10 @@ GDK_DEPRECATED_IN_3_0_FOR(g_object_unref) void gdk_cursor_unref (GdkCursor *cursor); GDK_AVAILABLE_IN_ALL GdkPixbuf* gdk_cursor_get_image (GdkCursor *cursor); +GDK_AVAILABLE_IN_3_10 +cairo_surface_t *gdk_cursor_get_surface (GdkCursor *cursor, + gdouble *x_hot, + gdouble *y_hot); GDK_AVAILABLE_IN_ALL GdkCursorType gdk_cursor_get_cursor_type (GdkCursor *cursor); diff --git a/gdk/gdkcursorprivate.h b/gdk/gdkcursorprivate.h index e637d3d72f..15dbe037b5 100644 --- a/gdk/gdkcursorprivate.h +++ b/gdk/gdkcursorprivate.h @@ -47,7 +47,9 @@ struct _GdkCursorClass { GObjectClass parent_class; - GdkPixbuf * (* get_image) (GdkCursor * cursor); + cairo_surface_t * (* get_surface) (GdkCursor *cursor, + gdouble *x_hot, + gdouble *y_hot); }; G_END_DECLS diff --git a/gdk/quartz/gdkcursor-quartz.c b/gdk/quartz/gdkcursor-quartz.c index 7e5adc55eb..1b1ed3e952 100644 --- a/gdk/quartz/gdkcursor-quartz.c +++ b/gdk/quartz/gdkcursor-quartz.c @@ -284,7 +284,9 @@ _gdk_quartz_display_get_cursor_for_name (GdkDisplay *display, G_DEFINE_TYPE (GdkQuartzCursor, gdk_quartz_cursor, GDK_TYPE_CURSOR) -static GdkPixbuf *gdk_quartz_cursor_get_image (GdkCursor *cursor); +static cairo_surface_t *gdk_quartz_cursor_get_surface (GdkCursor *cursor, + gdouble *x_hot, + gdouble *y_hot); static void gdk_quartz_cursor_finalize (GObject *object) @@ -304,7 +306,7 @@ gdk_quartz_cursor_class_init (GdkQuartzCursorClass *quartz_cursor_class) object_class->finalize = gdk_quartz_cursor_finalize; - cursor_class->get_image = gdk_quartz_cursor_get_image; + cursor_class->get_surface = gdk_quartz_cursor_get_surface; } static void @@ -360,8 +362,10 @@ _gdk_quartz_cursor_get_ns_cursor (GdkCursor *cursor) return cursor_private->nscursor; } -static GdkPixbuf * -gdk_quartz_cursor_get_image (GdkCursor *cursor) +static cairo_surface_t * +gdk_quartz_cursor_get_surface (GdkCursor *cursor, + gdouble *x_hot, + gdouble *y_hot) { /* FIXME: Implement */ return NULL; diff --git a/gdk/wayland/gdkcursor-wayland.c b/gdk/wayland/gdkcursor-wayland.c index 25a86de74a..035106fd6d 100644 --- a/gdk/wayland/gdkcursor-wayland.c +++ b/gdk/wayland/gdkcursor-wayland.c @@ -174,8 +174,10 @@ gdk_wayland_cursor_finalize (GObject *object) G_OBJECT_CLASS (_gdk_wayland_cursor_parent_class)->finalize (object); } -static GdkPixbuf* -gdk_wayland_cursor_get_image (GdkCursor *cursor) +static cairo_surface_t * +gdk_wayland_cursor_get_surface (GdkCursor *cursor, + gdouble *x_hot, + gdouble *y_hot) { return NULL; } @@ -258,7 +260,7 @@ _gdk_wayland_cursor_class_init (GdkWaylandCursorClass *wayland_cursor_class) object_class->finalize = gdk_wayland_cursor_finalize; - cursor_class->get_image = gdk_wayland_cursor_get_image; + cursor_class->get_surface = gdk_wayland_cursor_get_surface; } static void diff --git a/gdk/win32/gdkcursor-win32.c b/gdk/win32/gdkcursor-win32.c index 86e7d7bd3e..1ce7cb1ec4 100644 --- a/gdk/win32/gdkcursor-win32.c +++ b/gdk/win32/gdkcursor-win32.c @@ -250,7 +250,7 @@ _gdk_win32_display_get_cursor_for_name (GdkDisplay *display, } GdkPixbuf * -gdk_win32_icon_to_pixbuf_libgtk_only (HICON hicon) +gdk_win32_icon_to_pixbuf_libgtk_only (HICON hicon, gint *x_hot, gint *y_hot) { GdkPixbuf *pixbuf = NULL; ICONINFO ii; @@ -407,11 +407,10 @@ gdk_win32_icon_to_pixbuf_libgtk_only (HICON hicon) } } - g_snprintf (buf, sizeof (buf), "%ld", ii.xHotspot); - gdk_pixbuf_set_option (pixbuf, "x_hot", buf); - - g_snprintf (buf, sizeof (buf), "%ld", ii.yHotspot); - gdk_pixbuf_set_option (pixbuf, "y_hot", buf); + if (x_hot) + *x_hot = ii.xHotspot; + if (y_hot) + *y_hot = ii.yHotspot; /* release temporary resources */ out2: @@ -425,12 +424,25 @@ gdk_win32_icon_to_pixbuf_libgtk_only (HICON hicon) return pixbuf; } -static GdkPixbuf * -_gdk_win32_cursor_get_image (GdkCursor *cursor) +static cairo_surface_t * +_gdk_win32_cursor_get_surface (GdkCursor *cursor, + gdouble *x_hot, + gdouble *y_hot) { + GdkPixbuf *pixbuf; + cairo_surface_t *surface + g_return_val_if_fail (cursor != NULL, NULL); - return gdk_win32_icon_to_pixbuf_libgtk_only (((GdkWin32Cursor *) cursor)->hcursor); + pixbuf = gdk_win32_icon_to_pixbuf_libgtk_only (((GdkWin32Cursor *) cursor)->hcursor, x_hot, y_hot); + + if (pixbuf == NULL) + return NULL; + + surface = gdk_cairo_surface_create_from_pixbuf (pixbuf, 1, NULL); + g_object_unref (pixbuf); + + return surface; } GdkCursor * @@ -830,5 +842,5 @@ gdk_win32_cursor_class_init(GdkWin32CursorClass *klass) object_class->finalize = _gdk_win32_cursor_finalize; - cursor_class->get_image = _gdk_win32_cursor_get_image; + cursor_class->get_surface = _gdk_win32_cursor_get_surface; } diff --git a/gdk/x11/gdkcursor-x11.c b/gdk/x11/gdkcursor-x11.c index 8d4da14a3e..507ec4258b 100644 --- a/gdk/x11/gdkcursor-x11.c +++ b/gdk/x11/gdkcursor-x11.c @@ -169,7 +169,9 @@ _gdk_x11_cursor_display_finalize (GdkDisplay *display) G_DEFINE_TYPE (GdkX11Cursor, gdk_x11_cursor, GDK_TYPE_CURSOR) -static GdkPixbuf* gdk_x11_cursor_get_image (GdkCursor *cursor); +static cairo_surface_t *gdk_x11_cursor_get_surface (GdkCursor *cursor, + gdouble *x_hot, + gdouble *y_hot); static void gdk_x11_cursor_finalize (GObject *object) @@ -194,7 +196,7 @@ gdk_x11_cursor_class_init (GdkX11CursorClass *xcursor_class) object_class->finalize = gdk_x11_cursor_finalize; - cursor_class->get_image = gdk_x11_cursor_get_image; + cursor_class->get_surface = gdk_x11_cursor_get_surface; } static void @@ -316,22 +318,25 @@ gdk_x11_cursor_get_xcursor (GdkCursor *cursor) #if defined(HAVE_XCURSOR) && defined(HAVE_XFIXES) && XFIXES_MAJOR >= 2 -static GdkPixbuf* -gdk_x11_cursor_get_image (GdkCursor *cursor) +static cairo_surface_t * +gdk_x11_cursor_get_surface (GdkCursor *cursor, + gdouble *x_hot, + gdouble *y_hot) { + GdkDisplay *display; Display *xdisplay; GdkX11Cursor *private; XcursorImages *images = NULL; XcursorImage *image; gint size; - gchar buf[32]; - guchar *data, *p, tmp; - GdkPixbuf *pixbuf; + cairo_surface_t *surface; + gint scale; gchar *theme; private = GDK_X11_CURSOR (cursor); - - xdisplay = GDK_DISPLAY_XDISPLAY (gdk_cursor_get_display (cursor)); + + display = gdk_cursor_get_display (cursor); + xdisplay = GDK_DISPLAY_XDISPLAY (display); size = XcursorGetDefaultSize (xdisplay); theme = XcursorGetTheme (xdisplay); @@ -349,31 +354,30 @@ gdk_x11_cursor_get_image (GdkCursor *cursor) image = images->images[0]; - data = g_malloc (4 * image->width * image->height); - memcpy (data, image->pixels, 4 * image->width * image->height); + /* Assume the currently set cursor was defined for the screen + scale */ + scale = + gdk_screen_get_monitor_scale_factor (gdk_display_get_default_screen (display), 0); - for (p = data; p < data + (4 * image->width * image->height); p += 4) - { - tmp = p[0]; - p[0] = p[2]; - p[2] = tmp; - } + surface = gdk_window_create_similar_image_surface (NULL, + CAIRO_FORMAT_ARGB32, + image->width, + image->height, + scale); - pixbuf = gdk_pixbuf_new_from_data (data, GDK_COLORSPACE_RGB, TRUE, - 8, image->width, image->height, - 4 * image->width, - (GdkPixbufDestroyNotify)g_free, NULL); + memcpy (cairo_image_surface_get_data (surface), + image->pixels, 4 * image->width * image->height); - if (private->name) - gdk_pixbuf_set_option (pixbuf, "name", private->name); - g_snprintf (buf, 32, "%d", image->xhot); - gdk_pixbuf_set_option (pixbuf, "x_hot", buf); - g_snprintf (buf, 32, "%d", image->yhot); - gdk_pixbuf_set_option (pixbuf, "y_hot", buf); + cairo_surface_mark_dirty (surface); + + if (x_hot) + *x_hot = (double)image->xhot / scale; + if (y_hot) + *y_hot = (double)image->yhot / scale; XcursorImagesDestroy (images); - return pixbuf; + return surface; } void @@ -484,8 +488,10 @@ gdk_x11_display_set_cursor_theme (GdkDisplay *display, #else -static GdkPixbuf* -gdk_x11_cursor_get_image (GdkCursor *cursor) +static cairo_surface_t * +gdk_x11_cursor_get_surface (GdkCursor *cursor, + gdouble *x_hot, + gdouble *y_hot) { return NULL; }