diff --git a/README.md b/README.md
index fc5039e8..daa1be9c 100644
--- a/README.md
+++ b/README.md
@@ -118,6 +118,11 @@ information on what to include when reporting a bug.
## Changelog
+ - Added `GLFW_RESIZE_NWSE_CURSOR`, `GLFW_RESIZE_NESW_CURSOR`,
+ `GLFW_RESIZE_ALL_CURSOR` and `GLFW_NOT_ALLOWED_CURSOR` cursor shapes (#427)
+ - Added `GLFW_RESIZE_EW_CURSOR` alias for `GLFW_HRESIZE_CURSOR` (#427)
+ - Added `GLFW_RESIZE_NS_CURSOR` alias for `GLFW_VRESIZE_CURSOR` (#427)
+ - Added `GLFW_POINTING_HAND_CURSOR` alias for `GLFW_HAND_CURSOR` (#427)
- Disabled tests and examples by default when built as a CMake subdirectory
- Bugfix: The CMake config-file package used an absolute path and was not
relocatable (#1470)
diff --git a/docs/compat.dox b/docs/compat.dox
index 77ba63b4..c437e350 100644
--- a/docs/compat.dox
+++ b/docs/compat.dox
@@ -85,6 +85,13 @@ transparent window framebuffers. If the running X server does not support this
extension or there is no running compositing manager, the
`GLFW_TRANSPARENT_FRAMEBUFFER` framebuffer hint will have no effect.
+GLFW uses both the Xcursor extension and the freedesktop cursor conventions to
+provide an expanded set of standard cursor shapes. If the running X server does
+not support this extension or the current cursor theme does not support the
+conventions, the `GLFW_RESIZE_NWSE_CURSOR`, `GLFW_RESIZE_NESW_CURSOR` and
+`GLFW_NOT_ALLOWED_CURSOR` shapes will not be available and other shapes may use
+legacy images.
+
@section compat_wayland Wayland protocols and IPC standards
diff --git a/docs/input.dox b/docs/input.dox
index dea64877..331f9718 100644
--- a/docs/input.dox
+++ b/docs/input.dox
@@ -373,12 +373,15 @@ A cursor with a [standard shape](@ref shapes) from the current system cursor
theme can be can be created with @ref glfwCreateStandardCursor.
@code
-GLFWcursor* cursor = glfwCreateStandardCursor(GLFW_HRESIZE_CURSOR);
+GLFWcursor* url_cursor = glfwCreateStandardCursor(GLFW_POINTING_HAND_CURSOR);
@endcode
These cursor objects behave in the exact same way as those created with @ref
glfwCreateCursor except that the system cursor theme provides the actual image.
+A few of these shapes are not available everywhere. If a shape is unavailable,
+`NULL` is returned. See @ref glfwCreateStandardCursor for details.
+
@subsubsection cursor_destruction Cursor destruction
diff --git a/docs/news.dox b/docs/news.dox
index 39f19031..f18537af 100644
--- a/docs/news.dox
+++ b/docs/news.dox
@@ -9,6 +9,24 @@
@subsection features_34 New features in version 3.4
+@subsubsection standard_cursors_34 More standard cursors
+
+GLFW now provides the standard cursor shapes @ref GLFW_RESIZE_NWSE_CURSOR and
+@ref GLFW_RESIZE_NESW_CURSOR for diagonal resizing, @ref GLFW_RESIZE_ALL_CURSOR
+for omni-directional resizing and @ref GLFW_NOT_ALLOWED_CURSOR for showing an
+action is not allowed.
+
+Unlike the original set, these shapes may not be available everywhere and
+creation will then fail with the new @ref GLFW_CURSOR_UNAVAILABLE error.
+
+The cursors for horizontal and vertical resizing are now referred to as @ref
+GLFW_RESIZE_EW_CURSOR and @ref GLFW_RESIZE_NS_CURSOR, and the pointing hand
+cursor is now referred to as @ref GLFW_POINTING_HAND_CURSOR. The older names
+are still available.
+
+For more information see @ref cursor_standard.
+
+
@subsubsection features_34_win32_keymenu Support for keyboard access to Windows window menu
GLFW now provides the
@@ -44,6 +62,14 @@ add_subdirectory(path/to/glfw)
@subsubsection types_34 New types in version 3.4
@subsubsection constants_34 New constants in version 3.4
+ - @ref GLFW_POINTING_HAND_CURSOR
+ - @ref GLFW_RESIZE_EW_CURSOR
+ - @ref GLFW_RESIZE_NS_CURSOR
+ - @ref GLFW_RESIZE_NWSE_CURSOR
+ - @ref GLFW_RESIZE_NESW_CURSOR
+ - @ref GLFW_RESIZE_ALL_CURSOR
+ - @ref GLFW_NOT_ALLOWED_CURSOR
+ - @ref GLFW_CURSOR_UNAVAILABLE
- @ref GLFW_WIN32_KEYBOARD_MENU
diff --git a/include/GLFW/glfw3.h b/include/GLFW/glfw3.h
index 0b603160..8b5b87dd 100644
--- a/include/GLFW/glfw3.h
+++ b/include/GLFW/glfw3.h
@@ -757,6 +757,17 @@ extern "C" {
* @analysis Application programmer error. Fix the offending call.
*/
#define GLFW_NO_WINDOW_CONTEXT 0x0001000A
+/*! @brief The specified cursor shape is not available.
+ *
+ * The specified standard cursor shape is not available, either because the
+ * current system cursor theme does not provide it or because it is not
+ * available on the platform.
+ *
+ * @analysis Platform or system settings limitation. Pick another
+ * [standard cursor shape](@ref shapes) or create a
+ * [custom cursor](@ref cursor_custom).
+ */
+#define GLFW_CURSOR_UNAVAILABLE 0x0001000B
/*! @} */
/*! @addtogroup window
@@ -1039,14 +1050,15 @@ extern "C" {
/*! @defgroup shapes Standard cursor shapes
* @brief Standard system cursor shapes.
*
- * See [standard cursor creation](@ref cursor_standard) for how these are used.
+ * These are the [standard cursor shapes](@ref cursor_standard) that can be
+ * requested from the window system.
*
* @ingroup input
* @{ */
/*! @brief The regular arrow cursor shape.
*
- * The regular arrow cursor.
+ * The regular arrow cursor shape.
*/
#define GLFW_ARROW_CURSOR 0x00036001
/*! @brief The text input I-beam cursor shape.
@@ -1054,26 +1066,91 @@ extern "C" {
* The text input I-beam cursor shape.
*/
#define GLFW_IBEAM_CURSOR 0x00036002
-/*! @brief The crosshair shape.
+/*! @brief The crosshair cursor shape.
*
- * The crosshair shape.
+ * The crosshair cursor shape.
*/
#define GLFW_CROSSHAIR_CURSOR 0x00036003
-/*! @brief The hand shape.
+/*! @brief The pointing hand cursor shape.
*
- * The hand shape.
+ * The pointing hand cursor shape.
*/
-#define GLFW_HAND_CURSOR 0x00036004
-/*! @brief The horizontal resize arrow shape.
+#define GLFW_POINTING_HAND_CURSOR 0x00036004
+/*! @brief The horizontal resize/move arrow shape.
*
- * The horizontal resize arrow shape.
+ * The horizontal resize/move arrow shape. This is usually a horizontal
+ * double-headed arrow.
*/
-#define GLFW_HRESIZE_CURSOR 0x00036005
-/*! @brief The vertical resize arrow shape.
+#define GLFW_RESIZE_EW_CURSOR 0x00036005
+/*! @brief The vertical resize/move arrow shape.
*
- * The vertical resize arrow shape.
+ * The vertical resize/move shape. This is usually a vertical double-headed
+ * arrow.
*/
-#define GLFW_VRESIZE_CURSOR 0x00036006
+#define GLFW_RESIZE_NS_CURSOR 0x00036006
+/*! @brief The top-left to bottom-right diagonal resize/move arrow shape.
+ *
+ * The top-left to bottom-right diagonal resize/move shape. This is usually
+ * a diagonal double-headed arrow.
+ *
+ * @note @macos This shape is provided by a private system API and may fail
+ * with @ref GLFW_CURSOR_UNAVAILABLE in the future.
+ *
+ * @note @x11 This shape is provided by a newer standard not supported by all
+ * cursor themes.
+ *
+ * @note @wayland This shape is provided by a newer standard not supported by
+ * all cursor themes.
+ */
+#define GLFW_RESIZE_NWSE_CURSOR 0x00036007
+/*! @brief The top-right to bottom-left diagonal resize/move arrow shape.
+ *
+ * The top-right to bottom-left diagonal resize/move shape. This is usually
+ * a diagonal double-headed arrow.
+ *
+ * @note @macos This shape is provided by a private system API and may fail
+ * with @ref GLFW_CURSOR_UNAVAILABLE in the future.
+ *
+ * @note @x11 This shape is provided by a newer standard not supported by all
+ * cursor themes.
+ *
+ * @note @wayland This shape is provided by a newer standard not supported by
+ * all cursor themes.
+ */
+#define GLFW_RESIZE_NESW_CURSOR 0x00036008
+/*! @brief The omni-directional resize/move cursor shape.
+ *
+ * The omni-directional resize cursor/move shape. This is usually either
+ * a combined horizontal and vertical double-headed arrow or a grabbing hand.
+ */
+#define GLFW_RESIZE_ALL_CURSOR 0x00036009
+/*! @brief The operation-not-allowed shape.
+ *
+ * The operation-not-allowed shape. This is usually a circle with a diagonal
+ * line through it.
+ *
+ * @note @x11 This shape is provided by a newer standard not supported by all
+ * cursor themes.
+ *
+ * @note @wayland This shape is provided by a newer standard not supported by
+ * all cursor themes.
+ */
+#define GLFW_NOT_ALLOWED_CURSOR 0x0003600A
+/*! @brief Legacy name for compatibility.
+ *
+ * This is an alias for compatibility with earlier versions.
+ */
+#define GLFW_HRESIZE_CURSOR GLFW_RESIZE_EW_CURSOR
+/*! @brief Legacy name for compatibility.
+ *
+ * This is an alias for compatibility with earlier versions.
+ */
+#define GLFW_VRESIZE_CURSOR GLFW_RESIZE_NS_CURSOR
+/*! @brief Legacy name for compatibility.
+ *
+ * This is an alias for compatibility with earlier versions.
+ */
+#define GLFW_HAND_CURSOR GLFW_POINTING_HAND_CURSOR
/*! @} */
#define GLFW_CONNECTED 0x00040001
@@ -4417,19 +4494,44 @@ 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), that can be set for
- * a window with @ref glfwSetCursor.
+ * Returns a cursor with a standard shape, that can be set for a window with
+ * @ref glfwSetCursor. The images for these cursors come from the system
+ * cursor theme and their exact appearance will vary between platforms.
+ *
+ * Most of these shapes are guaranteed to exist on every supported platform but
+ * a few may not be present. See the table below for details.
+ *
+ * Cursor shape | Windows | macOS | X11 | Wayland
+ * ------------------------------ | ------- | ----- | ------ | -------
+ * @ref GLFW_ARROW_CURSOR | Yes | Yes | Yes | Yes
+ * @ref GLFW_IBEAM_CURSOR | Yes | Yes | Yes | Yes
+ * @ref GLFW_CROSSHAIR_CURSOR | Yes | Yes | Yes | Yes
+ * @ref GLFW_POINTING_HAND_CURSOR | Yes | Yes | Yes | Yes
+ * @ref GLFW_RESIZE_EW_CURSOR | Yes | Yes | Yes | Yes
+ * @ref GLFW_RESIZE_NS_CURSOR | Yes | Yes | Yes | Yes
+ * @ref GLFW_RESIZE_NWSE_CURSOR | Yes | Yes1 | Maybe2 | Maybe2
+ * @ref GLFW_RESIZE_NESW_CURSOR | Yes | Yes1 | Maybe2 | Maybe2
+ * @ref GLFW_RESIZE_ALL_CURSOR | Yes | Yes | Yes | Yes
+ * @ref GLFW_NOT_ALLOWED_CURSOR | Yes | Yes | Maybe2 | Maybe2
+ *
+ * 1) This uses a private system API and may fail in the future.
+ *
+ * 2) This uses a newer standard that not all cursor themes support.
+ *
+ * If the requested shape is not available, this function emits a @ref
+ * GLFW_CURSOR_UNAVAILABLE error and returns `NULL`.
*
* @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.
*
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref
- * GLFW_INVALID_ENUM and @ref GLFW_PLATFORM_ERROR.
+ * GLFW_INVALID_ENUM, @ref GLFW_CURSOR_UNAVAILABLE and @ref
+ * GLFW_PLATFORM_ERROR.
*
* @thread_safety This function must only be called from the main thread.
*
- * @sa @ref cursor_object
+ * @sa @ref cursor_standard
* @sa @ref glfwCreateCursor
*
* @since Added in version 3.1.
diff --git a/src/cocoa_window.m b/src/cocoa_window.m
index 8e0b28aa..bfec8b7b 100644
--- a/src/cocoa_window.m
+++ b/src/cocoa_window.m
@@ -1603,23 +1603,49 @@ int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, int shape)
{
@autoreleasepool {
- if (shape == GLFW_ARROW_CURSOR)
- cursor->ns.object = [NSCursor arrowCursor];
- else if (shape == GLFW_IBEAM_CURSOR)
- cursor->ns.object = [NSCursor IBeamCursor];
- else if (shape == GLFW_CROSSHAIR_CURSOR)
- cursor->ns.object = [NSCursor crosshairCursor];
- else if (shape == GLFW_HAND_CURSOR)
- cursor->ns.object = [NSCursor pointingHandCursor];
- else if (shape == GLFW_HRESIZE_CURSOR)
- cursor->ns.object = [NSCursor resizeLeftRightCursor];
- else if (shape == GLFW_VRESIZE_CURSOR)
- cursor->ns.object = [NSCursor resizeUpDownCursor];
+ SEL cursorSelector = NULL;
+
+ // HACK: Try to use a private message
+ if (shape == GLFW_RESIZE_EW_CURSOR)
+ cursorSelector = @selector(_windowResizeEastWestCursor);
+ else if (shape == GLFW_RESIZE_NS_CURSOR)
+ cursorSelector = @selector(_windowResizeNorthSouthCursor);
+ else if (shape == GLFW_RESIZE_NWSE_CURSOR)
+ cursorSelector = @selector(_windowResizeNorthWestSouthEastCursor);
+ else if (shape == GLFW_RESIZE_NESW_CURSOR)
+ cursorSelector = @selector(_windowResizeNorthEastSouthWestCursor);
+
+ if (cursorSelector && [NSCursor respondsToSelector:cursorSelector])
+ {
+ id object = [NSCursor performSelector:cursorSelector];
+ if ([object isKindOfClass:[NSCursor class]])
+ cursor->ns.object = object;
+ }
if (!cursor->ns.object)
{
- _glfwInputError(GLFW_PLATFORM_ERROR,
- "Cocoa: Failed to retrieve standard cursor");
+ if (shape == GLFW_ARROW_CURSOR)
+ cursor->ns.object = [NSCursor arrowCursor];
+ else if (shape == GLFW_IBEAM_CURSOR)
+ cursor->ns.object = [NSCursor IBeamCursor];
+ else if (shape == GLFW_CROSSHAIR_CURSOR)
+ cursor->ns.object = [NSCursor crosshairCursor];
+ else if (shape == GLFW_POINTING_HAND_CURSOR)
+ cursor->ns.object = [NSCursor pointingHandCursor];
+ else if (shape == GLFW_RESIZE_EW_CURSOR)
+ cursor->ns.object = [NSCursor resizeLeftRightCursor];
+ else if (shape == GLFW_RESIZE_NS_CURSOR)
+ cursor->ns.object = [NSCursor resizeUpDownCursor];
+ else if (shape == GLFW_RESIZE_ALL_CURSOR)
+ cursor->ns.object = [NSCursor closedHandCursor];
+ else if (shape == GLFW_NOT_ALLOWED_CURSOR)
+ cursor->ns.object = [NSCursor operationNotAllowedCursor];
+ }
+
+ if (!cursor->ns.object)
+ {
+ _glfwInputError(GLFW_CURSOR_UNAVAILABLE,
+ "Cocoa: Standard cursor shape unavailable");
return GLFW_FALSE;
}
diff --git a/src/init.c b/src/init.c
index 96feab0c..13baff70 100644
--- a/src/init.c
+++ b/src/init.c
@@ -189,6 +189,8 @@ void _glfwInputError(int code, const char* format, ...)
strcpy(description, "The requested format is unavailable");
else if (code == GLFW_NO_WINDOW_CONTEXT)
strcpy(description, "The specified window has no context");
+ else if (code == GLFW_CURSOR_UNAVAILABLE)
+ strcpy(description, "The specified cursor shape is unavailable");
else
strcpy(description, "ERROR: UNKNOWN GLFW ERROR");
}
diff --git a/src/input.c b/src/input.c
index 28291750..f6163093 100644
--- a/src/input.c
+++ b/src/input.c
@@ -757,9 +757,13 @@ GLFWAPI GLFWcursor* glfwCreateStandardCursor(int shape)
if (shape != GLFW_ARROW_CURSOR &&
shape != GLFW_IBEAM_CURSOR &&
shape != GLFW_CROSSHAIR_CURSOR &&
- shape != GLFW_HAND_CURSOR &&
- shape != GLFW_HRESIZE_CURSOR &&
- shape != GLFW_VRESIZE_CURSOR)
+ shape != GLFW_POINTING_HAND_CURSOR &&
+ shape != GLFW_RESIZE_EW_CURSOR &&
+ shape != GLFW_RESIZE_NS_CURSOR &&
+ shape != GLFW_RESIZE_NWSE_CURSOR &&
+ shape != GLFW_RESIZE_NESW_CURSOR &&
+ shape != GLFW_RESIZE_ALL_CURSOR &&
+ shape != GLFW_NOT_ALLOWED_CURSOR)
{
_glfwInputError(GLFW_INVALID_ENUM, "Invalid standard cursor 0x%08X", shape);
return NULL;
diff --git a/src/win32_window.c b/src/win32_window.c
index 81cdf62a..3a50c542 100644
--- a/src/win32_window.c
+++ b/src/win32_window.c
@@ -2068,14 +2068,25 @@ int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, int shape)
id = OCR_IBEAM;
else if (shape == GLFW_CROSSHAIR_CURSOR)
id = OCR_CROSS;
- else if (shape == GLFW_HAND_CURSOR)
+ else if (shape == GLFW_POINTING_HAND_CURSOR)
id = OCR_HAND;
- else if (shape == GLFW_HRESIZE_CURSOR)
+ else if (shape == GLFW_RESIZE_EW_CURSOR)
id = OCR_SIZEWE;
- else if (shape == GLFW_VRESIZE_CURSOR)
+ else if (shape == GLFW_RESIZE_NS_CURSOR)
id = OCR_SIZENS;
+ else if (shape == GLFW_RESIZE_NWSE_CURSOR)
+ id = OCR_SIZENWSE;
+ else if (shape == GLFW_RESIZE_NESW_CURSOR)
+ id = OCR_SIZENESW;
+ else if (shape == GLFW_RESIZE_ALL_CURSOR)
+ id = OCR_SIZEALL;
+ else if (shape == GLFW_NOT_ALLOWED_CURSOR)
+ id = OCR_NO;
else
+ {
+ _glfwInputError(GLFW_PLATFORM_ERROR, "Win32: Unknown standard cursor");
return GLFW_FALSE;
+ }
cursor->win32.handle = LoadImageW(NULL,
MAKEINTRESOURCEW(id), IMAGE_CURSOR, 0, 0,
diff --git a/src/wl_window.c b/src/wl_window.c
index fa422ebe..c8dde30a 100644
--- a/src/wl_window.c
+++ b/src/wl_window.c
@@ -770,28 +770,6 @@ static void handleEvents(int timeout)
}
}
-// Translates a GLFW standard cursor to a theme cursor name
-//
-static char *translateCursorShape(int shape)
-{
- switch (shape)
- {
- case GLFW_ARROW_CURSOR:
- return "left_ptr";
- case GLFW_IBEAM_CURSOR:
- return "xterm";
- case GLFW_CROSSHAIR_CURSOR:
- return "crosshair";
- case GLFW_HAND_CURSOR:
- return "hand2";
- case GLFW_HRESIZE_CURSOR:
- return "sb_h_double_arrow";
- case GLFW_VRESIZE_CURSOR:
- return "sb_v_double_arrow";
- }
- return NULL;
-}
-
//////////////////////////////////////////////////////////////////////////
////// GLFW platform API //////
//////////////////////////////////////////////////////////////////////////
@@ -1233,26 +1211,79 @@ int _glfwPlatformCreateCursor(_GLFWcursor* cursor,
int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, int shape)
{
- struct wl_cursor* standardCursor;
+ const char* name = NULL;
- standardCursor = wl_cursor_theme_get_cursor(_glfw.wl.cursorTheme,
- translateCursorShape(shape));
- if (!standardCursor)
- {
- _glfwInputError(GLFW_PLATFORM_ERROR,
- "Wayland: Standard cursor \"%s\" not found",
- translateCursorShape(shape));
- return GLFW_FALSE;
- }
+ // Try the XDG names first
+ if (shape == GLFW_ARROW_CURSOR)
+ name = "default";
+ else if (shape == GLFW_IBEAM_CURSOR)
+ name = "text";
+ else if (shape == GLFW_CROSSHAIR_CURSOR)
+ name = "crosshair";
+ else if (shape == GLFW_POINTING_HAND_CURSOR)
+ name = "pointer";
+ else if (shape == GLFW_RESIZE_EW_CURSOR)
+ name = "ew-resize";
+ else if (shape == GLFW_RESIZE_NS_CURSOR)
+ name = "ns-resize";
+ else if (shape == GLFW_RESIZE_NWSE_CURSOR)
+ name = "nwse-resize";
+ else if (shape == GLFW_RESIZE_NESW_CURSOR)
+ name = "nesw-resize";
+ else if (shape == GLFW_RESIZE_ALL_CURSOR)
+ name = "all-scroll";
+ else if (shape == GLFW_NOT_ALLOWED_CURSOR)
+ name = "not-allowed";
- cursor->wl.cursor = standardCursor;
- cursor->wl.currentImage = 0;
+ cursor->wl.cursor = wl_cursor_theme_get_cursor(_glfw.wl.cursorTheme, name);
if (_glfw.wl.cursorThemeHiDPI)
{
- standardCursor = wl_cursor_theme_get_cursor(_glfw.wl.cursorThemeHiDPI,
- translateCursorShape(shape));
- cursor->wl.cursorHiDPI = standardCursor;
+ cursor->wl.cursorHiDPI =
+ wl_cursor_theme_get_cursor(_glfw.wl.cursorThemeHiDPI, name);
+ }
+
+ if (!cursor->wl.cursor)
+ {
+ // Fall back to the core X11 names
+ if (shape == GLFW_ARROW_CURSOR)
+ name = "left_ptr";
+ else if (shape == GLFW_IBEAM_CURSOR)
+ name = "xterm";
+ else if (shape == GLFW_CROSSHAIR_CURSOR)
+ name = "crosshair";
+ else if (shape == GLFW_POINTING_HAND_CURSOR)
+ name = "hand2";
+ else if (shape == GLFW_RESIZE_EW_CURSOR)
+ name = "sb_h_double_arrow";
+ else if (shape == GLFW_RESIZE_NS_CURSOR)
+ name = "sb_v_double_arrow";
+ else if (shape == GLFW_RESIZE_ALL_CURSOR)
+ name = "fleur";
+ else
+ {
+ _glfwInputError(GLFW_CURSOR_UNAVAILABLE,
+ "Wayland: Standard cursor shape unavailable");
+ return GLFW_FALSE;
+ }
+
+ cursor->wl.cursor = wl_cursor_theme_get_cursor(_glfw.wl.cursorTheme, name);
+ if (!cursor->wl.cursor)
+ {
+ _glfwInputError(GLFW_PLATFORM_ERROR,
+ "Wayland: Failed to create standard cursor \"%s\"",
+ name);
+ return GLFW_FALSE;
+ }
+
+ if (_glfw.wl.cursorThemeHiDPI)
+ {
+ if (!cursor->wl.cursorHiDPI)
+ {
+ cursor->wl.cursorHiDPI =
+ wl_cursor_theme_get_cursor(_glfw.wl.cursorThemeHiDPI, name);
+ }
+ }
}
return GLFW_TRUE;
diff --git a/src/x11_init.c b/src/x11_init.c
index ca8b2ff2..d44d4e2a 100644
--- a/src/x11_init.c
+++ b/src/x11_init.c
@@ -615,6 +615,12 @@ static GLFWbool initExtensions(void)
_glfw_dlsym(_glfw.x11.xcursor.handle, "XcursorImageDestroy");
_glfw.x11.xcursor.ImageLoadCursor = (PFN_XcursorImageLoadCursor)
_glfw_dlsym(_glfw.x11.xcursor.handle, "XcursorImageLoadCursor");
+ _glfw.x11.xcursor.GetTheme = (PFN_XcursorGetTheme)
+ _glfw_dlsym(_glfw.x11.xcursor.handle, "XcursorGetTheme");
+ _glfw.x11.xcursor.GetDefaultSize = (PFN_XcursorGetDefaultSize)
+ _glfw_dlsym(_glfw.x11.xcursor.handle, "XcursorGetDefaultSize");
+ _glfw.x11.xcursor.LibraryLoadImage = (PFN_XcursorLibraryLoadImage)
+ _glfw_dlsym(_glfw.x11.xcursor.handle, "XcursorLibraryLoadImage");
}
#if defined(__CYGWIN__)
diff --git a/src/x11_platform.h b/src/x11_platform.h
index 33145e63..cb3b1068 100644
--- a/src/x11_platform.h
+++ b/src/x11_platform.h
@@ -85,9 +85,15 @@ typedef int (* PFN_XRRUpdateConfiguration)(XEvent*);
typedef XcursorImage* (* PFN_XcursorImageCreate)(int,int);
typedef void (* PFN_XcursorImageDestroy)(XcursorImage*);
typedef Cursor (* PFN_XcursorImageLoadCursor)(Display*,const XcursorImage*);
+typedef char* (* PFN_XcursorGetTheme)(Display*);
+typedef int (* PFN_XcursorGetDefaultSize)(Display*);
+typedef XcursorImage* (* PFN_XcursorLibraryLoadImage)(const char*,const char*,int);
#define XcursorImageCreate _glfw.x11.xcursor.ImageCreate
#define XcursorImageDestroy _glfw.x11.xcursor.ImageDestroy
#define XcursorImageLoadCursor _glfw.x11.xcursor.ImageLoadCursor
+#define XcursorGetTheme _glfw.x11.xcursor.GetTheme
+#define XcursorGetDefaultSize _glfw.x11.xcursor.GetDefaultSize
+#define XcursorLibraryLoadImage _glfw.x11.xcursor.LibraryLoadImage
typedef Bool (* PFN_XineramaIsActive)(Display*);
typedef Bool (* PFN_XineramaQueryExtension)(Display*,int*,int*);
@@ -352,6 +358,9 @@ typedef struct _GLFWlibraryX11
PFN_XcursorImageCreate ImageCreate;
PFN_XcursorImageDestroy ImageDestroy;
PFN_XcursorImageLoadCursor ImageLoadCursor;
+ PFN_XcursorGetTheme GetTheme;
+ PFN_XcursorGetDefaultSize GetDefaultSize;
+ PFN_XcursorLibraryLoadImage LibraryLoadImage;
} xcursor;
struct {
diff --git a/src/x11_window.c b/src/x11_window.c
index 56379a3c..9f0312ce 100644
--- a/src/x11_window.c
+++ b/src/x11_window.c
@@ -2823,29 +2823,76 @@ int _glfwPlatformCreateCursor(_GLFWcursor* cursor,
int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, int shape)
{
- int native = 0;
+ if (_glfw.x11.xcursor.handle)
+ {
+ char* theme = XcursorGetTheme(_glfw.x11.display);
+ if (theme)
+ {
+ const int size = XcursorGetDefaultSize(_glfw.x11.display);
+ const char* name = NULL;
- if (shape == GLFW_ARROW_CURSOR)
- native = XC_left_ptr;
- else if (shape == GLFW_IBEAM_CURSOR)
- native = XC_xterm;
- else if (shape == GLFW_CROSSHAIR_CURSOR)
- native = XC_crosshair;
- else if (shape == GLFW_HAND_CURSOR)
- native = XC_hand2;
- else if (shape == GLFW_HRESIZE_CURSOR)
- native = XC_sb_h_double_arrow;
- else if (shape == GLFW_VRESIZE_CURSOR)
- native = XC_sb_v_double_arrow;
- else
- return GLFW_FALSE;
+ if (shape == GLFW_ARROW_CURSOR)
+ name = "default";
+ else if (shape == GLFW_IBEAM_CURSOR)
+ name = "text";
+ else if (shape == GLFW_CROSSHAIR_CURSOR)
+ name = "crosshair";
+ else if (shape == GLFW_POINTING_HAND_CURSOR)
+ name = "pointer";
+ else if (shape == GLFW_RESIZE_EW_CURSOR)
+ name = "ew-resize";
+ else if (shape == GLFW_RESIZE_NS_CURSOR)
+ name = "ns-resize";
+ else if (shape == GLFW_RESIZE_NWSE_CURSOR)
+ name = "nwse-resize";
+ else if (shape == GLFW_RESIZE_NESW_CURSOR)
+ name = "nesw-resize";
+ else if (shape == GLFW_RESIZE_ALL_CURSOR)
+ name = "all-scroll";
+ else if (shape == GLFW_NOT_ALLOWED_CURSOR)
+ name = "not-allowed";
+
+ XcursorImage* image = XcursorLibraryLoadImage(name, theme, size);
+ if (image)
+ {
+ cursor->x11.handle = XcursorImageLoadCursor(_glfw.x11.display, image);
+ XcursorImageDestroy(image);
+ }
+ }
+ }
- cursor->x11.handle = XCreateFontCursor(_glfw.x11.display, native);
if (!cursor->x11.handle)
{
- _glfwInputError(GLFW_PLATFORM_ERROR,
- "X11: Failed to create standard cursor");
- return GLFW_FALSE;
+ unsigned int native = 0;
+
+ if (shape == GLFW_ARROW_CURSOR)
+ native = XC_left_ptr;
+ else if (shape == GLFW_IBEAM_CURSOR)
+ native = XC_xterm;
+ else if (shape == GLFW_CROSSHAIR_CURSOR)
+ native = XC_crosshair;
+ else if (shape == GLFW_POINTING_HAND_CURSOR)
+ native = XC_hand2;
+ else if (shape == GLFW_RESIZE_EW_CURSOR)
+ native = XC_sb_h_double_arrow;
+ else if (shape == GLFW_RESIZE_NS_CURSOR)
+ native = XC_sb_v_double_arrow;
+ else if (shape == GLFW_RESIZE_ALL_CURSOR)
+ native = XC_fleur;
+ else
+ {
+ _glfwInputError(GLFW_CURSOR_UNAVAILABLE,
+ "X11: Standard cursor shape unavailable");
+ return GLFW_FALSE;
+ }
+
+ cursor->x11.handle = XCreateFontCursor(_glfw.x11.display, native);
+ if (!cursor->x11.handle)
+ {
+ _glfwInputError(GLFW_PLATFORM_ERROR,
+ "X11: Failed to create standard cursor");
+ return GLFW_FALSE;
+ }
}
return GLFW_TRUE;
diff --git a/tests/cursor.c b/tests/cursor.c
index b6288f6a..f72c0f91 100644
--- a/tests/cursor.c
+++ b/tests/cursor.c
@@ -69,7 +69,7 @@ static int swap_interval = 1;
static int wait_events = GLFW_TRUE;
static int animate_cursor = GLFW_FALSE;
static int track_cursor = GLFW_FALSE;
-static GLFWcursor* standard_cursors[6];
+static GLFWcursor* standard_cursors[10];
static GLFWcursor* tracking_cursor = NULL;
static void error_callback(int error, const char* description)
@@ -271,28 +271,24 @@ static void key_callback(GLFWwindow* window, int key, int scancode, int action,
break;
case GLFW_KEY_1:
- glfwSetCursor(window, standard_cursors[0]);
- break;
-
case GLFW_KEY_2:
- glfwSetCursor(window, standard_cursors[1]);
- break;
-
case GLFW_KEY_3:
- glfwSetCursor(window, standard_cursors[2]);
- break;
-
case GLFW_KEY_4:
- glfwSetCursor(window, standard_cursors[3]);
- break;
-
case GLFW_KEY_5:
- glfwSetCursor(window, standard_cursors[4]);
- break;
-
case GLFW_KEY_6:
- glfwSetCursor(window, standard_cursors[5]);
+ case GLFW_KEY_7:
+ case GLFW_KEY_8:
+ case GLFW_KEY_9:
+ {
+ int index = key - GLFW_KEY_1;
+ if (mods & GLFW_MOD_SHIFT)
+ index += 9;
+
+ if (index < sizeof(standard_cursors) / sizeof(standard_cursors[0]))
+ glfwSetCursor(window, standard_cursors[index]);
+
break;
+ }
case GLFW_KEY_F11:
case GLFW_KEY_ENTER:
@@ -358,17 +354,16 @@ int main(void)
GLFW_ARROW_CURSOR,
GLFW_IBEAM_CURSOR,
GLFW_CROSSHAIR_CURSOR,
- GLFW_HAND_CURSOR,
- GLFW_HRESIZE_CURSOR,
- GLFW_VRESIZE_CURSOR
+ GLFW_POINTING_HAND_CURSOR,
+ GLFW_RESIZE_EW_CURSOR,
+ GLFW_RESIZE_NS_CURSOR,
+ GLFW_RESIZE_NWSE_CURSOR,
+ GLFW_RESIZE_NESW_CURSOR,
+ GLFW_RESIZE_ALL_CURSOR,
+ GLFW_NOT_ALLOWED_CURSOR
};
standard_cursors[i] = glfwCreateStandardCursor(shapes[i]);
- if (!standard_cursors[i])
- {
- glfwTerminate();
- exit(EXIT_FAILURE);
- }
}
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2);