diff --git a/gdk/wayland/gdkcursor-wayland.c b/gdk/wayland/gdkcursor-wayland.c index 8fa1c0407c..665c224d17 100644 --- a/gdk/wayland/gdkcursor-wayland.c +++ b/gdk/wayland/gdkcursor-wayland.c @@ -59,6 +59,7 @@ struct _GdkWaylandCursor } surface; struct wl_cursor *wl_cursor; + int scale; }; struct _GdkWaylandCursorClass @@ -83,11 +84,18 @@ _gdk_wayland_display_finalize_cursors (GdkWaylandDisplay *display) } static gboolean -set_cursor_from_theme (GdkWaylandCursor *cursor, - struct wl_cursor_theme *theme) +_gdk_wayland_cursor_update (GdkWaylandDisplay *wayland_display, + GdkWaylandCursor *cursor) { struct wl_cursor *c; + struct wl_cursor_theme *theme; + /* Do nothing if this is not a wl_cursor cursor. */ + if (cursor->name == NULL) + return FALSE; + + theme = _gdk_wayland_display_get_scaled_cursor_theme (wayland_display, + cursor->scale); c = wl_cursor_theme_get_cursor (theme, cursor->name); if (!c) { @@ -106,8 +114,7 @@ set_cursor_from_theme (GdkWaylandCursor *cursor, } void -_gdk_wayland_display_update_cursors (GdkWaylandDisplay *display, - struct wl_cursor_theme *theme) +_gdk_wayland_display_update_cursors (GdkWaylandDisplay *display) { GHashTableIter iter; const char *name; @@ -116,7 +123,7 @@ _gdk_wayland_display_update_cursors (GdkWaylandDisplay *display, g_hash_table_iter_init (&iter, display->cursor_cache); while (g_hash_table_iter_next (&iter, (gpointer *) &name, (gpointer *) &cursor)) - set_cursor_from_theme (cursor, theme); + _gdk_wayland_cursor_update (display, cursor); } static void @@ -169,7 +176,7 @@ _gdk_wayland_cursor_get_buffer (GdkCursor *cursor, *w = image->width; *h = image->height; - *scale = 1; + *scale = wayland_cursor->scale; return wl_cursor_image_get_buffer (image); } @@ -217,6 +224,28 @@ _gdk_wayland_cursor_get_next_image_index (GdkCursor *cursor, return current_image_index; } +void +_gdk_wayland_cursor_set_scale (GdkCursor *cursor, + guint scale) +{ + GdkWaylandDisplay *wayland_display = + GDK_WAYLAND_DISPLAY (gdk_cursor_get_display (cursor)); + GdkWaylandCursor *wayland_cursor = GDK_WAYLAND_CURSOR (cursor); + + if (scale > GDK_WAYLAND_MAX_THEME_SCALE) + { + g_warning (G_STRLOC ": cursor theme size %u too large", scale); + scale = GDK_WAYLAND_MAX_THEME_SCALE; + } + + if (wayland_cursor->scale == scale) + return; + + wayland_cursor->scale = scale; + + _gdk_wayland_cursor_update (wayland_display, wayland_cursor); +} + static void _gdk_wayland_cursor_class_init (GdkWaylandCursorClass *wayland_cursor_class) { @@ -233,9 +262,15 @@ _gdk_wayland_cursor_init (GdkWaylandCursor *cursor) { } +static GdkCursor * +_gdk_wayland_display_get_cursor_for_name_with_scale (GdkDisplay *display, + const gchar *name, + guint scale); + GdkCursor * -_gdk_wayland_display_get_cursor_for_type (GdkDisplay *display, - GdkCursorType cursor_type) +_gdk_wayland_display_get_cursor_for_type_with_scale (GdkDisplay *display, + GdkCursorType cursor_type, + guint scale) { GEnumClass *enum_class; GEnumValue *enum_value; @@ -248,7 +283,9 @@ _gdk_wayland_display_get_cursor_for_type (GdkDisplay *display, g_strdelimit (cursor_name, "-", '_'); g_type_class_unref (enum_class); - result = _gdk_wayland_display_get_cursor_for_name (display, cursor_name); + result = _gdk_wayland_display_get_cursor_for_name_with_scale (display, + cursor_name, + scale); g_free (cursor_name); @@ -256,8 +293,18 @@ _gdk_wayland_display_get_cursor_for_type (GdkDisplay *display, } GdkCursor * -_gdk_wayland_display_get_cursor_for_name (GdkDisplay *display, - const gchar *name) +_gdk_wayland_display_get_cursor_for_type (GdkDisplay *display, + GdkCursorType cursor_type) +{ + return _gdk_wayland_display_get_cursor_for_type_with_scale (display, + cursor_type, + 1); +} + +static GdkCursor * +_gdk_wayland_display_get_cursor_for_name_with_scale (GdkDisplay *display, + const gchar *name, + guint scale) { GdkWaylandCursor *private; GdkWaylandDisplay *wayland_display = GDK_WAYLAND_DISPLAY (display); @@ -273,12 +320,13 @@ _gdk_wayland_display_get_cursor_for_name (GdkDisplay *display, "display", display, NULL); private->name = g_strdup (name); + private->scale = scale; /* Blank cursor case */ if (!name || g_str_equal (name, "blank_cursor")) return GDK_CURSOR (private); - if (!set_cursor_from_theme (private, wayland_display->cursor_theme)) + if (!_gdk_wayland_cursor_update (wayland_display, private)) return GDK_CURSOR (private); /* Insert into cache. */ @@ -286,6 +334,13 @@ _gdk_wayland_display_get_cursor_for_name (GdkDisplay *display, return GDK_CURSOR (private); } +GdkCursor * +_gdk_wayland_display_get_cursor_for_name (GdkDisplay *display, + const gchar *name) +{ + return _gdk_wayland_display_get_cursor_for_name_with_scale (display, name, 1); +} + GdkCursor * _gdk_wayland_display_get_cursor_for_surface (GdkDisplay *display, cairo_surface_t *surface, diff --git a/gdk/wayland/gdkdevice-wayland.c b/gdk/wayland/gdkdevice-wayland.c index 4837a129d5..a22351b4d7 100644 --- a/gdk/wayland/gdkdevice-wayland.c +++ b/gdk/wayland/gdkdevice-wayland.c @@ -93,6 +93,8 @@ struct _GdkWaylandDeviceData GdkDragContext *drop_context; struct wl_surface *pointer_surface; + guint current_output_scale; + GSList *pointer_surface_outputs; }; struct _GdkWaylandDevice @@ -253,7 +255,15 @@ gdk_wayland_device_set_window_cursor (GdkDevice *device, * the default cursor */ if (!cursor) - cursor = _gdk_wayland_display_get_cursor_for_type (device->display, GDK_LEFT_PTR); + { + guint scale = wd->current_output_scale; + cursor = + _gdk_wayland_display_get_cursor_for_type_with_scale (wd->display, + GDK_LEFT_PTR, + scale); + } + else + _gdk_wayland_cursor_set_scale (cursor, wd->current_output_scale); if (cursor == wd->cursor) return; @@ -1756,6 +1766,78 @@ init_devices (GdkWaylandDeviceData *device) _gdk_device_set_associated_device (device->master_keyboard, device->master_pointer); } +static void +pointer_surface_update_scale (GdkWaylandDeviceData *device) +{ + GdkWaylandDisplay *wayland_display = GDK_WAYLAND_DISPLAY (device->display); + guint32 scale; + GSList *l; + + if (wayland_display->compositor_version < WL_SURFACE_HAS_BUFFER_SCALE) + { + /* We can't set the scale on this surface */ + return; + } + + scale = 1; + for (l = device->pointer_surface_outputs; l != NULL; l = l->next) + { + guint32 output_scale = + _gdk_wayland_screen_get_output_scale (wayland_display->screen, + l->data); + scale = MAX (scale, output_scale); + } + + device->current_output_scale = scale; + + if (device->grab_cursor) + _gdk_wayland_cursor_set_scale (device->grab_cursor, scale); + if (device->cursor) + _gdk_wayland_cursor_set_scale (device->cursor, scale); + + gdk_wayland_device_update_window_cursor (device); +} + +static void +pointer_surface_enter (void *data, + struct wl_surface *wl_surface, + struct wl_output *output) + +{ + GdkWaylandDeviceData *device = data; + + GDK_NOTE (EVENTS, + g_message ("pointer surface of device %p entered output %p", + device, output)); + + device->pointer_surface_outputs = + g_slist_append (device->pointer_surface_outputs, output); + + pointer_surface_update_scale (device); +} + +static void +pointer_surface_leave (void *data, + struct wl_surface *wl_surface, + struct wl_output *output) +{ + GdkWaylandDeviceData *device = data; + + GDK_NOTE (EVENTS, + g_message ("pointer surface of device %p left output %p", + device, output)); + + device->pointer_surface_outputs = + g_slist_remove (device->pointer_surface_outputs, output); + + pointer_surface_update_scale (device); +} + +static const struct wl_surface_listener pointer_surface_listener = { + pointer_surface_enter, + pointer_surface_leave +}; + void _gdk_wayland_device_manager_add_seat (GdkDeviceManager *device_manager, guint32 id, @@ -1786,8 +1868,12 @@ _gdk_wayland_device_manager_add_seat (GdkDeviceManager *device_manager, wl_data_device_add_listener (device->data_device, &data_device_listener, device); + device->current_output_scale = 1; device->pointer_surface = wl_compositor_create_surface (display_wayland->compositor); + wl_surface_add_listener (device->pointer_surface, + &pointer_surface_listener, + device); init_devices (device); } diff --git a/gdk/wayland/gdkdisplay-wayland.c b/gdk/wayland/gdkdisplay-wayland.c index 21369de2c1..5caf0e1684 100644 --- a/gdk/wayland/gdkdisplay-wayland.c +++ b/gdk/wayland/gdkdisplay-wayland.c @@ -581,6 +581,7 @@ gdk_wayland_display_set_cursor_theme (GdkDisplay *display, { GdkWaylandDisplay *wayland_display = GDK_WAYLAND_DISPLAY(display); struct wl_cursor_theme *theme; + int i; g_assert (wayland_display); g_assert (wayland_display->shm); @@ -592,11 +593,49 @@ gdk_wayland_display_set_cursor_theme (GdkDisplay *display, return; } - _gdk_wayland_display_update_cursors (wayland_display, theme); + for (i = 0; i < GDK_WAYLAND_THEME_SCALES_COUNT; i++) + { + if (wayland_display->scaled_cursor_themes[i]) + { + wl_cursor_theme_destroy (wayland_display->scaled_cursor_themes[i]); + wayland_display->scaled_cursor_themes[i] = NULL; + } + } + wayland_display->scaled_cursor_themes[0] = theme; + if (wayland_display->cursor_theme_name != NULL) + free (wayland_display->cursor_theme_name); + wayland_display->cursor_theme_name = g_strdup (name); + wayland_display->cursor_theme_size = size; - if (wayland_display->cursor_theme != NULL) - wl_cursor_theme_destroy (wayland_display->cursor_theme); - wayland_display->cursor_theme = theme; + _gdk_wayland_display_update_cursors (wayland_display); +} + +struct wl_cursor_theme * +_gdk_wayland_display_get_scaled_cursor_theme (GdkWaylandDisplay *wayland_display, + guint scale) +{ + struct wl_cursor_theme *theme; + + g_assert (wayland_display->cursor_theme_name); + g_assert (scale <= GDK_WAYLAND_MAX_THEME_SCALE); + g_assert (scale >= 1); + + theme = wayland_display->scaled_cursor_themes[scale - 1]; + if (!theme) + { + theme = wl_cursor_theme_load (wayland_display->cursor_theme_name, + wayland_display->cursor_theme_size * scale, + wayland_display->shm); + if (theme == NULL) + { + g_warning ("Failed to load cursor theme %s with scale %u\n", + wayland_display->cursor_theme_name, scale); + return NULL; + } + wayland_display->scaled_cursor_themes[scale - 1] = theme; + } + + return theme; } static void diff --git a/gdk/wayland/gdkdisplay-wayland.h b/gdk/wayland/gdkdisplay-wayland.h index 48de6f90b4..781be518ef 100644 --- a/gdk/wayland/gdkdisplay-wayland.h +++ b/gdk/wayland/gdkdisplay-wayland.h @@ -42,6 +42,9 @@ G_BEGIN_DECLS +#define GDK_WAYLAND_MAX_THEME_SCALE 2 +#define GDK_WAYLAND_THEME_SCALES_COUNT GDK_WAYLAND_MAX_THEME_SCALE + typedef struct _GdkWaylandSelection GdkWaylandSelection; struct _GdkWaylandDisplay @@ -69,7 +72,9 @@ struct _GdkWaylandDisplay struct wl_data_device_manager *data_device_manager; struct wl_subcompositor *subcompositor; - struct wl_cursor_theme *cursor_theme; + struct wl_cursor_theme *scaled_cursor_themes[GDK_WAYLAND_THEME_SCALES_COUNT]; + gchar *cursor_theme_name; + int cursor_theme_size; GHashTable *cursor_cache; GSource *event_source; diff --git a/gdk/wayland/gdkprivate-wayland.h b/gdk/wayland/gdkprivate-wayland.h index dabd22d6ae..0c634ae2a9 100644 --- a/gdk/wayland/gdkprivate-wayland.h +++ b/gdk/wayland/gdkprivate-wayland.h @@ -56,11 +56,16 @@ gboolean _gdk_wayland_keymap_key_is_modifier (GdkKeymap *keymap, void _gdk_wayland_display_init_cursors (GdkWaylandDisplay *display); void _gdk_wayland_display_finalize_cursors (GdkWaylandDisplay *display); -void _gdk_wayland_display_update_cursors (GdkWaylandDisplay *display, - struct wl_cursor_theme *theme); +void _gdk_wayland_display_update_cursors (GdkWaylandDisplay *display); + +struct wl_cursor_theme * _gdk_wayland_display_get_scaled_cursor_theme (GdkWaylandDisplay *wayland_display, + guint scale); GdkCursor *_gdk_wayland_display_get_cursor_for_type (GdkDisplay *display, GdkCursorType cursor_type); +GdkCursor *_gdk_wayland_display_get_cursor_for_type_with_scale (GdkDisplay *display, + GdkCursorType cursor_type, + guint scale); GdkCursor *_gdk_wayland_display_get_cursor_for_name (GdkDisplay *display, const gchar *name); GdkCursor *_gdk_wayland_display_get_cursor_for_surface (GdkDisplay *display, @@ -87,6 +92,9 @@ guint _gdk_wayland_cursor_get_next_image_index (GdkCursor *cursor, guint current_image_index, guint *next_image_delay); +void _gdk_wayland_cursor_set_scale (GdkCursor *cursor, + guint scale); + GdkDragProtocol _gdk_wayland_window_get_drag_protocol (GdkWindow *window, GdkWindow **target);