mirror of
https://github.com/glfw/glfw.git
synced 2026-09-19 23:43:15 +00:00
Added support for custom system cursors.
This adds 3 functions to the GLFW API: glfwCreateCursor, glfwDestroyCursor and glfwSetCursor.
This commit is contained in:
@@ -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
|
||||
//========================================================================
|
||||
|
||||
+63
-1
@@ -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];
|
||||
|
||||
+75
@@ -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
|
||||
//========================================================================
|
||||
|
||||
+94
-1
@@ -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
|
||||
//========================================================================
|
||||
|
||||
+57
-1
@@ -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)
|
||||
return GL_FALSE;
|
||||
|
||||
return GL_TRUE;
|
||||
}
|
||||
|
||||
void _glfwPlatformDestroyCursor(_GLFWcursor* cursor)
|
||||
{
|
||||
XFreeCursor(_glfw.x11.display, cursor->x11.handle);
|
||||
}
|
||||
|
||||
void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor)
|
||||
{
|
||||
if (window->cursorMode == GLFW_CURSOR_NORMAL)
|
||||
{
|
||||
if (cursor)
|
||||
XDefineCursor(_glfw.x11.display, window->x11.handle, cursor->x11.handle);
|
||||
else
|
||||
XUndefineCursor(_glfw.x11.display, window->x11.handle);
|
||||
|
||||
XFlush(_glfw.x11.display);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
////// GLFW native API //////
|
||||
|
||||
Reference in New Issue
Block a user