From 0195e7f8410dc22efb92103895dc3a5a90ac9f87 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 10 Sep 2018 11:54:22 +0530 Subject: [PATCH] Wayland: Add support for animated cursors --- glfw/wl_init.c | 4 +- glfw/wl_platform.h | 5 ++- glfw/wl_window.c | 109 +++++++++++++++++++++++++++++++-------------- 3 files changed, 82 insertions(+), 36 deletions(-) diff --git a/glfw/wl_init.c b/glfw/wl_init.c index b244e0f56..766bb5cb7 100644 --- a/glfw/wl_init.c +++ b/glfw/wl_init.c @@ -139,6 +139,7 @@ static void setCursor(const char* name) "Wayland: Standard cursor not found"); return; } + // TODO: handle animated cursors too. image = cursor->images[0]; if (!image) @@ -688,7 +689,8 @@ int _glfwPlatformInit(void) } initPollData(&_glfw.wl.eventLoopData, _glfw.wl.eventLoopData.wakeupFds[0], wl_display_get_fd(_glfw.wl.display)); glfw_dbus_init(&_glfw.wl.dbus, &_glfw.wl.eventLoopData); - _glfw.wl.keyRepeatInfo.keyRepeatTimer = addTimer(&_glfw.wl.eventLoopData, "wayland-keyrepeat", 0.5, 0, dispatchPendingKeyRepeats, NULL); + _glfw.wl.keyRepeatInfo.keyRepeatTimer = addTimer(&_glfw.wl.eventLoopData, "wayland-key-repeat", 0.5, 0, dispatchPendingKeyRepeats, NULL); + _glfw.wl.cursorAnimationTimer = addTimer(&_glfw.wl.eventLoopData, "wayland-cursor-animation", 0.5, 0, animateCursorImage, NULL); _glfw.wl.registry = wl_display_get_registry(_glfw.wl.display); wl_registry_add_listener(_glfw.wl.registry, ®istryListener, NULL); diff --git a/glfw/wl_platform.h b/glfw/wl_platform.h index c0e0b5291..347290f15 100644 --- a/glfw/wl_platform.h +++ b/glfw/wl_platform.h @@ -225,6 +225,7 @@ typedef struct _GLFWlibraryWayland id_type keyRepeatTimer; _GLFWwindow* keyboardFocus; } keyRepeatInfo; + id_type cursorAnimationTimer; _GLFWXKBData xkb; _GLFWDBUSData dbus; @@ -274,12 +275,14 @@ typedef struct _GLFWmonitorWayland // typedef struct _GLFWcursorWayland { - struct wl_cursor_image* image; + struct wl_cursor* cursor; struct wl_buffer* buffer; int width, height; int xhot, yhot; + int currentImage; } _GLFWcursorWayland; void _glfwAddOutputWayland(uint32_t name, uint32_t version); void _glfwSetupWaylandDataDevice(); +void animateCursorImage(id_type timer_id, void *data); diff --git a/glfw/wl_window.c b/glfw/wl_window.c index 8a2d934a3..9a44b9f41 100644 --- a/glfw/wl_window.c +++ b/glfw/wl_window.c @@ -702,6 +702,68 @@ static GLFWbool createXdgSurface(_GLFWwindow* window) return GLFW_TRUE; } +static void +setCursorImage(_GLFWcursorWayland* cursorWayland) +{ + struct wl_cursor_image* image; + struct wl_buffer* buffer; + struct wl_surface* surface = _glfw.wl.cursorSurface; + + if (!cursorWayland->cursor) { + buffer = cursorWayland->buffer; + toggleTimer(&_glfw.wl.eventLoopData, _glfw.wl.cursorAnimationTimer, 0); + } else + { + image = cursorWayland->cursor->images[cursorWayland->currentImage]; + buffer = wl_cursor_image_get_buffer(image); + if (image->delay) { + changeTimerInterval(&_glfw.wl.eventLoopData, _glfw.wl.cursorAnimationTimer, ((double)image->delay) / 1000.0); + toggleTimer(&_glfw.wl.eventLoopData, _glfw.wl.cursorAnimationTimer, 1); + } else { + toggleTimer(&_glfw.wl.eventLoopData, _glfw.wl.cursorAnimationTimer, 0); + } + + if (!buffer) + return; + + cursorWayland->width = image->width; + cursorWayland->height = image->height; + cursorWayland->xhot = image->hotspot_x; + cursorWayland->yhot = image->hotspot_y; + } + + wl_pointer_set_cursor(_glfw.wl.pointer, _glfw.wl.pointerSerial, + surface, + cursorWayland->xhot, + cursorWayland->yhot); + wl_surface_attach(surface, buffer, 0, 0); + wl_surface_damage(surface, 0, 0, + cursorWayland->width, cursorWayland->height); + wl_surface_commit(surface); +} + +static void +incrementCursorImage(_GLFWwindow* window) +{ + if (window && window->wl.decorations.focus == mainWindow) { + _GLFWcursor* cursor = window->wl.currentCursor; + if (cursor && cursor->wl.cursor) + { + cursor->wl.currentImage += 1; + cursor->wl.currentImage %= cursor->wl.cursor->image_count; + setCursorImage(&cursor->wl); + return; + } + } + toggleTimer(&_glfw.wl.eventLoopData, _glfw.wl.cursorAnimationTimer, 1); +} + +void +animateCursorImage(id_type timer_id, void *data) { + incrementCursorImage(_glfw.wl.pointerFocus); +} + + static void handleEvents(double timeout) { @@ -1263,14 +1325,15 @@ int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, int shape) return GLFW_FALSE; } - cursor->wl.image = standardCursor->images[0]; + cursor->wl.cursor = standardCursor; + cursor->wl.currentImage = 0; return GLFW_TRUE; } void _glfwPlatformDestroyCursor(_GLFWcursor* cursor) { // If it's a standard cursor we don't need to do anything here - if (cursor->wl.image) + if (cursor->wl.cursor) return; if (cursor->wl.buffer) @@ -1376,10 +1439,7 @@ static GLFWbool isPointerLocked(_GLFWwindow* window) void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor) { - struct wl_buffer* buffer; struct wl_cursor* defaultCursor; - struct wl_cursor_image* image; - struct wl_surface* surface = _glfw.wl.cursorSurface; if (!_glfw.wl.pointer) return; @@ -1388,7 +1448,7 @@ void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor) // If we're not in the correct window just save the cursor // the next time the pointer enters the window the cursor will change - if (window != _glfw.wl.pointerFocus) + if (window != _glfw.wl.pointerFocus || window->wl.decorations.focus != mainWindow) return; // Unlock possible pointer lock if no longer disabled. @@ -1398,7 +1458,7 @@ void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor) if (window->cursorMode == GLFW_CURSOR_NORMAL) { if (cursor) - image = cursor->wl.image; + setCursorImage(&cursor->wl); else { defaultCursor = wl_cursor_theme_get_cursor(_glfw.wl.cursorTheme, @@ -1409,33 +1469,14 @@ void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor) "Wayland: Standard cursor not found"); return; } - image = defaultCursor->images[0]; - } - - if (image) - { - buffer = wl_cursor_image_get_buffer(image); - if (!buffer) - return; - wl_pointer_set_cursor(_glfw.wl.pointer, _glfw.wl.pointerSerial, - surface, - image->hotspot_x, - image->hotspot_y); - wl_surface_attach(surface, buffer, 0, 0); - wl_surface_damage(surface, 0, 0, - image->width, image->height); - wl_surface_commit(surface); - } - else - { - wl_pointer_set_cursor(_glfw.wl.pointer, _glfw.wl.pointerSerial, - surface, - cursor->wl.xhot, - cursor->wl.yhot); - wl_surface_attach(surface, cursor->wl.buffer, 0, 0); - wl_surface_damage(surface, 0, 0, - cursor->wl.width, cursor->wl.height); - wl_surface_commit(surface); + _GLFWcursorWayland cursorWayland = { + defaultCursor, + NULL, + 0, 0, + 0, 0, + 0 + }; + setCursorImage(&cursorWayland); } } else if (window->cursorMode == GLFW_CURSOR_DISABLED)