Commit 40c04a75 authored by urraka's avatar urraka Committed by Camilla Berglund
Browse files

Added support for custom system cursors.

This adds 3 functions to the GLFW API: glfwCreateCursor,
glfwDestroyCursor and glfwSetCursor.
parent 30f86286
......@@ -121,7 +121,7 @@ endif()
#--------------------------------------------------------------------
if (WIN32)
set(_GLFW_WIN32 1)
message(STATUS "Using Win32 for window creation")
message(STATUS "Using Win32 for window creation")
if (GLFW_USE_EGL)
set(_GLFW_EGL 1)
......@@ -137,7 +137,7 @@ elseif (APPLE)
message(STATUS "Using NSGL for context creation")
elseif (UNIX)
set(_GLFW_X11 1)
message(STATUS "Using X11 for window creation")
message(STATUS "Using X11 for window creation")
if (GLFW_USE_EGL)
set(_GLFW_EGL 1)
......@@ -250,7 +250,7 @@ if (_GLFW_X11)
# Check for Xkb (X keyboard extension)
if (NOT X11_Xkb_FOUND)
message(FATAL_ERROR "The X keyboard extension headers were not found")
endif()
endif()
list(APPEND glfw_INCLUDE_DIR ${X11_Xkb_INCLUDE_PATH})
......@@ -268,6 +268,15 @@ if (_GLFW_X11)
set(GLFW_PKG_LIBS "${GLFW_PKG_LIBS} -lm")
endif()
# Check for Xcursor
if (NOT X11_Xcursor_FOUND)
message(FATAL_ERROR "The Xcursor libraries and headers were not found")
endif()
list(APPEND glfw_INCLUDE_DIR ${X11_Xcursor_INCLUDE_PATH})
list(APPEND glfw_LIBRARIES ${X11_Xcursor_LIB})
set(GLFW_PKG_DEPS "${GLFW_PKG_DEPS} xcursor")
endif()
#--------------------------------------------------------------------
......@@ -338,7 +347,7 @@ endif()
# Use Cocoa for window creation and NSOpenGL for context creation
#--------------------------------------------------------------------
if (_GLFW_COCOA AND _GLFW_NSGL)
if (GLFW_USE_MENUBAR)
set(_GLFW_USE_MENUBAR 1)
endif()
......@@ -357,7 +366,7 @@ if (_GLFW_COCOA AND _GLFW_NSGL)
else()
message(STATUS "Building GLFW only for the native architecture")
endif()
# Set up library and include paths
find_library(COCOA_FRAMEWORK Cocoa)
find_library(IOKIT_FRAMEWORK IOKit)
......@@ -394,7 +403,7 @@ endif()
configure_file(${GLFW_SOURCE_DIR}/docs/Doxyfile.in
${GLFW_BINARY_DIR}/docs/Doxyfile @ONLY)
configure_file(${GLFW_SOURCE_DIR}/src/glfw_config.h.in
configure_file(${GLFW_SOURCE_DIR}/src/glfw_config.h.in
${GLFW_BINARY_DIR}/src/glfw_config.h @ONLY)
configure_file(${GLFW_SOURCE_DIR}/src/glfwConfig.cmake.in
......@@ -428,7 +437,7 @@ endif()
# The library is installed by src/CMakeLists.txt
#--------------------------------------------------------------------
if (GLFW_INSTALL)
install(DIRECTORY include/GLFW DESTINATION include
install(DIRECTORY include/GLFW DESTINATION include
FILES_MATCHING PATTERN glfw3.h PATTERN glfw3native.h)
install(FILES ${GLFW_BINARY_DIR}/src/glfwConfig.cmake
......
......@@ -575,6 +575,14 @@ typedef struct GLFWmonitor GLFWmonitor;
*/
typedef struct GLFWwindow GLFWwindow;
/*! @brief Opaque cursor object.
*
* Opaque cursor object.
*
* @ingroup cursor
*/
typedef struct GLFWcursor GLFWcursor;
/*! @brief The function signature for error callbacks.
*
* This is the function signature for error callback functions.
......@@ -1926,6 +1934,50 @@ GLFWAPI void glfwGetCursorPos(GLFWwindow* window, double* xpos, double* ypos);
*/
GLFWAPI void glfwSetCursorPos(GLFWwindow* window, double xpos, double ypos);
/*! @brief Creates a cursor.
*
* @param[in] width The desired cursor width.
* @param[in] height The desired cursor height.
* @param[in] xhot The desired x-coordinate of the cursor hotspot.
* @param[in] yhot The desired y-coordinate of the cursor hotspot.
* @param[in] format Not used.
* @param[in] data The cursor image data in RGBA8 format, packed in rows from
* top to bottom.
*
* @return A new cursor ready to use or `NULL` if an error occurred. If you
* don't destroy the cursor by calling `glfwDestroyCursor` it will be destroyed
* automatically by `GLFW` on termination.
*
* @note This function may only be called from the main thread.
*
* @ingroup input
*/
GLFWAPI GLFWcursor* glfwCreateCursor(int width, int height, int xhot, int yhot, int format, const void* data);
/*! @brief Destroys a cursor.
*
* This function destroys a cursor previously created by a call to
* `glfwCreateCursor`. `GLFW` will destroy all cursors automatically on
* termination.
*
* @param[in] cursor The cursor to destroy.
*
* @note This function may only be called from the main thread.
*
* @ingroup input
*/
GLFWAPI void glfwDestroyCursor(GLFWcursor* cursor);
/*! @brief Sets the cursor for a given window.
*
* @param[in] window The window to set the cursor for.
* @param[in] cursor The cursor to change to, or `NULL` to switch back to the
* default system cursor.
*
* @ingroup input
*/
GLFWAPI void glfwSetCursor(GLFWwindow* window, GLFWcursor* cursor);
/*! @brief Sets the key callback.
*
* This function sets the key callback of the specific window, which is called
......
......@@ -51,6 +51,7 @@ typedef void* id;
#define _GLFW_PLATFORM_WINDOW_STATE _GLFWwindowNS ns
#define _GLFW_PLATFORM_LIBRARY_WINDOW_STATE _GLFWlibraryNS ns
#define _GLFW_PLATFORM_MONITOR_STATE _GLFWmonitorNS ns
#define _GLFW_PLATFORM_CURSOR_STATE _GLFWcursorNS ns
//========================================================================
......@@ -67,6 +68,7 @@ typedef struct _GLFWwindowNS
id delegate;
id view;
unsigned int modifierFlags;
int cursorInside;
} _GLFWwindowNS;
......@@ -123,6 +125,15 @@ typedef struct _GLFWmonitorNS
} _GLFWmonitorNS;
//------------------------------------------------------------------------
// Platform-specific cursor structure
//------------------------------------------------------------------------
typedef struct _GLFWcursorNS
{
id handle;
} _GLFWcursorNS;
//========================================================================
// Prototypes for platform specific internal functions
//========================================================================
......
......@@ -44,7 +44,12 @@ static void centerCursor(_GLFWwindow *window)
static void setModeCursor(_GLFWwindow* window)
{
if (window->cursorMode == GLFW_CURSOR_NORMAL)
[[NSCursor arrowCursor] set];
{
if (window->cursor)
[(NSCursor*) window->cursor->ns.handle set];
else
[[NSCursor arrowCursor] set];
}
else
[(NSCursor*) _glfw.ns.cursor set];
}
......@@ -556,11 +561,13 @@ static int translateKey(unsigned int key)
- (void)mouseExited:(NSEvent *)event
{
window->ns.cursorInside = GL_FALSE;
_glfwInputCursorEnter(window, GL_FALSE);
}
- (void)mouseEntered:(NSEvent *)event
{
window->ns.cursorInside = GL_TRUE;
_glfwInputCursorEnter(window, GL_TRUE);
}
......@@ -1194,6 +1201,61 @@ void _glfwPlatformApplyCursorMode(_GLFWwindow* window)
CGAssociateMouseAndMouseCursorPosition(true);
}
int _glfwPlatformCreateCursor(_GLFWcursor* cursor, int width, int height, int cx, int cy,
int format, const void* data)
{
NSImage* image;
NSBitmapImageRep* rep;
rep = [[NSBitmapImageRep alloc]
initWithBitmapDataPlanes:NULL
pixelsWide:width
pixelsHigh:height
bitsPerSample:8
samplesPerPixel:4
hasAlpha:YES
isPlanar:NO
colorSpaceName:NSCalibratedRGBColorSpace
bitmapFormat:NSAlphaNonpremultipliedBitmapFormat
bytesPerRow:width * 4
bitsPerPixel:32];
if (rep == nil)
return GL_FALSE;
memcpy([rep bitmapData], data, 4 * width * height);
image = [[NSImage alloc] initWithSize:NSMakeSize(width, height)];
[image addRepresentation: rep];
cursor->ns.handle = [[NSCursor alloc] initWithImage:image
hotSpot:NSMakePoint(cx, cy)];
[image release];
[rep release];
if (cursor->ns.handle == nil)
return GL_FALSE;
return GL_TRUE;
}
void _glfwPlatformDestroyCursor(_GLFWcursor* cursor)
{
[(NSCursor*) cursor->ns.handle release];
}
void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor)
{
if (window->cursorMode == GLFW_CURSOR_NORMAL && window->ns.cursorInside)
{
if (cursor)
[(NSCursor*) cursor->ns.handle set];
else
[[NSCursor arrowCursor] set];
}
}
//////////////////////////////////////////////////////////////////////////
////// GLFW native API //////
......
......@@ -156,6 +156,10 @@ GLFWAPI void glfwTerminate(void)
while (_glfw.windowListHead)
glfwDestroyWindow((GLFWwindow*) _glfw.windowListHead);
// Destroy all cursors
while (_glfw.cursorListHead)
glfwDestroyCursor((GLFWcursor*) _glfw.cursorListHead);
for (i = 0; i < _glfw.monitorCount; i++)
{
_GLFWmonitor* monitor = _glfw.monitors[i];
......
......@@ -27,6 +27,11 @@
#include "internal.h"
#include <stdlib.h>
#if defined(_MSC_VER)
#include <malloc.h>
#endif
// Internal key state used for sticky keys
#define _GLFW_STICK 3
......@@ -348,6 +353,76 @@ GLFWAPI void glfwSetCursorPos(GLFWwindow* handle, double xpos, double ypos)
_glfwPlatformSetCursorPos(window, xpos, ypos);
}
GLFWAPI GLFWcursor* glfwCreateCursor(int width, int height, int cx, int cy,
int format, const void* data)
{
_GLFWcursor* cursor;
_GLFW_REQUIRE_INIT_OR_RETURN(NULL);
cursor = calloc(1, sizeof(_GLFWcursor));
if (!_glfwPlatformCreateCursor(cursor, width, height, cx, cy, format, data))
{
free(cursor);
return NULL;
}
cursor->next = _glfw.cursorListHead;
_glfw.cursorListHead = cursor;
return (GLFWcursor*) cursor;
}
GLFWAPI void glfwDestroyCursor(GLFWcursor* handle)
{
_GLFWcursor* cursor = (_GLFWcursor*) handle;
_GLFW_REQUIRE_INIT();
if (cursor == NULL)
return;
// Make sure the cursor is not being used by any window
{
_GLFWwindow* window = _glfw.windowListHead;
while (window)
{
if (window->cursor == cursor)
glfwSetCursor((GLFWwindow*) window, NULL);
window = window->next;
}
}
_glfwPlatformDestroyCursor(cursor);
// Unlink cursor from global linked list
{
_GLFWcursor** prev = &_glfw.cursorListHead;
while (*prev != cursor)
prev = &((*prev)->next);
*prev = cursor->next;
}
free(cursor);
}
GLFWAPI void glfwSetCursor(GLFWwindow* windowHandle, GLFWcursor* cursorHandle)
{
_GLFWwindow* window = (_GLFWwindow*) windowHandle;
_GLFWcursor* cursor = (_GLFWcursor*) cursorHandle;
_GLFW_REQUIRE_INIT();
_glfwPlatformSetCursor(window, cursor);
window->cursor = cursor;
}
GLFWAPI GLFWkeyfun glfwSetKeyCallback(GLFWwindow* handle, GLFWkeyfun cbfun)
{
_GLFWwindow* window = (_GLFWwindow*) handle;
......
......@@ -65,6 +65,7 @@ typedef struct _GLFWfbconfig _GLFWfbconfig;
typedef struct _GLFWwindow _GLFWwindow;
typedef struct _GLFWlibrary _GLFWlibrary;
typedef struct _GLFWmonitor _GLFWmonitor;
typedef struct _GLFWcursor _GLFWcursor;
#if defined(_GLFW_COCOA)
#include "cocoa_platform.h"
......@@ -218,6 +219,7 @@ struct _GLFWwindow
void* userPointer;
GLFWvidmode videoMode;
_GLFWmonitor* monitor;
_GLFWcursor* cursor;
// Window input state
GLboolean stickyKeys;
......@@ -285,6 +287,17 @@ struct _GLFWmonitor
};
/*! @brief Cursor structure
*/
struct _GLFWcursor
{
_GLFWcursor* next;
// This is defined in the window API's platform.h
_GLFW_PLATFORM_CURSOR_STATE;
};
/*! @brief Library global data.
*/
struct _GLFWlibrary
......@@ -319,6 +332,8 @@ struct _GLFWlibrary
double cursorPosX, cursorPosY;
_GLFWcursor* cursorListHead;
_GLFWwindow* windowListHead;
_GLFWwindow* focusedWindow;
......@@ -573,6 +588,12 @@ int _glfwPlatformExtensionSupported(const char* extension);
*/
GLFWglproc _glfwPlatformGetProcAddress(const char* procname);
int _glfwPlatformCreateCursor(_GLFWcursor* cursor, int width, int height, int cx, int cy,
int format, const void* data);
void _glfwPlatformDestroyCursor(_GLFWcursor* cursor);
void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor);
//========================================================================
// Event API functions
......
......@@ -164,6 +164,7 @@ typedef HRESULT (WINAPI * DWMISCOMPOSITIONENABLED_T)(BOOL*);
#define _GLFW_PLATFORM_WINDOW_STATE _GLFWwindowWin32 win32
#define _GLFW_PLATFORM_LIBRARY_WINDOW_STATE _GLFWlibraryWin32 win32
#define _GLFW_PLATFORM_MONITOR_STATE _GLFWmonitorWin32 win32
#define _GLFW_PLATFORM_CURSOR_STATE _GLFWcursorWin32 win32
//========================================================================
......@@ -250,6 +251,15 @@ typedef struct _GLFWmonitorWin32
} _GLFWmonitorWin32;
//------------------------------------------------------------------------
// Platform-specific cursor structure
//------------------------------------------------------------------------
typedef struct _GLFWcursorWin32
{
HCURSOR handle;
} _GLFWcursorWin32;
//========================================================================
// Prototypes for platform specific internal functions
//========================================================================
......
......@@ -100,7 +100,12 @@ static void restoreCursor(_GLFWwindow* window)
if (GetCursorPos(&pos))
{
if (WindowFromPoint(pos) == window->win32.handle)
SetCursor(LoadCursorW(NULL, IDC_ARROW));
{
if (window->cursor)
SetCursor(window->cursor->win32.handle);
else
SetCursor(LoadCursorW(NULL, IDC_ARROW));
}
}
}
......@@ -720,6 +725,11 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg,
SetCursor(NULL);
return TRUE;
}
else if (window->cursor)
{
SetCursor(window->cursor->win32.handle);
return TRUE;
}
}
break;
......@@ -1213,6 +1223,89 @@ void _glfwPlatformApplyCursorMode(_GLFWwindow* window)
}
}
int _glfwPlatformCreateCursor(_GLFWcursor* cursor, int width, int height, int cx, int cy,
int format, const void* data)
{
HDC hdc;
HBITMAP hBitmap, hMonoBitmap;
BITMAPV5HEADER bi;
ICONINFO ii;
DWORD *buffer = 0;
BYTE *image = (BYTE*) data;
int i, size = width * height;
ZeroMemory(&bi, sizeof(BITMAPV5HEADER));
bi.bV5Size = sizeof(BITMAPV5HEADER);
bi.bV5Width = width;
bi.bV5Height = -height;
bi.bV5Planes = 1;
bi.bV5BitCount = 32;
bi.bV5Compression = BI_BITFIELDS;
bi.bV5RedMask = 0x00FF0000;
bi.bV5GreenMask = 0x0000FF00;
bi.bV5BlueMask = 0x000000FF;
bi.bV5AlphaMask = 0xFF000000;
hdc = GetDC(NULL);
hBitmap = CreateDIBSection(hdc, (BITMAPINFO*) &bi, DIB_RGB_COLORS, (void**) &buffer,
NULL, (DWORD) 0);
ReleaseDC(NULL, hdc);
if (hBitmap == NULL)
return GL_FALSE;
hMonoBitmap = CreateBitmap(width, height, 1, 1, NULL);
if (hMonoBitmap == NULL)
{
DeleteObject(hBitmap);
return GL_FALSE;
}
for (i = 0; i < size; i++, buffer++, image += 4)
*buffer = (image[3] << 24) | (image[0] << 16) | (image[1] << 8) | image[2];
ii.fIcon = FALSE;
ii.xHotspot = cx;
ii.yHotspot = cy;
ii.hbmMask = hMonoBitmap;
ii.hbmColor = hBitmap;
cursor->win32.handle = (HCURSOR) CreateIconIndirect(&ii);
DeleteObject(hBitmap);
DeleteObject(hMonoBitmap);
if (cursor->win32.handle == NULL)
return GL_FALSE;
return GL_TRUE;
}
void _glfwPlatformDestroyCursor(_GLFWcursor* cursor)
{
DestroyIcon((HICON) cursor->win32.handle);
}
void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor)
{
// It should be guaranteed that the cursor is not being used by this window if
// the following condition is not met. That way it should be safe to destroy the
// cursor after calling glfwSetCursor(window, NULL) on all windows using the cursor.
if (window->cursorMode == GLFW_CURSOR_NORMAL && _glfw.focusedWindow == window &&
window->win32.cursorInside)
{
if (cursor)
SetCursor(cursor->win32.handle);
else
SetCursor(LoadCursor(NULL, IDC_ARROW));
}
}
//////////////////////////////////////////////////////////////////////////
////// GLFW native API //////
......
......@@ -34,6 +34,7 @@
#include <X11/Xlib.h>
#include <X11/keysym.h>
#include <X11/Xatom.h>
#include <X11/Xcursor/Xcursor.h>
// The Xf86VidMode extension provides fallback gamma control
#include <X11/extensions/xf86vmode.h>
......@@ -62,6 +63,7 @@
#define _GLFW_PLATFORM_WINDOW_STATE _GLFWwindowX11 x11
#define _GLFW_PLATFORM_LIBRARY_WINDOW_STATE _GLFWlibraryX11 x11
#define _GLFW_PLATFORM_MONITOR_STATE _GLFWmonitorX11 x11
#define _GLFW_PLATFORM_CURSOR_STATE _GLFWcursorX11 x11
//========================================================================
......@@ -232,6 +234,15 @@ typedef struct _GLFWmonitorX11
} _GLFWmonitorX11;
//------------------------------------------------------------------------
// Platform-specific cursor structure
//------------------------------------------------------------------------
typedef struct _GLFWcursorX11
{
Cursor handle;
} _GLFWcursorX11;
//========================================================================
// Prototypes for platform specific internal functions
//========================================================================
......
......@@ -394,7 +394,14 @@ static void disableCursor(_GLFWwindow* window)
static void restoreCursor(_GLFWwindow* window)
{
XUngrabPointer(_glfw.x11.display, CurrentTime);
XUndefineCursor(_glfw.x11.display, window->x11.handle);
if (window->cursor)
{
XDefineCursor(_glfw.x11.display, window->x11.handle,
window->cursor->x11.handle);
}
else
XUndefineCursor(_glfw.x11.display, window->x11.handle);
}
// Enter fullscreen mode
......@@ -1349,6 +1356,55 @@ void _glfwPlatformApplyCursorMode(_GLFWwindow* window)
}
}
int _glfwPlatformCreateCursor(_GLFWcursor* cursor, int width, int height, int cx, int cy,
int format, const void* data)
{
XcursorImage* cursorImage;
XcursorPixel* buffer;
unsigned char* image = (unsigned char*) data;
int i, size = width * height;
cursorImage = XcursorImageCreate(width, height);
if (cursorImage == NULL)
return GL_FALSE;
cursorImage->xhot = cx;
cursorImage->yhot = cy;
buffer = cursorImage->pixels;
for (i = 0; i < size; i++, buffer++, image += 4)
*buffer = (image[3] << 24) | (image[0] << 16) | (image[1] << 8) | image[2];
cursor->x11.handle = XcursorImageLoadCursor(_glfw.x11.display, cursorImage);
XcursorImageDestroy(cursorImage);
if (cursor->x11.handle == None)