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);