Add more standard cursors

This adds the standard cursors for diagonal and omnidirectional
resize/move and operation-not-allowed.  It also adds new (better?) names
for the horizontal and vertical resize/move and pointing hand cursors.

References:
 - https://developer.apple.com/documentation/appkit/nscursor
 - https://stackoverflow.com/questions/10733228/
 - https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setsystemcursor
 - https://freedesktop.org/wiki/Specifications/cursor-spec/
 - https://tronche.com/gui/x/xlib/appendix/b/

Related to #427.
This commit is contained in:
Camilla Löwy 2019-05-07 20:40:37 +02:00
parent 0ac013381b
commit 02461dc843
13 changed files with 367 additions and 119 deletions

View File

@ -118,6 +118,11 @@ information on what to include when reporting a bug.
## Changelog ## 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)
- [X11] Bugfix: IME input of CJK was broken for "C" locale (#1587,#1636) - [X11] Bugfix: IME input of CJK was broken for "C" locale (#1587,#1636)

View File

@ -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 extension or there is no running compositing manager, the
`GLFW_TRANSPARENT_FRAMEBUFFER` framebuffer hint will have no effect. `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 @section compat_wayland Wayland protocols and IPC standards

View File

@ -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. theme can be can be created with @ref glfwCreateStandardCursor.
@code @code
GLFWcursor* cursor = glfwCreateStandardCursor(GLFW_HRESIZE_CURSOR); GLFWcursor* url_cursor = glfwCreateStandardCursor(GLFW_POINTING_HAND_CURSOR);
@endcode @endcode
These cursor objects behave in the exact same way as those created with @ref 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. 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 @subsubsection cursor_destruction Cursor destruction

View File

@ -757,6 +757,17 @@ extern "C" {
* @analysis Application programmer error. Fix the offending call. * @analysis Application programmer error. Fix the offending call.
*/ */
#define GLFW_NO_WINDOW_CONTEXT 0x0001000A #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 /*! @addtogroup window
@ -1038,14 +1049,15 @@ extern "C" {
/*! @defgroup shapes Standard cursor shapes /*! @defgroup shapes Standard cursor shapes
* @brief Standard system 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 * @ingroup input
* @{ */ * @{ */
/*! @brief The regular arrow cursor shape. /*! @brief The regular arrow cursor shape.
* *
* The regular arrow cursor. * The regular arrow cursor shape.
*/ */
#define GLFW_ARROW_CURSOR 0x00036001 #define GLFW_ARROW_CURSOR 0x00036001
/*! @brief The text input I-beam cursor shape. /*! @brief The text input I-beam cursor shape.
@ -1053,26 +1065,91 @@ extern "C" {
* The text input I-beam cursor shape. * The text input I-beam cursor shape.
*/ */
#define GLFW_IBEAM_CURSOR 0x00036002 #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 #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 #define GLFW_POINTING_HAND_CURSOR 0x00036004
/*! @brief The horizontal resize arrow shape. /*! @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 #define GLFW_RESIZE_EW_CURSOR 0x00036005
/*! @brief The vertical resize arrow shape. /*! @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 #define GLFW_CONNECTED 0x00040001
@ -4424,19 +4501,44 @@ GLFWAPI GLFWcursor* glfwCreateCursor(const GLFWimage* image, int xhot, int yhot)
/*! @brief Creates a cursor with a standard shape. /*! @brief Creates a cursor with a standard shape.
* *
* Returns a cursor with a [standard shape](@ref shapes), that can be set for * Returns a cursor with a standard shape, that can be set for a window with
* a window with @ref glfwSetCursor. * @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). * @param[in] shape One of the [standard shapes](@ref shapes).
* @return A new cursor ready to use or `NULL` if an * @return A new cursor ready to use or `NULL` if an
* [error](@ref error_handling) occurred. * [error](@ref error_handling) occurred.
* *
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref * @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. * @thread_safety This function must only be called from the main thread.
* *
* @sa @ref cursor_object * @sa @ref cursor_standard
* @sa @ref glfwCreateCursor * @sa @ref glfwCreateCursor
* *
* @since Added in version 3.1. * @since Added in version 3.1.

View File

@ -1614,23 +1614,49 @@ int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, int shape)
{ {
@autoreleasepool { @autoreleasepool {
if (shape == GLFW_ARROW_CURSOR) SEL cursorSelector = NULL;
cursor->ns.object = [NSCursor arrowCursor];
else if (shape == GLFW_IBEAM_CURSOR) // HACK: Try to use a private message
cursor->ns.object = [NSCursor IBeamCursor]; if (shape == GLFW_RESIZE_EW_CURSOR)
else if (shape == GLFW_CROSSHAIR_CURSOR) cursorSelector = @selector(_windowResizeEastWestCursor);
cursor->ns.object = [NSCursor crosshairCursor]; else if (shape == GLFW_RESIZE_NS_CURSOR)
else if (shape == GLFW_HAND_CURSOR) cursorSelector = @selector(_windowResizeNorthSouthCursor);
cursor->ns.object = [NSCursor pointingHandCursor]; else if (shape == GLFW_RESIZE_NWSE_CURSOR)
else if (shape == GLFW_HRESIZE_CURSOR) cursorSelector = @selector(_windowResizeNorthWestSouthEastCursor);
cursor->ns.object = [NSCursor resizeLeftRightCursor]; else if (shape == GLFW_RESIZE_NESW_CURSOR)
else if (shape == GLFW_VRESIZE_CURSOR) cursorSelector = @selector(_windowResizeNorthEastSouthWestCursor);
cursor->ns.object = [NSCursor resizeUpDownCursor];
if (cursorSelector && [NSCursor respondsToSelector:cursorSelector])
{
id object = [NSCursor performSelector:cursorSelector];
if ([object isKindOfClass:[NSCursor class]])
cursor->ns.object = object;
}
if (!cursor->ns.object) if (!cursor->ns.object)
{ {
_glfwInputError(GLFW_PLATFORM_ERROR, if (shape == GLFW_ARROW_CURSOR)
"Cocoa: Failed to retrieve standard 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; return GLFW_FALSE;
} }

View File

@ -189,6 +189,8 @@ void _glfwInputError(int code, const char* format, ...)
strcpy(description, "The requested format is unavailable"); strcpy(description, "The requested format is unavailable");
else if (code == GLFW_NO_WINDOW_CONTEXT) else if (code == GLFW_NO_WINDOW_CONTEXT)
strcpy(description, "The specified window has no context"); strcpy(description, "The specified window has no context");
else if (code == GLFW_CURSOR_UNAVAILABLE)
strcpy(description, "The specified cursor shape is unavailable");
else else
strcpy(description, "ERROR: UNKNOWN GLFW ERROR"); strcpy(description, "ERROR: UNKNOWN GLFW ERROR");
} }

View File

@ -757,9 +757,13 @@ GLFWAPI GLFWcursor* glfwCreateStandardCursor(int shape)
if (shape != GLFW_ARROW_CURSOR && if (shape != GLFW_ARROW_CURSOR &&
shape != GLFW_IBEAM_CURSOR && shape != GLFW_IBEAM_CURSOR &&
shape != GLFW_CROSSHAIR_CURSOR && shape != GLFW_CROSSHAIR_CURSOR &&
shape != GLFW_HAND_CURSOR && shape != GLFW_POINTING_HAND_CURSOR &&
shape != GLFW_HRESIZE_CURSOR && shape != GLFW_RESIZE_EW_CURSOR &&
shape != GLFW_VRESIZE_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); _glfwInputError(GLFW_INVALID_ENUM, "Invalid standard cursor 0x%08X", shape);
return NULL; return NULL;

View File

@ -2053,14 +2053,25 @@ int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, int shape)
id = OCR_IBEAM; id = OCR_IBEAM;
else if (shape == GLFW_CROSSHAIR_CURSOR) else if (shape == GLFW_CROSSHAIR_CURSOR)
id = OCR_CROSS; id = OCR_CROSS;
else if (shape == GLFW_HAND_CURSOR) else if (shape == GLFW_POINTING_HAND_CURSOR)
id = OCR_HAND; id = OCR_HAND;
else if (shape == GLFW_HRESIZE_CURSOR) else if (shape == GLFW_RESIZE_EW_CURSOR)
id = OCR_SIZEWE; id = OCR_SIZEWE;
else if (shape == GLFW_VRESIZE_CURSOR) else if (shape == GLFW_RESIZE_NS_CURSOR)
id = OCR_SIZENS; 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 else
{
_glfwInputError(GLFW_PLATFORM_ERROR, "Win32: Unknown standard cursor");
return GLFW_FALSE; return GLFW_FALSE;
}
cursor->win32.handle = LoadImageW(NULL, cursor->win32.handle = LoadImageW(NULL,
MAKEINTRESOURCEW(id), IMAGE_CURSOR, 0, 0, MAKEINTRESOURCEW(id), IMAGE_CURSOR, 0, 0,

View File

@ -891,28 +891,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 ////// ////// GLFW platform API //////
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
@ -1404,26 +1382,79 @@ int _glfwPlatformCreateCursor(_GLFWcursor* cursor,
int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, int shape) int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, int shape)
{ {
struct wl_cursor* standardCursor; const char* name = NULL;
standardCursor = wl_cursor_theme_get_cursor(_glfw.wl.cursorTheme, // Try the XDG names first
translateCursorShape(shape)); if (shape == GLFW_ARROW_CURSOR)
if (!standardCursor) name = "default";
{ else if (shape == GLFW_IBEAM_CURSOR)
_glfwInputError(GLFW_PLATFORM_ERROR, name = "text";
"Wayland: Standard cursor \"%s\" not found", else if (shape == GLFW_CROSSHAIR_CURSOR)
translateCursorShape(shape)); name = "crosshair";
return GLFW_FALSE; 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.cursor = wl_cursor_theme_get_cursor(_glfw.wl.cursorTheme, name);
cursor->wl.currentImage = 0;
if (_glfw.wl.cursorThemeHiDPI) if (_glfw.wl.cursorThemeHiDPI)
{ {
standardCursor = wl_cursor_theme_get_cursor(_glfw.wl.cursorThemeHiDPI, cursor->wl.cursorHiDPI =
translateCursorShape(shape)); wl_cursor_theme_get_cursor(_glfw.wl.cursorThemeHiDPI, name);
cursor->wl.cursorHiDPI = standardCursor; }
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; return GLFW_TRUE;

View File

@ -615,6 +615,12 @@ static GLFWbool initExtensions(void)
_glfw_dlsym(_glfw.x11.xcursor.handle, "XcursorImageDestroy"); _glfw_dlsym(_glfw.x11.xcursor.handle, "XcursorImageDestroy");
_glfw.x11.xcursor.ImageLoadCursor = (PFN_XcursorImageLoadCursor) _glfw.x11.xcursor.ImageLoadCursor = (PFN_XcursorImageLoadCursor)
_glfw_dlsym(_glfw.x11.xcursor.handle, "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__) #if defined(__CYGWIN__)

View File

@ -85,9 +85,15 @@ typedef int (* PFN_XRRUpdateConfiguration)(XEvent*);
typedef XcursorImage* (* PFN_XcursorImageCreate)(int,int); typedef XcursorImage* (* PFN_XcursorImageCreate)(int,int);
typedef void (* PFN_XcursorImageDestroy)(XcursorImage*); typedef void (* PFN_XcursorImageDestroy)(XcursorImage*);
typedef Cursor (* PFN_XcursorImageLoadCursor)(Display*,const 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 XcursorImageCreate _glfw.x11.xcursor.ImageCreate
#define XcursorImageDestroy _glfw.x11.xcursor.ImageDestroy #define XcursorImageDestroy _glfw.x11.xcursor.ImageDestroy
#define XcursorImageLoadCursor _glfw.x11.xcursor.ImageLoadCursor #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_XineramaIsActive)(Display*);
typedef Bool (* PFN_XineramaQueryExtension)(Display*,int*,int*); typedef Bool (* PFN_XineramaQueryExtension)(Display*,int*,int*);
@ -353,6 +359,9 @@ typedef struct _GLFWlibraryX11
PFN_XcursorImageCreate ImageCreate; PFN_XcursorImageCreate ImageCreate;
PFN_XcursorImageDestroy ImageDestroy; PFN_XcursorImageDestroy ImageDestroy;
PFN_XcursorImageLoadCursor ImageLoadCursor; PFN_XcursorImageLoadCursor ImageLoadCursor;
PFN_XcursorGetTheme GetTheme;
PFN_XcursorGetDefaultSize GetDefaultSize;
PFN_XcursorLibraryLoadImage LibraryLoadImage;
} xcursor; } xcursor;
struct { struct {

View File

@ -2905,29 +2905,76 @@ int _glfwPlatformCreateCursor(_GLFWcursor* cursor,
int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, int shape) 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) if (shape == GLFW_ARROW_CURSOR)
native = XC_left_ptr; name = "default";
else if (shape == GLFW_IBEAM_CURSOR) else if (shape == GLFW_IBEAM_CURSOR)
native = XC_xterm; name = "text";
else if (shape == GLFW_CROSSHAIR_CURSOR) else if (shape == GLFW_CROSSHAIR_CURSOR)
native = XC_crosshair; name = "crosshair";
else if (shape == GLFW_HAND_CURSOR) else if (shape == GLFW_POINTING_HAND_CURSOR)
native = XC_hand2; name = "pointer";
else if (shape == GLFW_HRESIZE_CURSOR) else if (shape == GLFW_RESIZE_EW_CURSOR)
native = XC_sb_h_double_arrow; name = "ew-resize";
else if (shape == GLFW_VRESIZE_CURSOR) else if (shape == GLFW_RESIZE_NS_CURSOR)
native = XC_sb_v_double_arrow; name = "ns-resize";
else else if (shape == GLFW_RESIZE_NWSE_CURSOR)
return GLFW_FALSE; 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) if (!cursor->x11.handle)
{ {
_glfwInputError(GLFW_PLATFORM_ERROR, unsigned int native = 0;
"X11: Failed to create standard cursor");
return GLFW_FALSE; 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; return GLFW_TRUE;

View File

@ -69,7 +69,7 @@ static int swap_interval = 1;
static int wait_events = GLFW_TRUE; static int wait_events = GLFW_TRUE;
static int animate_cursor = GLFW_FALSE; static int animate_cursor = GLFW_FALSE;
static int track_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 GLFWcursor* tracking_cursor = NULL;
static void error_callback(int error, const char* description) 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; break;
case GLFW_KEY_1: case GLFW_KEY_1:
glfwSetCursor(window, standard_cursors[0]);
break;
case GLFW_KEY_2: case GLFW_KEY_2:
glfwSetCursor(window, standard_cursors[1]);
break;
case GLFW_KEY_3: case GLFW_KEY_3:
glfwSetCursor(window, standard_cursors[2]);
break;
case GLFW_KEY_4: case GLFW_KEY_4:
glfwSetCursor(window, standard_cursors[3]);
break;
case GLFW_KEY_5: case GLFW_KEY_5:
glfwSetCursor(window, standard_cursors[4]);
break;
case GLFW_KEY_6: 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; break;
}
case GLFW_KEY_F11: case GLFW_KEY_F11:
case GLFW_KEY_ENTER: case GLFW_KEY_ENTER:
@ -358,17 +354,16 @@ int main(void)
GLFW_ARROW_CURSOR, GLFW_ARROW_CURSOR,
GLFW_IBEAM_CURSOR, GLFW_IBEAM_CURSOR,
GLFW_CROSSHAIR_CURSOR, GLFW_CROSSHAIR_CURSOR,
GLFW_HAND_CURSOR, GLFW_POINTING_HAND_CURSOR,
GLFW_HRESIZE_CURSOR, GLFW_RESIZE_EW_CURSOR,
GLFW_VRESIZE_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]); standard_cursors[i] = glfwCreateStandardCursor(shapes[i]);
if (!standard_cursors[i])
{
glfwTerminate();
exit(EXIT_FAILURE);
}
} }
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2); glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2);