diff --git a/README.md b/README.md index cf4b72be..216bf47a 100644 --- a/README.md +++ b/README.md @@ -63,8 +63,8 @@ GLFW bundles a number of dependencies in the `deps/` directory. ## Changelog - Added `GLFWcursor` custom system cursor handle - - Added `glfwCreateCursor`, `glfwDestroyCursor` and `glfwSetCursor` for - managing custom system cursors + - Added `glfwCreateCursor`, `glfwCreateStandardCursor`, `glfwDestroyCursor` and + `glfwSetCursor` for managing system cursor images - Added `GLFWimage` struct for passing 32-bit RGBA images - Added monitor and adapter identifier access to native API - Added `glfwSetDropCallback` and `GLFWdropfun` for receiving dropped files diff --git a/docs/input.dox b/docs/input.dox index e5655148..422c480d 100644 --- a/docs/input.dox +++ b/docs/input.dox @@ -343,6 +343,20 @@ When a cursor is destroyed, it is removed from any window where it is set. This does not affect the cursor modes of those windows. +@subsubsection input_cursor_standard Standard cursor shapes + +Cursor objects with platform-specific variants of the +[standard shapes](@ref shapes) can be created with @ref +glfwCreateStandardCursor. + +@code +GLFWcursor* cursor = glfwCreateStandardCursor(GLFW_HRESIZE_CURSOR); +@endcode + +These cursor objects behave in the exact same way as those created with @ref +glfwCreateCursor except that the platform provides the cursor image data. + + @subsection input_cursor_enter Cursor enter/leave events If you wish to be notified when the cursor enters or leaves the client area of diff --git a/docs/news.dox b/docs/news.dox index d225d22b..16101cf9 100644 --- a/docs/news.dox +++ b/docs/news.dox @@ -9,10 +9,9 @@ @subsection news_31_cursor Custom system cursor support -GLFW now supports creating and setting custom system cursors. They can be -created with @ref glfwCreateCursor, set with @ref glfwSetCursor and destroyed -with @ref glfwDestroyCursor. Custom cursors are only visible in normal cursor -mode. +GLFW now supports creating and setting both custom and standard system cursors. +They can be created with @ref glfwCreateCursor or @ref glfwCreateStandardCursor, +set with @ref glfwSetCursor and destroyed with @ref glfwDestroyCursor. @subsection news_31_drop File drop event support diff --git a/include/GLFW/glfw3.h b/include/GLFW/glfw3.h index 83b2ff00..b7dab5b9 100644 --- a/include/GLFW/glfw3.h +++ b/include/GLFW/glfw3.h @@ -672,6 +672,42 @@ extern "C" { #define GLFW_RELEASE_BEHAVIOR_FLUSH 0x00035001 #define GLFW_RELEASE_BEHAVIOR_NONE 0x00035002 +/*! @defgroup shapes Standard cursor shapes + * @ingroup input + * @{ */ + +/*! @brief The regular arrow cursor shape. + * + * The regular arrow cursor. + */ +#define GLFW_ARROW_CURSOR 0x00036001 +/*! @brief The text input I-beam cursor shape. + * + * The text input I-beam cursor shape. + */ +#define GLFW_IBEAM_CURSOR 0x00036002 +/*! @brief The crosshair shape. + * + * The crosshair shape. + */ +#define GLFW_CROSSHAIR_CURSOR 0x00036003 +/*! @brief The hand shape. + * + * The hand shape. + */ +#define GLFW_HAND_CURSOR 0x00036004 +/*! @brief The horizontal resize arrow shape. + * + * The horizontal resize arrow shape. + */ +#define GLFW_HRESIZE_CURSOR 0x00036005 +/*! @brief The vertical resize arrow shape. + * + * The vertical resize arrow shape. + */ +#define GLFW_VRESIZE_CURSOR 0x00036006 +/*! @} */ + #define GLFW_CONNECTED 0x00040001 #define GLFW_DISCONNECTED 0x00040002 @@ -2606,7 +2642,7 @@ GLFWAPI void glfwGetCursorPos(GLFWwindow* window, double* xpos, double* ypos); */ GLFWAPI void glfwSetCursorPos(GLFWwindow* window, double xpos, double ypos); -/*! @brief Creates a cursor. +/*! @brief Creates a custom cursor. * * Creates a new cursor that can be made the system cursor for a window with * @ref glfwSetCursor. The cursor can be destroyed with @ref @@ -2633,6 +2669,7 @@ GLFWAPI void glfwSetCursorPos(GLFWwindow* window, double xpos, double ypos); * * @sa @ref input_cursor * @sa glfwDestroyCursor + * @sa glfwCreateStandardCursor * * @par History * Added in GLFW 3.1. @@ -2641,6 +2678,31 @@ GLFWAPI void glfwSetCursorPos(GLFWwindow* window, double xpos, double ypos); */ GLFWAPI GLFWcursor* glfwCreateCursor(const GLFWimage* image, int xhot, int yhot); +/*! @brief Creates a cursor with a standard shape. + * + * Returns a cursor with a [standard shape](@ref shapes), which can be made the + * system cursor for a window with @ref glfwSetCursor. + * + * @param[in] shape One of the [standard shapes](@ref shapes). + * + * @return A new cursor ready to use or `NULL` if an + * [error](@ref error_handling) occurred. + * + * @note This function may not be called from a callback. + * + * @par Thread Safety + * This function may only be called from the main thread. + * + * @sa @ref input_cursor + * @sa glfwCreateCursor + * + * @par History + * Added in GLFW 3.1. + * + * @ingroup input + */ +GLFWAPI GLFWcursor* glfwCreateStandardCursor(int shape); + /*! @brief Destroys a cursor. * * This function destroys a cursor previously created with @ref diff --git a/src/cocoa_window.m b/src/cocoa_window.m index b73bc00b..ba4ac059 100644 --- a/src/cocoa_window.m +++ b/src/cocoa_window.m @@ -32,6 +32,29 @@ #include +// Returns the specified standard cursor +// +static NSCursor* getStandardCursor(int shape) +{ + switch (shape) + { + case GLFW_ARROW_CURSOR: + return [NSCursor arrowCursor]; + case GLFW_IBEAM_CURSOR: + return [NSCursor IBeamCursor]; + case GLFW_CROSSHAIR_CURSOR: + return [NSCursor crosshairCursor]; + case GLFW_HAND_CURSOR: + return [NSCursor pointingHandCursor]; + case GLFW_HRESIZE_CURSOR: + return [NSCursor resizeLeftRightCursor]; + case GLFW_VRESIZE_CURSOR: + return [NSCursor resizeUpDownCursor]; + } + + return nil; +} + // Center the cursor in the view of the window // static void centerCursor(_GLFWwindow *window) @@ -1161,6 +1184,19 @@ int _glfwPlatformCreateCursor(_GLFWcursor* cursor, return GL_TRUE; } +int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, int shape) +{ + cursor->ns.object = getStandardCursor(shape); + if (!cursor->ns.object) + { + _glfwInputError(GLFW_INVALID_ENUM, "Cocoa: Invalid standard cursor"); + return GL_FALSE; + } + + [cursor->ns.object retain]; + return GL_TRUE; +} + void _glfwPlatformDestroyCursor(_GLFWcursor* cursor) { if (cursor->ns.object) diff --git a/src/input.c b/src/input.c index a2d8ddcb..b1ed02f5 100644 --- a/src/input.c +++ b/src/input.c @@ -382,6 +382,25 @@ GLFWAPI GLFWcursor* glfwCreateCursor(const GLFWimage* image, int xhot, int yhot) return (GLFWcursor*) cursor; } +GLFWAPI GLFWcursor* glfwCreateStandardCursor(int shape) +{ + _GLFWcursor* cursor; + + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + + cursor = calloc(1, sizeof(_GLFWcursor)); + cursor->next = _glfw.cursorListHead; + _glfw.cursorListHead = cursor; + + if (!_glfwPlatformCreateStandardCursor(cursor, shape)) + { + glfwDestroyCursor((GLFWcursor*) cursor); + return NULL; + } + + return (GLFWcursor*) cursor; +} + GLFWAPI void glfwDestroyCursor(GLFWcursor* handle) { _GLFWcursor* cursor = (_GLFWcursor*) handle; diff --git a/src/internal.h b/src/internal.h index 0565256c..26fcf2a5 100644 --- a/src/internal.h +++ b/src/internal.h @@ -625,6 +625,11 @@ GLFWglproc _glfwPlatformGetProcAddress(const char* procname); */ int _glfwPlatformCreateCursor(_GLFWcursor* cursor, const GLFWimage* image, int xhot, int yhot); +/*! @copydoc glfwCreateStandardCursor + * @ingroup platform + */ +int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, int shape); + /*! @copydoc glfwDestroyCursor * @ingroup platform */ diff --git a/src/mir_window.c b/src/mir_window.c index 9896d3ad..2c8547fb 100644 --- a/src/mir_window.c +++ b/src/mir_window.c @@ -610,6 +610,14 @@ int _glfwPlatformCreateCursor(_GLFWcursor* cursor, return GL_FALSE; } +int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, int shape) +{ + _glfwInputError(GLFW_PLATFORM_ERROR, + "Mir: Unsupported Function %s!", __PRETTY_FUNCTION__); + + return GL_FALSE; +} + void _glfwPlatformDestroyCursor(_GLFWcursor* cursor) { _glfwInputError(GLFW_PLATFORM_ERROR, diff --git a/src/win32_window.c b/src/win32_window.c index edae668a..fd99d056 100644 --- a/src/win32_window.c +++ b/src/win32_window.c @@ -112,6 +112,29 @@ static void restoreCursor(_GLFWwindow* window) } } +// Translates a GLFW standard cursor to a resource ID +// +static LPWSTR translateCursorShape(int shape) +{ + switch (shape) + { + case GLFW_ARROW_CURSOR: + return IDC_ARROW; + case GLFW_IBEAM_CURSOR: + return IDC_IBEAM; + case GLFW_CROSSHAIR_CURSOR: + return IDC_CROSS; + case GLFW_HAND_CURSOR: + return IDC_HAND; + case GLFW_HRESIZE_CURSOR: + return IDC_SIZEWE; + case GLFW_VRESIZE_CURSOR: + return IDC_SIZENS; + } + + return NULL; +} + // Retrieves and translates modifier keys // static int getKeyMods(void) @@ -1176,6 +1199,26 @@ int _glfwPlatformCreateCursor(_GLFWcursor* cursor, return GL_TRUE; } +int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, int shape) +{ + LPCWSTR native = translateCursorShape(shape); + if (!native) + { + _glfwInputError(GLFW_INVALID_ENUM, "Win32: Invalid standard cursor"); + return GL_FALSE; + } + + cursor->win32.handle = CopyCursor(LoadCursorW(NULL, native)); + if (!cursor->win32.handle) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Win32: Failed to retrieve shared cursor"); + return GL_FALSE; + } + + return GL_TRUE; +} + void _glfwPlatformDestroyCursor(_GLFWcursor* cursor) { if (cursor->win32.handle) diff --git a/src/wl_window.c b/src/wl_window.c index 9ace38ff..ce71dbd5 100644 --- a/src/wl_window.c +++ b/src/wl_window.c @@ -422,6 +422,13 @@ int _glfwPlatformCreateCursor(_GLFWcursor* cursor, return GL_TRUE; } +int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, int shape) +{ + // TODO + fprintf(stderr, "_glfwPlatformCreateStandardCursor not implemented yet\n"); + return GL_FALSE; +} + void _glfwPlatformDestroyCursor(_GLFWcursor* cursor) { wl_buffer_destroy(cursor->wl.buffer); diff --git a/src/x11_window.c b/src/x11_window.c index 1b38daae..83734874 100644 --- a/src/x11_window.c +++ b/src/x11_window.c @@ -27,6 +27,8 @@ #include "internal.h" +#include + #include #include @@ -66,6 +68,29 @@ static Bool isFrameExtentsEvent(Display* display, XEvent* event, XPointer pointe event->xproperty.atom == _glfw.x11.NET_FRAME_EXTENTS; } +// Translates a GLFW standard cursor to a font cursor shape +// +static int translateCursorShape(int shape) +{ + switch (shape) + { + case GLFW_ARROW_CURSOR: + return XC_arrow; + case GLFW_IBEAM_CURSOR: + return XC_xterm; + case GLFW_CROSSHAIR_CURSOR: + return XC_crosshair; + case GLFW_HAND_CURSOR: + return XC_hand1; + case GLFW_HRESIZE_CURSOR: + return XC_sb_h_double_arrow; + case GLFW_VRESIZE_CURSOR: + return XC_sb_v_double_arrow; + } + + return 0; +} + // Translates an X event modifier state mask // static int translateState(int state) @@ -1731,12 +1756,32 @@ int _glfwPlatformCreateCursor(_GLFWcursor* cursor, int xhot, int yhot) { cursor->x11.handle = _glfwCreateCursor(image, xhot, yhot); - if (cursor->x11.handle == None) + if (!cursor->x11.handle) return GL_FALSE; return GL_TRUE; } +int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, int shape) +{ + const unsigned int native = translateCursorShape(shape); + if (!native) + { + _glfwInputError(GLFW_INVALID_ENUM, "X11: Invalid standard cursor"); + return GL_FALSE; + } + + cursor->x11.handle = XCreateFontCursor(_glfw.x11.display, native); + if (!cursor->x11.handle) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "X11: Failed to create standard cursor"); + return GL_FALSE; + } + + return GL_TRUE; +} + void _glfwPlatformDestroyCursor(_GLFWcursor* cursor) { if (cursor->x11.handle)