wayland: Implement animatable cursors
Cursor animations are handled on a per-device basis, with GdkWaylandDevice updating the pointer surface for each frame. https://bugzilla.gnome.org/show_bug.cgi?id=696429
This commit is contained in:
		
				
					committed by
					
						
						Kristian Høgsberg
					
				
			
			
				
	
			
			
			
						parent
						
							4801977d80
						
					
				
				
					commit
					b8ed3e9ef5
				
			@ -182,6 +182,7 @@ gdk_wayland_cursor_get_image (GdkCursor *cursor)
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
struct wl_buffer *
 | 
					struct wl_buffer *
 | 
				
			||||||
_gdk_wayland_cursor_get_buffer (GdkCursor *cursor,
 | 
					_gdk_wayland_cursor_get_buffer (GdkCursor *cursor,
 | 
				
			||||||
 | 
					                                guint      image_index,
 | 
				
			||||||
                                int       *x,
 | 
					                                int       *x,
 | 
				
			||||||
                                int       *y,
 | 
					                                int       *y,
 | 
				
			||||||
                                int       *w,
 | 
					                                int       *w,
 | 
				
			||||||
@ -191,13 +192,25 @@ _gdk_wayland_cursor_get_buffer (GdkCursor *cursor,
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  if (wayland_cursor->wl_cursor)
 | 
					  if (wayland_cursor->wl_cursor)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
      *x = wayland_cursor->wl_cursor->images[0]->hotspot_x;
 | 
					      struct wl_cursor_image *image;
 | 
				
			||||||
      *y = wayland_cursor->wl_cursor->images[0]->hotspot_y;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
      *w = wayland_cursor->wl_cursor->images[0]->width;
 | 
					      if (image_index >= wayland_cursor->wl_cursor->image_count)
 | 
				
			||||||
      *h = wayland_cursor->wl_cursor->images[0]->height;
 | 
					        {
 | 
				
			||||||
 | 
					          g_warning (G_STRLOC " out of bounds cursor image [%d / %d]",
 | 
				
			||||||
 | 
					                     image_index,
 | 
				
			||||||
 | 
					                     wayland_cursor->wl_cursor->image_count - 1);
 | 
				
			||||||
 | 
					          image_index = 0;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      return wl_cursor_image_get_buffer(wayland_cursor->wl_cursor->images[0]);
 | 
					      image = wayland_cursor->wl_cursor->images[image_index];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      *x = image->hotspot_x;
 | 
				
			||||||
 | 
					      *y = image->hotspot_y;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      *w = image->width;
 | 
				
			||||||
 | 
					      *h = image->height;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      return wl_cursor_image_get_buffer (image);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  else /* From pixbuf */
 | 
					  else /* From pixbuf */
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
@ -211,6 +224,32 @@ _gdk_wayland_cursor_get_buffer (GdkCursor *cursor,
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					guint
 | 
				
			||||||
 | 
					_gdk_wayland_cursor_get_next_image_index (GdkCursor *cursor,
 | 
				
			||||||
 | 
					                                          guint      current_image_index,
 | 
				
			||||||
 | 
					                                          guint     *next_image_delay)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  struct wl_cursor *wl_cursor = GDK_WAYLAND_CURSOR (cursor)->wl_cursor;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (wl_cursor && wl_cursor->image_count > 1)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      if (current_image_index >= wl_cursor->image_count)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          g_warning (G_STRLOC " out of bounds cursor image [%d / %d]",
 | 
				
			||||||
 | 
					                     current_image_index, wl_cursor->image_count - 1);
 | 
				
			||||||
 | 
					          current_image_index = 0;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      /* Return the time to next image */
 | 
				
			||||||
 | 
					      if (next_image_delay)
 | 
				
			||||||
 | 
					        *next_image_delay = wl_cursor->images[current_image_index]->delay;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      return (current_image_index + 1) % wl_cursor->image_count;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  else
 | 
				
			||||||
 | 
					    return current_image_index;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void
 | 
					static void
 | 
				
			||||||
_gdk_wayland_cursor_class_init (GdkWaylandCursorClass *wayland_cursor_class)
 | 
					_gdk_wayland_cursor_class_init (GdkWaylandCursorClass *wayland_cursor_class)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
@ -335,11 +374,6 @@ _gdk_wayland_display_get_cursor_for_name (GdkDisplay  *display,
 | 
				
			|||||||
  if (!set_cursor_from_theme (private, wayland_display->cursor_theme))
 | 
					  if (!set_cursor_from_theme (private, wayland_display->cursor_theme))
 | 
				
			||||||
    return GDK_CURSOR (private);
 | 
					    return GDK_CURSOR (private);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /* TODO: Do something clever so we can do animated cursors - move the
 | 
					 | 
				
			||||||
   * wl_pointer_set_cursor to a function here so that we can do the magic to
 | 
					 | 
				
			||||||
   * iterate through
 | 
					 | 
				
			||||||
   */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  add_to_cache (wayland_display, private);
 | 
					  add_to_cache (wayland_display, private);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return GDK_CURSOR (private);
 | 
					  return GDK_CURSOR (private);
 | 
				
			||||||
 | 
				
			|||||||
@ -71,6 +71,9 @@ struct _GdkWaylandDeviceData
 | 
				
			|||||||
  guint32 repeat_count;
 | 
					  guint32 repeat_count;
 | 
				
			||||||
  GSettings *keyboard_settings;
 | 
					  GSettings *keyboard_settings;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  guint cursor_timeout_id;
 | 
				
			||||||
 | 
					  guint cursor_image_index;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  DataOffer *drag_offer;
 | 
					  DataOffer *drag_offer;
 | 
				
			||||||
  DataOffer *selection_offer;
 | 
					  DataOffer *selection_offer;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -144,14 +147,63 @@ gdk_wayland_device_get_state (GdkDevice       *device,
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
 | 
					gdk_wayland_device_stop_window_cursor_animation (GdkWaylandDeviceData *wd)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  if (wd->cursor_timeout_id > 0)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      g_source_remove (wd->cursor_timeout_id);
 | 
				
			||||||
 | 
					      wd->cursor_timeout_id = 0;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  wd->cursor_image_index = 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static gboolean
 | 
				
			||||||
 | 
					gdk_wayland_device_update_window_cursor (GdkWaylandDeviceData *wd)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  struct wl_buffer *buffer;
 | 
				
			||||||
 | 
					  int x, y, w, h;
 | 
				
			||||||
 | 
					  guint next_image_index, next_image_delay;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  buffer = _gdk_wayland_cursor_get_buffer (wd->cursor, wd->cursor_image_index,
 | 
				
			||||||
 | 
					                                           &x, &y, &w, &h);
 | 
				
			||||||
 | 
					  wl_pointer_set_cursor (wd->wl_pointer,
 | 
				
			||||||
 | 
					                         wd->enter_serial,
 | 
				
			||||||
 | 
					                         wd->pointer_surface,
 | 
				
			||||||
 | 
					                         x, y);
 | 
				
			||||||
 | 
					  wl_surface_attach (wd->pointer_surface, buffer, 0, 0);
 | 
				
			||||||
 | 
					  wl_surface_damage (wd->pointer_surface,  0, 0, w, h);
 | 
				
			||||||
 | 
					  wl_surface_commit (wd->pointer_surface);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  next_image_index =
 | 
				
			||||||
 | 
					    _gdk_wayland_cursor_get_next_image_index (wd->cursor,
 | 
				
			||||||
 | 
					                                              wd->cursor_image_index,
 | 
				
			||||||
 | 
					                                              &next_image_delay);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (next_image_index != wd->cursor_image_index)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      guint id;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      /* Queue timeout for next frame */
 | 
				
			||||||
 | 
					      id = g_timeout_add (next_image_delay,
 | 
				
			||||||
 | 
					                          (GSourceFunc)gdk_wayland_device_update_window_cursor,
 | 
				
			||||||
 | 
					                          wd);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      wd->cursor_timeout_id = id;
 | 
				
			||||||
 | 
					      wd->cursor_image_index = next_image_index;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  else
 | 
				
			||||||
 | 
					    wd->cursor_timeout_id = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return FALSE;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void
 | 
					static void
 | 
				
			||||||
gdk_wayland_device_set_window_cursor (GdkDevice *device,
 | 
					gdk_wayland_device_set_window_cursor (GdkDevice *device,
 | 
				
			||||||
                                      GdkWindow *window,
 | 
					                                      GdkWindow *window,
 | 
				
			||||||
                                      GdkCursor *cursor)
 | 
					                                      GdkCursor *cursor)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
  GdkWaylandDeviceData *wd = GDK_WAYLAND_DEVICE(device)->device;
 | 
					  GdkWaylandDeviceData *wd = GDK_WAYLAND_DEVICE(device)->device;
 | 
				
			||||||
  struct wl_buffer *buffer;
 | 
					 | 
				
			||||||
  int x, y, w, h;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /* Setting the cursor to NULL means that we should use the default cursor */
 | 
					  /* Setting the cursor to NULL means that we should use the default cursor */
 | 
				
			||||||
  if (!cursor)
 | 
					  if (!cursor)
 | 
				
			||||||
@ -164,19 +216,14 @@ gdk_wayland_device_set_window_cursor (GdkDevice *device,
 | 
				
			|||||||
  if (cursor == wd->cursor)
 | 
					  if (cursor == wd->cursor)
 | 
				
			||||||
    return;
 | 
					    return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  gdk_wayland_device_stop_window_cursor_animation (wd);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (wd->cursor)
 | 
					  if (wd->cursor)
 | 
				
			||||||
    g_object_unref (wd->cursor);
 | 
					    g_object_unref (wd->cursor);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  wd->cursor = g_object_ref (cursor);
 | 
					  wd->cursor = g_object_ref (cursor);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  buffer = _gdk_wayland_cursor_get_buffer (wd->cursor, &x, &y, &w, &h);
 | 
					  gdk_wayland_device_update_window_cursor (wd);
 | 
				
			||||||
  wl_pointer_set_cursor (wd->wl_pointer,
 | 
					 | 
				
			||||||
                         wd->enter_serial,
 | 
					 | 
				
			||||||
                         wd->pointer_surface,
 | 
					 | 
				
			||||||
                         x, y);
 | 
					 | 
				
			||||||
  wl_surface_attach (wd->pointer_surface, buffer, 0, 0);
 | 
					 | 
				
			||||||
  wl_surface_damage (wd->pointer_surface,  0, 0, w, h);
 | 
					 | 
				
			||||||
  wl_surface_commit (wd->pointer_surface);
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void
 | 
					static void
 | 
				
			||||||
@ -622,6 +669,7 @@ pointer_handle_leave (void              *data,
 | 
				
			|||||||
  g_object_unref(device->pointer_focus);
 | 
					  g_object_unref(device->pointer_focus);
 | 
				
			||||||
  if (device->cursor)
 | 
					  if (device->cursor)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
 | 
					      gdk_wayland_device_stop_window_cursor_animation (device);
 | 
				
			||||||
      g_object_unref (device->cursor);
 | 
					      g_object_unref (device->cursor);
 | 
				
			||||||
      device->cursor = NULL;
 | 
					      device->cursor = NULL;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
				
			|||||||
@ -78,10 +78,14 @@ gboolean   _gdk_wayland_display_supports_cursor_alpha (GdkDisplay *display);
 | 
				
			|||||||
gboolean   _gdk_wayland_display_supports_cursor_color (GdkDisplay *display);
 | 
					gboolean   _gdk_wayland_display_supports_cursor_color (GdkDisplay *display);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct wl_buffer *_gdk_wayland_cursor_get_buffer (GdkCursor *cursor,
 | 
					struct wl_buffer *_gdk_wayland_cursor_get_buffer (GdkCursor *cursor,
 | 
				
			||||||
						  int       *x,
 | 
					                                                  guint      image_index,
 | 
				
			||||||
						  int       *y,
 | 
					                                                  int       *x,
 | 
				
			||||||
 | 
					                                                  int       *y,
 | 
				
			||||||
                                                  int       *w,
 | 
					                                                  int       *w,
 | 
				
			||||||
                                                  int       *h);
 | 
					                                                  int       *h);
 | 
				
			||||||
 | 
					guint      _gdk_wayland_cursor_get_next_image_index (GdkCursor *cursor,
 | 
				
			||||||
 | 
					                                                     guint      current_image_index,
 | 
				
			||||||
 | 
					                                                     guint     *next_image_delay);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
GdkDragProtocol _gdk_wayland_window_get_drag_protocol (GdkWindow *window,
 | 
					GdkDragProtocol _gdk_wayland_window_get_drag_protocol (GdkWindow *window,
 | 
				
			||||||
						       GdkWindow **target);
 | 
											       GdkWindow **target);
 | 
				
			||||||
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user