wayland: Support for setting a cursor image

Closes #346.
This commit is contained in:
Ricardo Vieira 2014-09-09 16:37:55 +02:00 committed by Camilla Berglund
parent 8d170c7f47
commit 25204b1ec7
4 changed files with 248 additions and 23 deletions

View File

@ -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 ()

View File

@ -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)

View File

@ -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;

View File

@ -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");
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)