mirror of
https://github.com/glfw/glfw.git
synced 2024-11-25 03:25:10 +00:00
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:
parent
80fde12fda
commit
7dbdd2e6a5
@ -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)
|
||||||
- Disabled tests and examples by default when built as a CMake subdirectory
|
- 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
|
- Bugfix: The CMake config-file package used an absolute path and was not
|
||||||
relocatable (#1470)
|
relocatable (#1470)
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -9,6 +9,24 @@
|
|||||||
|
|
||||||
@subsection features_34 New features in version 3.4
|
@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
|
@subsubsection features_34_win32_keymenu Support for keyboard access to Windows window menu
|
||||||
|
|
||||||
GLFW now provides the
|
GLFW now provides the
|
||||||
@ -44,6 +62,14 @@ add_subdirectory(path/to/glfw)
|
|||||||
@subsubsection types_34 New types in version 3.4
|
@subsubsection types_34 New types in version 3.4
|
||||||
@subsubsection constants_34 New constants 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
|
- @ref GLFW_WIN32_KEYBOARD_MENU
|
||||||
|
|
||||||
|
|
||||||
|
@ -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
|
||||||
@ -1039,14 +1050,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.
|
||||||
@ -1054,26 +1066,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
|
||||||
@ -4417,19 +4494,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.
|
||||||
|
@ -1603,23 +1603,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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");
|
||||||
}
|
}
|
||||||
|
10
src/input.c
10
src/input.c
@ -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;
|
||||||
|
@ -2068,14 +2068,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,
|
||||||
|
105
src/wl_window.c
105
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 //////
|
////// GLFW platform API //////
|
||||||
//////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////
|
||||||
@ -1233,26 +1211,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;
|
||||||
|
@ -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__)
|
||||||
|
@ -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*);
|
||||||
@ -352,6 +358,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 {
|
||||||
|
@ -2823,29 +2823,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;
|
||||||
|
@ -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);
|
||||||
|
Loading…
Reference in New Issue
Block a user