wayland: Support scaling of theme based cursors
authorJonas Ådahl <jadahl@gmail.com>
Fri, 13 Mar 2015 07:40:18 +0000 (15:40 +0800)
committerJonas Ådahl <jadahl@gmail.com>
Mon, 16 Mar 2015 11:53:37 +0000 (19:53 +0800)
Support scaling of cursors created from themes. The default scale is
always 1, but if the pointer cursor surface enters an output with a
higher scale, load the larger version of the cursor theme and use the
image from that theme.

This assumes the theme size is set to one that fits with an output scale
= 1.

https://bugzilla.gnome.org/show_bug.cgi?id=746141

gdk/wayland/gdkcursor-wayland.c
gdk/wayland/gdkdevice-wayland.c
gdk/wayland/gdkdisplay-wayland.c
gdk/wayland/gdkdisplay-wayland.h
gdk/wayland/gdkprivate-wayland.h

index 8fa1c0407c0d532e4ada458a5a9d486b360803a6..665c224d176efbb99fe299a20c8b929931eb81a5 100644 (file)
@@ -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,
index 4837a129d5523628d256f9c26f02f5602e69c90b..a22351b4d7b2d8091b01e06a3124e9c2fd312c70 100644 (file)
@@ -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);
 }
index 21369de2c1b00f48669be8fbb901d8cf4dd2dad9..5caf0e168405b7f0be292b3c73fb770215a50ccb 100644 (file)
@@ -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;
+
+  _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;
+    }
 
-  if (wayland_display->cursor_theme != NULL)
-    wl_cursor_theme_destroy (wayland_display->cursor_theme);
-  wayland_display->cursor_theme = theme;
+  return theme;
 }
 
 static void
index 48de6f90b4dd635166f7d60d9879ba884794c0a1..781be518ef0b75dd59cf0a80e4d2bc047838a7b6 100644 (file)
@@ -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;
index dabd22d6ae9787ed3d68e13c5fb2d2aae254d92b..0c634ae2a9acc833812527c219b0cfb8bbb1e98f 100644 (file)
@@ -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);