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:
Chris Cummins 2013-03-28 12:08:43 +00:00 committed by Kristian Høgsberg
parent 4801977d80
commit b8ed3e9ef5
3 changed files with 108 additions and 22 deletions

View File

@ -182,6 +182,7 @@ gdk_wayland_cursor_get_image (GdkCursor *cursor)
struct wl_buffer *
_gdk_wayland_cursor_get_buffer (GdkCursor *cursor,
guint image_index,
int *x,
int *y,
int *w,
@ -191,13 +192,25 @@ _gdk_wayland_cursor_get_buffer (GdkCursor *cursor,
if (wayland_cursor->wl_cursor)
{
*x = wayland_cursor->wl_cursor->images[0]->hotspot_x;
*y = wayland_cursor->wl_cursor->images[0]->hotspot_y;
struct wl_cursor_image *image;
*w = wayland_cursor->wl_cursor->images[0]->width;
*h = wayland_cursor->wl_cursor->images[0]->height;
if (image_index >= wayland_cursor->wl_cursor->image_count)
{
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 */
{
@ -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
_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))
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);
return GDK_CURSOR (private);

View File

@ -71,6 +71,9 @@ struct _GdkWaylandDeviceData
guint32 repeat_count;
GSettings *keyboard_settings;
guint cursor_timeout_id;
guint cursor_image_index;
DataOffer *drag_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
gdk_wayland_device_set_window_cursor (GdkDevice *device,
GdkWindow *window,
GdkCursor *cursor)
{
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 */
if (!cursor)
@ -164,19 +216,14 @@ gdk_wayland_device_set_window_cursor (GdkDevice *device,
if (cursor == wd->cursor)
return;
gdk_wayland_device_stop_window_cursor_animation (wd);
if (wd->cursor)
g_object_unref (wd->cursor);
wd->cursor = g_object_ref (cursor);
buffer = _gdk_wayland_cursor_get_buffer (wd->cursor, &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);
gdk_wayland_device_update_window_cursor (wd);
}
static void
@ -622,6 +669,7 @@ pointer_handle_leave (void *data,
g_object_unref(device->pointer_focus);
if (device->cursor)
{
gdk_wayland_device_stop_window_cursor_animation (device);
g_object_unref (device->cursor);
device->cursor = NULL;
}

View File

@ -78,10 +78,14 @@ gboolean _gdk_wayland_display_supports_cursor_alpha (GdkDisplay *display);
gboolean _gdk_wayland_display_supports_cursor_color (GdkDisplay *display);
struct wl_buffer *_gdk_wayland_cursor_get_buffer (GdkCursor *cursor,
int *x,
int *y,
guint image_index,
int *x,
int *y,
int *w,
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,
GdkWindow **target);