Merge branch '3.3-stable' into new-cursors-on-3.3-stable
diff --git a/docs/compat.dox b/docs/compat.dox
index ceeff41..0a5031f 100644
--- a/docs/compat.dox
+++ b/docs/compat.dox
@@ -85,6 +85,13 @@
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 f5f5cac..495edb2 100644
--- a/docs/input.dox
+++ b/docs/input.dox
@@ -390,12 +390,15 @@
theme 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/include/GLFW/glfw3.h b/include/GLFW/glfw3.h
index c92b768..24270df 100644
--- a/include/GLFW/glfw3.h
+++ b/include/GLFW/glfw3.h
@@ -787,6 +787,17 @@
* @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
@@ -1071,14 +1082,15 @@
/*! @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.
@@ -1086,26 +1098,91 @@
* 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
@@ -4472,19 +4549,44 @@
/*! @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 | Yes<sup>1</sup> | Maybe<sup>2</sup> | Maybe<sup>2</sup>
+ * @ref GLFW_RESIZE_NESW_CURSOR | Yes | Yes<sup>1</sup> | Maybe<sup>2</sup> | Maybe<sup>2</sup>
+ * @ref GLFW_RESIZE_ALL_CURSOR | Yes | Yes | Yes | Yes
+ * @ref GLFW_NOT_ALLOWED_CURSOR | Yes | Yes | Maybe<sup>2</sup> | Maybe<sup>2</sup>
+ *
+ * 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 2d065c3..057604b 100644
--- a/src/cocoa_window.m
+++ b/src/cocoa_window.m
@@ -1733,23 +1733,49 @@
{
@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 b1af8e5..a11db4a 100644
--- a/src/init.c
+++ b/src/init.c
@@ -284,6 +284,8 @@
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 0ae27ee..11a5246 100644
--- a/src/input.c
+++ b/src/input.c
@@ -786,9 +786,13 @@
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 3c22ab1..a10f4ce 100644
--- a/src/win32_window.c
+++ b/src/win32_window.c
@@ -2227,14 +2227,25 @@
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 985a571..2e5f386 100644
--- a/src/wl_window.c
+++ b/src/wl_window.c
@@ -2027,28 +2027,6 @@
dataDeviceHandleSelection,
};
-// 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;
-}
-
void _glfwAddSeatListenerWayland(struct wl_seat* seat)
{
wl_seat_add_listener(seat, &seatListener, NULL);
@@ -2660,26 +2638,79 @@
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 6049904..ca8ffcd 100644
--- a/src/x11_init.c
+++ b/src/x11_init.c
@@ -735,6 +735,12 @@
_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 03ff9d2..01808a7 100644
--- a/src/x11_platform.h
+++ b/src/x11_platform.h
@@ -85,9 +85,15 @@
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*);
@@ -356,6 +362,9 @@
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 9bd29e3..3dd152b 100644
--- a/src/x11_window.c
+++ b/src/x11_window.c
@@ -2973,29 +2973,76 @@
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";
- cursor->x11.handle = XCreateFontCursor(_glfw.x11.display, native);
+ XcursorImage* image = XcursorLibraryLoadImage(name, theme, size);
+ if (image)
+ {
+ cursor->x11.handle = XcursorImageLoadCursor(_glfw.x11.display, image);
+ XcursorImageDestroy(image);
+ }
+ }
+ }
+
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 b6288f6..f72c0f9 100644
--- a/tests/cursor.c
+++ b/tests/cursor.c
@@ -69,7 +69,7 @@
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 @@
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 @@
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);