소스 검색

wayland: Support for setting a cursor image

Closes #346.
Ricardo Vieira 11 년 전
부모
커밋
25204b1ec7
4개의 변경된 파일248개의 추가작업 그리고 23개의 파일을 삭제
  1. 7 3
      CMake/modules/FindWayland.cmake
  2. 27 0
      src/wl_init.c
  3. 10 3
      src/wl_platform.h
  4. 204 17
      src/wl_window.c

+ 7 - 3
CMake/modules/FindWayland.cmake

@@ -27,21 +27,23 @@ IF (NOT WIN32)
   # Use pkg-config to get the directories and then use these values
   # in the FIND_PATH() and FIND_LIBRARY() calls
   FIND_PACKAGE(PkgConfig)
-  PKG_CHECK_MODULES(PKG_WAYLAND QUIET wayland-client wayland-server wayland-egl)
+  PKG_CHECK_MODULES(PKG_WAYLAND QUIET wayland-client wayland-server wayland-egl wayland-cursor)
 
   SET(WAYLAND_DEFINITIONS ${PKG_WAYLAND_CFLAGS})
 
   FIND_PATH(WAYLAND_CLIENT_INCLUDE_DIR  NAMES wayland-client.h HINTS ${PKG_WAYLAND_INCLUDE_DIRS})
   FIND_PATH(WAYLAND_SERVER_INCLUDE_DIR  NAMES wayland-server.h HINTS ${PKG_WAYLAND_INCLUDE_DIRS})
   FIND_PATH(WAYLAND_EGL_INCLUDE_DIR     NAMES wayland-egl.h    HINTS ${PKG_WAYLAND_INCLUDE_DIRS})
+  FIND_PATH(WAYLAND_CURSOR_INCLUDE_DIR  NAMES wayland-cursor.h HINTS ${PKG_WAYLAND_INCLUDE_DIRS})
 
   FIND_LIBRARY(WAYLAND_CLIENT_LIBRARIES NAMES wayland-client   HINTS ${PKG_WAYLAND_LIBRARY_DIRS})
   FIND_LIBRARY(WAYLAND_SERVER_LIBRARIES NAMES wayland-server   HINTS ${PKG_WAYLAND_LIBRARY_DIRS})
   FIND_LIBRARY(WAYLAND_EGL_LIBRARIES    NAMES wayland-egl      HINTS ${PKG_WAYLAND_LIBRARY_DIRS})
+  FIND_LIBRARY(WAYLAND_CURSOR_LIBRARIES NAMES wayland-cursor   HINTS ${PKG_WAYLAND_LIBRARY_DIRS})
 
-  set(WAYLAND_INCLUDE_DIR ${WAYLAND_CLIENT_INCLUDE_DIR} ${WAYLAND_SERVER_INCLUDE_DIR} ${WAYLAND_EGL_INCLUDE_DIR})
+  set(WAYLAND_INCLUDE_DIR ${WAYLAND_CLIENT_INCLUDE_DIR} ${WAYLAND_SERVER_INCLUDE_DIR} ${WAYLAND_EGL_INCLUDE_DIR} ${WAYLAND_CURSOR_INCLUDE_DIR})
 
-  set(WAYLAND_LIBRARIES ${WAYLAND_CLIENT_LIBRARIES} ${WAYLAND_SERVER_LIBRARIES} ${WAYLAND_EGL_LIBRARIES})
+  set(WAYLAND_LIBRARIES ${WAYLAND_CLIENT_LIBRARIES} ${WAYLAND_SERVER_LIBRARIES} ${WAYLAND_EGL_LIBRARIES} ${WAYLAND_CURSOR_LIBRARIES})
 
   list(REMOVE_DUPLICATES WAYLAND_INCLUDE_DIR)
 
@@ -50,6 +52,7 @@ IF (NOT WIN32)
   FIND_PACKAGE_HANDLE_STANDARD_ARGS(WAYLAND_CLIENT  DEFAULT_MSG  WAYLAND_CLIENT_LIBRARIES  WAYLAND_CLIENT_INCLUDE_DIR)
   FIND_PACKAGE_HANDLE_STANDARD_ARGS(WAYLAND_SERVER  DEFAULT_MSG  WAYLAND_SERVER_LIBRARIES  WAYLAND_SERVER_INCLUDE_DIR)
   FIND_PACKAGE_HANDLE_STANDARD_ARGS(WAYLAND_EGL     DEFAULT_MSG  WAYLAND_EGL_LIBRARIES     WAYLAND_EGL_INCLUDE_DIR)
+  FIND_PACKAGE_HANDLE_STANDARD_ARGS(WAYLAND_EGL     DEFAULT_MSG  WAYLAND_CURSOR_LIBRARIES  WAYLAND_CURSOR_INCLUDE_DIR)
   FIND_PACKAGE_HANDLE_STANDARD_ARGS(WAYLAND         DEFAULT_MSG  WAYLAND_LIBRARIES         WAYLAND_INCLUDE_DIR)
 
   MARK_AS_ADVANCED(
@@ -57,6 +60,7 @@ IF (NOT WIN32)
         WAYLAND_CLIENT_INCLUDE_DIR  WAYLAND_CLIENT_LIBRARIES
         WAYLAND_SERVER_INCLUDE_DIR  WAYLAND_SERVER_LIBRARIES
         WAYLAND_EGL_INCLUDE_DIR     WAYLAND_EGL_LIBRARIES
+        WAYLAND_CURSOR_INCLUDE_DIR  WAYLAND_CURSOR_LIBRARIES
   )
 
 ENDIF ()

+ 27 - 0
src/wl_init.c

@@ -46,7 +46,10 @@ static void pointerHandleEnter(void* data,
 {
     _GLFWwindow* window = wl_surface_get_user_data(surface);
 
+    _glfw.wl.pointerSerial = serial;
     _glfw.wl.pointerFocus = window;
+
+    _glfwPlatformSetCursor(window, window->wl.currentCursor);
     _glfwInputCursorEnter(window, GL_TRUE);
 }
 
@@ -60,6 +63,7 @@ static void pointerHandleLeave(void* data,
     if (!window)
         return;
 
+    _glfw.wl.pointerSerial = serial;
     _glfw.wl.pointerFocus = NULL;
     _glfwInputCursorEnter(window, GL_FALSE);
 }
@@ -491,6 +495,11 @@ static void registryHandleGlobal(void* data,
         _glfw.wl.compositor =
             wl_registry_bind(registry, name, &wl_compositor_interface, 1);
     }
+    else if (strcmp(interface, "wl_shm") == 0)
+    {
+        _glfw.wl.shm =
+            wl_registry_bind(registry, name, &wl_shm_interface, 1);
+    }
     else if (strcmp(interface, "wl_shell") == 0)
     {
         _glfw.wl.shell =
@@ -564,6 +573,20 @@ int _glfwPlatformInit(void)
     _glfwInitTimer();
     _glfwInitJoysticks();
 
+    if (_glfw.wl.pointer && _glfw.wl.shm){
+        _glfw.wl.cursorTheme = wl_cursor_theme_load(NULL, 24, _glfw.wl.shm);
+        if (!_glfw.wl.cursorTheme) {
+            _glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: Unable to load default cursor theme\n");
+            return GL_FALSE;
+        }
+        _glfw.wl.defaultCursor = wl_cursor_theme_get_cursor(_glfw.wl.cursorTheme, "left_ptr");
+        if (!_glfw.wl.defaultCursor) {
+            _glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: Unable to load default left pointer\n");
+            return GL_FALSE;
+        }
+        _glfw.wl.cursorSurface = wl_compositor_create_surface(_glfw.wl.compositor);
+    }
+
     return GL_TRUE;
 }
 
@@ -571,6 +594,10 @@ void _glfwPlatformTerminate(void)
 {
     _glfwTerminateContextAPI();
 
+    if (_glfw.wl.cursorTheme)
+        wl_cursor_theme_destroy(_glfw.wl.cursorTheme);
+    if (_glfw.wl.cursorSurface)
+        wl_surface_destroy(_glfw.wl.cursorSurface);
     if (_glfw.wl.registry)
         wl_registry_destroy(_glfw.wl.registry);
     if (_glfw.wl.display)

+ 10 - 3
src/wl_platform.h

@@ -66,7 +66,7 @@ typedef struct _GLFWwindowWayland
     struct wl_shell_surface*    shell_surface;
     EGLSurface                  egl_surface;
     struct wl_callback*         callback;
-
+    _GLFWcursor*                currentCursor;
 } _GLFWwindowWayland;
 
 
@@ -78,10 +78,16 @@ typedef struct _GLFWlibraryWayland
     struct wl_registry*         registry;
     struct wl_compositor*       compositor;
     struct wl_shell*            shell;
+    struct wl_shm*              shm;
     struct wl_seat*             seat;
     struct wl_pointer*          pointer;
     struct wl_keyboard*         keyboard;
 
+    struct wl_cursor_theme*     cursorTheme;
+    struct wl_cursor*           defaultCursor;
+    struct wl_surface*          cursorSurface;
+    uint32_t                    pointerSerial;
+
     _GLFWmonitor**              monitors;
     int                         monitorsCount;
     int                         monitorsSize;
@@ -124,8 +130,9 @@ typedef struct _GLFWmonitorWayland
 //
 typedef struct _GLFWcursorWayland
 {
-    int                         dummy;
-
+    struct wl_buffer*           buffer;
+    int                         width, height;
+    int                         xhot, yhot;
 } _GLFWcursorWayland;
 
 

+ 204 - 17
src/wl_window.c

@@ -27,9 +27,16 @@
 #include "internal.h"
 
 #include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/mman.h>
 #include <poll.h>
 #include <GL/gl.h>
 #include <wayland-egl.h>
+#include <wayland-cursor.h>
 
 
 static void handlePing(void* data,
@@ -93,6 +100,105 @@ static GLboolean createSurface(_GLFWwindow* window,
     return GL_TRUE;
 }
 
+static int
+set_cloexec_or_close(int fd)
+{
+    long flags;
+
+    if (fd == -1)
+        return -1;
+
+    flags = fcntl(fd, F_GETFD);
+    if (flags == -1)
+        goto err;
+
+    if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == -1)
+        goto err;
+
+    return fd;
+
+err:
+    close(fd);
+    return -1;
+}
+
+static int
+create_tmpfile_cloexec(char *tmpname)
+{
+    int fd;
+
+#ifdef HAVE_MKOSTEMP
+    fd = mkostemp(tmpname, O_CLOEXEC);
+    if (fd >= 0)
+        unlink(tmpname);
+#else
+    fd = mkstemp(tmpname);
+    if (fd >= 0) {
+        fd = set_cloexec_or_close(fd);
+        unlink(tmpname);
+    }
+#endif
+
+    return fd;
+}
+
+/*
+ * Create a new, unique, anonymous file of the given size, and
+ * return the file descriptor for it. The file descriptor is set
+ * CLOEXEC. The file is immediately suitable for mmap()'ing
+ * the given size at offset zero.
+ *
+ * The file should not have a permanent backing store like a disk,
+ * but may have if XDG_RUNTIME_DIR is not properly implemented in OS.
+ *
+ * The file name is deleted from the file system.
+ *
+ * The file is suitable for buffer sharing between processes by
+ * transmitting the file descriptor over Unix sockets using the
+ * SCM_RIGHTS methods.
+ *
+ * If the C library implements posix_fallocate(), it is used to
+ * guarantee that disk space is available for the file at the
+ * given size. If disk space is insufficent, errno is set to ENOSPC.
+ * If posix_fallocate() is not supported, program may receive
+ * SIGBUS on accessing mmap()'ed file contents instead.
+ */
+int
+os_create_anonymous_file(off_t size)
+{
+    static const char template[] = "/glfw-shared-XXXXXX";
+    const char *path;
+    char *name;
+    int fd;
+    int ret;
+
+    path = getenv("XDG_RUNTIME_DIR");
+    if (!path) {
+        errno = ENOENT;
+        return -1;
+    }
+
+    name = malloc(strlen(path) + sizeof(template));
+    if (!name)
+        return -1;
+
+    strcpy(name, path);
+    strcat(name, template);
+
+    fd = create_tmpfile_cloexec(name);
+
+    free(name);
+
+    if (fd < 0)
+        return -1;
+    ret = posix_fallocate(fd, 0, size);
+    if (ret != 0) {
+        close(fd);
+        errno = ret;
+        return -1;
+    }
+    return fd;
+}
 
 //////////////////////////////////////////////////////////////////////////
 //////                       GLFW platform API                      //////
@@ -122,6 +228,8 @@ int _glfwPlatformCreateWindow(_GLFWwindow* window,
         wl_shell_surface_set_toplevel(window->wl.shell_surface);
     }
 
+    window->wl.currentCursor = NULL;
+
     return GL_TRUE;
 }
 
@@ -279,37 +387,116 @@ void _glfwPlatformSetCursorPos(_GLFWwindow* window, double x, double y)
 
 void _glfwPlatformApplyCursorMode(_GLFWwindow* window)
 {
-    fprintf(stderr, "_glfwPlatformApplyCursorMode not implemented yet\n");
-    switch (window->cursorMode)
-    {
-        case GLFW_CURSOR_NORMAL:
-            // TODO: enable showing cursor
-            break;
-        case GLFW_CURSOR_HIDDEN:
-            // TODO: enable not showing cursor
-            break;
-        case GLFW_CURSOR_DISABLED:
-            // TODO: enable pointer lock and hide cursor
-            break;
-    }
+    _glfwPlatformSetCursor(window, window->wl.currentCursor);
 }
 
 int _glfwPlatformCreateCursor(_GLFWcursor* cursor,
                               const GLFWimage* image,
                               int xhot, int yhot)
 {
-    fprintf(stderr, "_glfwPlatformCreateCursor not implemented yet\n");
-    return GL_FALSE;
+    struct wl_shm_pool *pool;
+    int stride = image->width * 4;
+    int length = image->width * image->height * 4;
+    void *data;
+    int fd, i;
+
+    fd = os_create_anonymous_file(length);
+    if (fd < 0) {
+        _glfwInputError(GLFW_PLATFORM_ERROR,
+                    "Wayland: Creating a buffer file for %d B failed: %m\n",
+                    length);
+        return GL_FALSE;
+    }
+
+    data = mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+    if (data == MAP_FAILED) {
+        _glfwInputError(GLFW_PLATFORM_ERROR,
+                    "Wayland: Cursor mmap failed: %m\n");
+        close(fd);
+        return GL_FALSE;
+    }
+
+    pool = wl_shm_create_pool(_glfw.wl.shm, fd, length);
+
+    close(fd);
+    unsigned char* source = (unsigned char*) image->pixels;
+    unsigned char* target = data;
+    for (i = 0;  i < image->width * image->height;  i++, source += 4)
+    {
+        *target++ = source[2];
+        *target++ = source[1];
+        *target++ = source[0];
+        *target++ = source[3];
+    }
+
+    cursor->wl.buffer = wl_shm_pool_create_buffer(pool, 0,
+                    image->width,
+                    image->height,
+                    stride, WL_SHM_FORMAT_ARGB8888);
+    munmap(data, length);
+    wl_shm_pool_destroy(pool);
+
+    cursor->wl.width = image->width;
+    cursor->wl.height = image->height;
+    cursor->wl.xhot = xhot;
+    cursor->wl.yhot = yhot;
+    return GL_TRUE;
 }
 
 void _glfwPlatformDestroyCursor(_GLFWcursor* cursor)
 {
-    fprintf(stderr, "_glfwPlatformDestroyCursor not implemented yet\n");
+    wl_buffer_destroy(cursor->wl.buffer);
 }
 
 void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor)
 {
-    fprintf(stderr, "_glfwPlatformSetCursor not implemented yet\n");
+    struct wl_buffer *buffer;
+    struct wl_cursor_image *image;
+    struct wl_surface *surface = _glfw.wl.cursorSurface;
+
+    if (!_glfw.wl.pointer)
+        return;
+
+    window->wl.currentCursor = 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)
+        return;
+
+    if (window->cursorMode == GLFW_CURSOR_NORMAL)
+    {
+        if (cursor == NULL)
+        {
+            image = _glfw.wl.defaultCursor->images[0];
+            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);
+        }
+    }
+    else /* Cursor is hidden set cursor surface to NULL */
+    {
+        wl_pointer_set_cursor(_glfw.wl.pointer, _glfw.wl.pointerSerial, NULL, 0, 0);
+    }
 }
 
 void _glfwPlatformSetClipboardString(_GLFWwindow* window, const char* string)