diff --git a/cursor.go b/cursor.go index 4781f07a2..35e4fe93a 100644 --- a/cursor.go +++ b/cursor.go @@ -33,10 +33,14 @@ type CursorShapeType = ui.CursorShape // CursorShapeTypes const ( - CursorShapeDefault CursorShapeType = ui.CursorShapeDefault - CursorShapeText CursorShapeType = ui.CursorShapeText - CursorShapeCrosshair CursorShapeType = ui.CursorShapeCrosshair - CursorShapePointer CursorShapeType = ui.CursorShapePointer - CursorShapeEWResize CursorShapeType = ui.CursorShapeEWResize - CursorShapeNSResize CursorShapeType = ui.CursorShapeNSResize + CursorShapeDefault CursorShapeType = CursorShapeType(ui.CursorShapeDefault) + CursorShapeText CursorShapeType = CursorShapeType(ui.CursorShapeText) + CursorShapeCrosshair CursorShapeType = CursorShapeType(ui.CursorShapeCrosshair) + CursorShapePointer CursorShapeType = CursorShapeType(ui.CursorShapePointer) + CursorShapeEWResize CursorShapeType = CursorShapeType(ui.CursorShapeEWResize) + CursorShapeNSResize CursorShapeType = CursorShapeType(ui.CursorShapeNSResize) + CursorShapeNESWResize CursorShapeType = CursorShapeType(ui.CursorShapeNESWResize) + CursorShapeNWSEResize CursorShapeType = CursorShapeType(ui.CursorShapeNWSEResize) + CursorShapeMove CursorShapeType = CursorShapeType(ui.CursorShapeMove) + CursorShapeNotAllowed CursorShapeType = CursorShapeType(ui.CursorShapeNotAllowed) ) diff --git a/examples/cursor/main.go b/examples/cursor/main.go index 3fa95d74d..28c0f9818 100644 --- a/examples/cursor/main.go +++ b/examples/cursor/main.go @@ -33,6 +33,7 @@ const ( type Game struct { grids map[image.Rectangle]ebiten.CursorShapeType gridColors map[image.Rectangle]color.Color + cursor ebiten.CursorShapeType } func (g *Game) Update() error { @@ -44,7 +45,12 @@ func (g *Game) Update() error { break } } - ebiten.SetCursorShape(cursor) + + // Call SetCursorShape only when this is changed to test Ebitengine remembers the current cursor correctly even when it is hidden. + if g.cursor != cursor { + ebiten.SetCursorShape(cursor) + g.cursor = cursor + } if inpututil.IsKeyJustPressed(ebiten.KeyC) { switch ebiten.CursorMode() { @@ -76,6 +82,14 @@ func (g *Game) Draw(screen *ebiten.Image) { ebitenutil.DebugPrint(screen, "CursorShape: EW Resize") case ebiten.CursorShapeNSResize: ebitenutil.DebugPrint(screen, "CursorShape: NS Resize") + case ebiten.CursorShapeNESWResize: + ebitenutil.DebugPrint(screen, "CursorShape: NESW Resize") + case ebiten.CursorShapeNWSEResize: + ebitenutil.DebugPrint(screen, "CursorShape: NWSE Resize") + case ebiten.CursorShapeMove: + ebitenutil.DebugPrint(screen, "CursorShape: Move") + case ebiten.CursorShapeNotAllowed: + ebitenutil.DebugPrint(screen, "CursorShape: Not Allowed") } } @@ -89,18 +103,25 @@ func main() { image.Rect(100, 100, 200, 200): ebiten.CursorShapeDefault, image.Rect(200, 100, 300, 200): ebiten.CursorShapeText, image.Rect(300, 100, 400, 200): ebiten.CursorShapeCrosshair, - image.Rect(100, 200, 200, 300): ebiten.CursorShapePointer, - image.Rect(200, 200, 300, 300): ebiten.CursorShapeEWResize, - image.Rect(300, 200, 400, 300): ebiten.CursorShapeNSResize, + image.Rect(400, 100, 500, 200): ebiten.CursorShapePointer, + image.Rect(100, 200, 200, 300): ebiten.CursorShapeEWResize, + image.Rect(200, 200, 300, 300): ebiten.CursorShapeNSResize, + image.Rect(300, 200, 400, 300): ebiten.CursorShapeNESWResize, + image.Rect(400, 200, 500, 300): ebiten.CursorShapeNWSEResize, + image.Rect(100, 300, 200, 400): ebiten.CursorShapeMove, + image.Rect(200, 300, 300, 400): ebiten.CursorShapeNotAllowed, }, gridColors: map[image.Rectangle]color.Color{}, } for rect, c := range g.grids { clr := color.RGBA{0x40, 0x40, 0x40, 0xff} - if c%2 == 0 { + switch c % 3 { + case 0: clr.R = 0x80 - } else { + case 1: clr.G = 0x80 + case 2: + clr.B = 0x80 } g.gridColors[rect] = clr } diff --git a/internal/cglfw/cocoa_window_darwin.m b/internal/cglfw/cocoa_window_darwin.m index bd314c285..4a72f9f68 100644 --- a/internal/cglfw/cocoa_window_darwin.m +++ b/internal/cglfw/cocoa_window_darwin.m @@ -1728,18 +1728,64 @@ int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, int shape) { @autoreleasepool { - if (shape == GLFW_ARROW_CURSOR) - cursor->ns.object = [NSCursor arrowCursor]; - else if (shape == GLFW_IBEAM_CURSOR) - cursor->ns.object = [NSCursor IBeamCursor]; - else if (shape == GLFW_CROSSHAIR_CURSOR) - cursor->ns.object = [NSCursor crosshairCursor]; - else if (shape == GLFW_HAND_CURSOR) - cursor->ns.object = [NSCursor pointingHandCursor]; - else if (shape == GLFW_HRESIZE_CURSOR) - cursor->ns.object = [NSCursor resizeLeftRightCursor]; - else if (shape == GLFW_VRESIZE_CURSOR) - cursor->ns.object = [NSCursor resizeUpDownCursor]; + SEL cursorSelector = NULL; + + switch (shape) + { + case GLFW_HRESIZE_CURSOR: + cursorSelector = NSSelectorFromString(@"_windowResizeEastWestCursor"); + break; + case GLFW_VRESIZE_CURSOR: + cursorSelector = NSSelectorFromString(@"_windowResizeNorthSouthCursor"); + break; + case GLFW_RESIZE_NWSE_CURSOR: + cursorSelector = NSSelectorFromString(@"_windowResizeNorthWestSouthEastCursor"); + break; + case GLFW_RESIZE_NESW_CURSOR: + cursorSelector = NSSelectorFromString(@"_windowResizeNorthEastSouthWestCursor"); + break; + } + + if (cursorSelector && [NSCursor respondsToSelector:cursorSelector]) + { + id object = [NSCursor performSelector:cursorSelector]; + if ([object isKindOfClass:[NSCursor class]]) + cursor->ns.object = object; + } + + if (!cursor->ns.object) + { + switch (shape) + { + case GLFW_ARROW_CURSOR: + cursor->ns.object = [NSCursor arrowCursor]; + break; + case GLFW_IBEAM_CURSOR: + cursor->ns.object = [NSCursor IBeamCursor]; + break; + case GLFW_CROSSHAIR_CURSOR: + cursor->ns.object = [NSCursor crosshairCursor]; + break; + case GLFW_HAND_CURSOR: + cursor->ns.object = [NSCursor pointingHandCursor]; + break; + case GLFW_RESIZE_ALL_CURSOR: + { + // Use the OS's resource: https://stackoverflow.com/a/21786835/5435443 + NSString *cursorName = @"move"; + NSString *cursorPath = [@"/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/HIServices.framework/Versions/A/Resources/cursors" stringByAppendingPathComponent:cursorName]; + NSImage *image = [[NSImage alloc] initByReferencingFile:[cursorPath stringByAppendingPathComponent:@"cursor.pdf"]]; + NSDictionary *info = [NSDictionary dictionaryWithContentsOfFile:[cursorPath stringByAppendingPathComponent:@"info.plist"]]; + cursor->ns.object = [[NSCursor alloc] initWithImage:image + hotSpot:NSMakePoint([[info valueForKey:@"hotx"] doubleValue], + [[info valueForKey:@"hoty"] doubleValue])]; + break; + } + case GLFW_NOT_ALLOWED_CURSOR: + cursor->ns.object = [NSCursor operationNotAllowedCursor]; + break; + } + } if (!cursor->ns.object) { diff --git a/internal/cglfw/glfw3.h b/internal/cglfw/glfw3.h index c1e4ac676..296c68f81 100644 --- a/internal/cglfw/glfw3.h +++ b/internal/cglfw/glfw3.h @@ -964,6 +964,12 @@ extern "C" { #define GLFW_VRESIZE_CURSOR 0x00036006 /*! @} */ +// Added in GLFW v3.4. +#define GLFW_RESIZE_NWSE_CURSOR 0x00036007 +#define GLFW_RESIZE_NESW_CURSOR 0x00036008 +#define GLFW_RESIZE_ALL_CURSOR 0x00036009 +#define GLFW_NOT_ALLOWED_CURSOR 0x0003600A + #define GLFW_CONNECTED 0x00040001 #define GLFW_DISCONNECTED 0x00040002 diff --git a/internal/cglfw/input.c b/internal/cglfw/input.c index 397c6ec62..ef3199b64 100644 --- a/internal/cglfw/input.c +++ b/internal/cglfw/input.c @@ -470,7 +470,11 @@ GLFWAPI GLFWcursor* glfwCreateStandardCursor(int shape) shape != GLFW_CROSSHAIR_CURSOR && shape != GLFW_HAND_CURSOR && shape != GLFW_HRESIZE_CURSOR && - shape != GLFW_VRESIZE_CURSOR) + shape != GLFW_VRESIZE_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/internal/cglfw/wl_window_linbsd.c b/internal/cglfw/wl_window_linbsd.c index 9e9768d89..6526146e3 100644 --- a/internal/cglfw/wl_window_linbsd.c +++ b/internal/cglfw/wl_window_linbsd.c @@ -1823,28 +1823,6 @@ const struct wl_data_device_listener dataDeviceListener = 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); @@ -2392,26 +2370,101 @@ int _glfwPlatformCreateCursor(_GLFWcursor* cursor, int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, int shape) { - struct wl_cursor* standardCursor; + // See GLFW 3.4 implementation. + const char* name = NULL; - standardCursor = wl_cursor_theme_get_cursor(_glfw.wl.cursorTheme, - translateCursorShape(shape)); - if (!standardCursor) + // Try the XDG names first + switch (shape) { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Wayland: Standard cursor \"%s\" not found", - translateCursorShape(shape)); - return GLFW_FALSE; + case GLFW_ARROW_CURSOR: + name = "default"; + break; + case GLFW_IBEAM_CURSOR: + name = "text"; + break; + case GLFW_CROSSHAIR_CURSOR: + name = "crosshair"; + break; + case GLFW_HAND_CURSOR: + name = "pointer"; + break; + case GLFW_HRESIZE_CURSOR: + name = "ew-resize"; + break; + case GLFW_VRESIZE_CURSOR: + name = "ns-resize"; + break; + case GLFW_RESIZE_NWSE_CURSOR: + name = "nwse-resize"; + break; + case GLFW_RESIZE_NESW_CURSOR: + name = "nesw-resize"; + break; + case GLFW_RESIZE_ALL_CURSOR: + name = "all-scroll"; + break; + case GLFW_NOT_ALLOWED_CURSOR: + name = "not-allowed"; + break; } - 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 + switch (shape) + { + case GLFW_ARROW_CURSOR: + name = "left_ptr"; + break; + case GLFW_IBEAM_CURSOR: + name = "xterm"; + break; + case GLFW_CROSSHAIR_CURSOR: + name = "crosshair"; + break; + case GLFW_HAND_CURSOR: + name = "hand2"; + break; + case GLFW_HRESIZE_CURSOR: + name = "sb_h_double_arrow"; + break; + case GLFW_VRESIZE_CURSOR: + name = "sb_v_double_arrow"; + break; + case GLFW_RESIZE_ALL_CURSOR: + name = "fleur"; + break; + default: + //_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_CURSOR_UNAVAILABLE, + // "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/internal/cglfw/x11_init_linbsd.c b/internal/cglfw/x11_init_linbsd.c index 228771189..b6981666e 100644 --- a/internal/cglfw/x11_init_linbsd.c +++ b/internal/cglfw/x11_init_linbsd.c @@ -741,6 +741,12 @@ static GLFWbool initExtensions(void) _glfw_dlsym(_glfw.x11.xcursor.handle, "XcursorImageDestroy"); _glfw.x11.xcursor.ImageLoadCursor = (PFN_XcursorImageLoadCursor) _glfw_dlsym(_glfw.x11.xcursor.handle, "XcursorImageLoadCursor"); + _glfw.x11.xcursor.GetTheme = (PFN_XcursorGetTheme) + _glfw_dlsym(_glfw.x11.xcursor.handle, "XcursorGetTheme"); + _glfw.x11.xcursor.GetDefaultSize = (PFN_XcursorGetDefaultSize) + _glfw_dlsym(_glfw.x11.xcursor.handle, "XcursorGetDefaultSize"); + _glfw.x11.xcursor.LibraryLoadImage = (PFN_XcursorLibraryLoadImage) + _glfw_dlsym(_glfw.x11.xcursor.handle, "XcursorLibraryLoadImage"); } #if defined(__CYGWIN__) diff --git a/internal/cglfw/x11_platform_linbsd.h b/internal/cglfw/x11_platform_linbsd.h index 70124ee2b..a799400e6 100644 --- a/internal/cglfw/x11_platform_linbsd.h +++ b/internal/cglfw/x11_platform_linbsd.h @@ -87,9 +87,15 @@ typedef int (* PFN_XRRUpdateConfiguration)(XEvent*); typedef XcursorImage* (* PFN_XcursorImageCreate)(int,int); typedef void (* PFN_XcursorImageDestroy)(XcursorImage*); typedef Cursor (* PFN_XcursorImageLoadCursor)(Display*,const XcursorImage*); +typedef char* (* PFN_XcursorGetTheme)(Display*); +typedef int (* PFN_XcursorGetDefaultSize)(Display*); +typedef XcursorImage* (* PFN_XcursorLibraryLoadImage)(const char*,const char*,int); #define XcursorImageCreate _glfw.x11.xcursor.ImageCreate #define XcursorImageDestroy _glfw.x11.xcursor.ImageDestroy #define XcursorImageLoadCursor _glfw.x11.xcursor.ImageLoadCursor +#define XcursorGetTheme _glfw.x11.xcursor.GetTheme +#define XcursorGetDefaultSize _glfw.x11.xcursor.GetDefaultSize +#define XcursorLibraryLoadImage _glfw.x11.xcursor.LibraryLoadImage typedef Bool (* PFN_XineramaIsActive)(Display*); typedef Bool (* PFN_XineramaQueryExtension)(Display*,int*,int*); @@ -327,6 +333,9 @@ typedef struct _GLFWlibraryX11 PFN_XcursorImageCreate ImageCreate; PFN_XcursorImageDestroy ImageDestroy; PFN_XcursorImageLoadCursor ImageLoadCursor; + PFN_XcursorGetTheme GetTheme; + PFN_XcursorGetDefaultSize GetDefaultSize; + PFN_XcursorLibraryLoadImage LibraryLoadImage; } xcursor; struct { diff --git a/internal/cglfw/x11_window_linbsd.c b/internal/cglfw/x11_window_linbsd.c index f41e2452d..e9863518f 100644 --- a/internal/cglfw/x11_window_linbsd.c +++ b/internal/cglfw/x11_window_linbsd.c @@ -2908,29 +2908,98 @@ int _glfwPlatformCreateCursor(_GLFWcursor* cursor, int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, int shape) { - int native = 0; + // See GLFW 3.4 implementation. + 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; + switch (shape) + { + case GLFW_ARROW_CURSOR: + name = "default"; + break; + case GLFW_IBEAM_CURSOR: + name = "text"; + break; + case GLFW_CROSSHAIR_CURSOR: + name = "crosshair"; + break; + case GLFW_HAND_CURSOR: + name = "pointer"; + break; + case GLFW_HRESIZE_CURSOR: + name = "ew-resize"; + break; + case GLFW_VRESIZE_CURSOR: + name = "ns-resize"; + break; + case GLFW_RESIZE_NWSE_CURSOR: + name = "nwse-resize"; + break; + case GLFW_RESIZE_NESW_CURSOR: + name = "nesw-resize"; + break; + case GLFW_RESIZE_ALL_CURSOR: + name = "all-scroll"; + break; + case GLFW_NOT_ALLOWED_CURSOR: + name = "not-allowed"; + break; + } + + XcursorImage* image = XcursorLibraryLoadImage(name, theme, size); + if (image) + { + cursor->x11.handle = XcursorImageLoadCursor(_glfw.x11.display, image); + XcursorImageDestroy(image); + } + } + } - cursor->x11.handle = XCreateFontCursor(_glfw.x11.display, native); if (!cursor->x11.handle) { - _glfwInputError(GLFW_PLATFORM_ERROR, - "X11: Failed to create standard cursor"); - return GLFW_FALSE; + unsigned int native = 0; + + switch (shape) + { + case GLFW_ARROW_CURSOR: + native = XC_left_ptr; + break; + case GLFW_IBEAM_CURSOR: + native = XC_xterm; + break; + case GLFW_CROSSHAIR_CURSOR: + native = XC_crosshair; + break; + case GLFW_HAND_CURSOR: + native = XC_hand2; + break; + case GLFW_HRESIZE_CURSOR: + native = XC_sb_h_double_arrow; + break; + case GLFW_VRESIZE_CURSOR: + native = XC_sb_v_double_arrow; + break; + case GLFW_RESIZE_ALL_CURSOR: + native = XC_fleur; + break; + default: + //_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/internal/glfw/const.go b/internal/glfw/const.go index 41b2dd90b..53abf3ee9 100644 --- a/internal/glfw/const.go +++ b/internal/glfw/const.go @@ -141,4 +141,10 @@ const ( HandCursor = StandardCursor(0x00036004) HResizeCursor = StandardCursor(0x00036005) VResizeCursor = StandardCursor(0x00036006) + + // v3.4 + ResizeNWSECursor = StandardCursor(0x00036007) + ResizeNESWCursor = StandardCursor(0x00036008) + ResizeAllCursor = StandardCursor(0x00036009) + NotAllowedCursor = StandardCursor(0x0003600A) ) diff --git a/internal/goglfw/api_windows.go b/internal/goglfw/api_windows.go index 7a364707a..d510add4a 100644 --- a/internal/goglfw/api_windows.go +++ b/internal/goglfw/api_windows.go @@ -104,8 +104,12 @@ const ( _OCR_CROSS = 32515 _OCR_HAND = 32649 _OCR_IBEAM = 32513 + _OCR_NO = 32648 _OCR_NORMAL = 32512 + _OCR_SIZEALL = 32646 + _OCR_SIZENESW = 32643 _OCR_SIZENS = 32645 + _OCR_SIZENWSE = 32642 _OCR_SIZEWE = 32644 _PM_NOREMOVE = 0x0000 _PM_REMOVE = 0x0001 diff --git a/internal/goglfw/glfw3h.go b/internal/goglfw/glfw3h.go index 5088a6221..52ff80122 100644 --- a/internal/goglfw/glfw3h.go +++ b/internal/goglfw/glfw3h.go @@ -289,6 +289,12 @@ const ( HandCursor StandardCursor = 0x00036004 HResizeCursor StandardCursor = 0x00036005 VResizeCursor StandardCursor = 0x00036006 + + // v3.4 + ResizeNWSECursor StandardCursor = 0x00036007 + ResizeNESWCursor StandardCursor = 0x00036008 + ResizeAllCursor StandardCursor = 0x00036009 + NotAllowedCursor StandardCursor = 0x0003600A ) type Error int diff --git a/internal/goglfw/input.go b/internal/goglfw/input.go index d05b5b348..f57747878 100644 --- a/internal/goglfw/input.go +++ b/internal/goglfw/input.go @@ -351,7 +351,11 @@ func CreateStandardCursor(shape StandardCursor) (*Cursor, error) { shape != CrosshairCursor && shape != HandCursor && shape != HResizeCursor && - shape != VResizeCursor { + shape != VResizeCursor && + shape != ResizeNWSECursor && + shape != ResizeNESWCursor && + shape != ResizeAllCursor && + shape != NotAllowedCursor { return nil, fmt.Errorf("goglfw: invalid standard cursor 0x%08X: %w", shape, InvalidEnum) } diff --git a/internal/goglfw/win32window_windows.go b/internal/goglfw/win32window_windows.go index d0e6f05df..ac14f36ef 100644 --- a/internal/goglfw/win32window_windows.go +++ b/internal/goglfw/win32window_windows.go @@ -2242,6 +2242,14 @@ func (c *Cursor) platformCreateStandardCursor(shape StandardCursor) error { id = _OCR_SIZEWE case VResizeCursor: id = _OCR_SIZENS + case ResizeNWSECursor: // v3.4 + id = _OCR_SIZENWSE + case ResizeNESWCursor: // v3.4 + id = _OCR_SIZENESW + case ResizeAllCursor: // v3.4 + id = _OCR_SIZEALL + case NotAllowedCursor: // v3.4 + id = _OCR_NO default: return fmt.Errorf("goglfw: invalid shape: %d", shape) } diff --git a/internal/ui/ui.go b/internal/ui/ui.go index d00e548a9..84739b134 100644 --- a/internal/ui/ui.go +++ b/internal/ui/ui.go @@ -52,6 +52,10 @@ const ( CursorShapePointer CursorShapeEWResize CursorShapeNSResize + CursorShapeNESWResize + CursorShapeNWSEResize + CursorShapeMove + CursorShapeNotAllowed ) type WindowResizingMode int diff --git a/internal/ui/ui_glfw.go b/internal/ui/ui_glfw.go index b7042cbff..14b162e71 100644 --- a/internal/ui/ui_glfw.go +++ b/internal/ui/ui_glfw.go @@ -201,6 +201,10 @@ func initialize() error { glfwSystemCursors[CursorShapePointer] = glfw.CreateStandardCursor(glfw.HandCursor) glfwSystemCursors[CursorShapeEWResize] = glfw.CreateStandardCursor(glfw.HResizeCursor) glfwSystemCursors[CursorShapeNSResize] = glfw.CreateStandardCursor(glfw.VResizeCursor) + glfwSystemCursors[CursorShapeNESWResize] = glfw.CreateStandardCursor(glfw.ResizeNESWCursor) + glfwSystemCursors[CursorShapeNWSEResize] = glfw.CreateStandardCursor(glfw.ResizeNWSECursor) + glfwSystemCursors[CursorShapeMove] = glfw.CreateStandardCursor(glfw.ResizeAllCursor) + glfwSystemCursors[CursorShapeNotAllowed] = glfw.CreateStandardCursor(glfw.NotAllowedCursor) return nil } @@ -614,7 +618,7 @@ func (u *userInterfaceImpl) SetCursorMode(mode CursorMode) { u.mainThread.Call(func() { u.window.SetInputMode(glfw.CursorMode, driverCursorModeToGLFWCursorMode(mode)) if mode == CursorModeVisible { - u.setNativeCursor(u.getCursorShape()) + u.window.SetCursor(glfwSystemCursors[u.getCursorShape()]) } }) } @@ -632,7 +636,7 @@ func (u *userInterfaceImpl) SetCursorShape(shape CursorShape) { return } u.mainThread.Call(func() { - u.setNativeCursor(shape) + u.window.SetCursor(glfwSystemCursors[shape]) }) } diff --git a/internal/ui/ui_glfw_darwin.go b/internal/ui/ui_glfw_darwin.go index 0604bd135..4f41ed4c5 100644 --- a/internal/ui/ui_glfw_darwin.go +++ b/internal/ui/ui_glfw_darwin.go @@ -218,17 +218,13 @@ var ( var ( sel_alloc = objc.RegisterName("alloc") - sel_arrowCursor = objc.RegisterName("arrowCursor") sel_class = objc.RegisterName("class") sel_collectionBehavior = objc.RegisterName("collectionBehavior") - sel_crosshairCursor = objc.RegisterName("crosshairCursor") sel_delegate = objc.RegisterName("delegate") - sel_IBeamCursor = objc.RegisterName("IBeamCursor") sel_init = objc.RegisterName("init") sel_initWithOrigDelegate = objc.RegisterName("initWithOrigDelegate:") sel_mouseLocation = objc.RegisterName("mouseLocation") sel_performSelector = objc.RegisterName("performSelector:") - sel_pointingHandCursor = objc.RegisterName("pointingHandCursor") sel_set = objc.RegisterName("set") sel_setCollectionBehavior = objc.RegisterName("setCollectionBehavior:") sel_setDelegate = objc.RegisterName("setDelegate:") @@ -242,8 +238,6 @@ var ( sel_windowDidResignKey = objc.RegisterName("windowDidResignKey:") sel_windowDidResize = objc.RegisterName("windowDidResize:") sel_windowDidChangeOcclusionState = objc.RegisterName("windowDidChangeOcclusionState:") - sel_windowResizeEastWestCursor = objc.RegisterName("_windowResizeEastWestCursor") - sel_windowResizeNorthSouthCursor = objc.RegisterName("_windowResizeNorthSouthCursor") sel_windowShouldClose = objc.RegisterName("windowShouldClose:") sel_windowWillEnterFullScreen = objc.RegisterName("windowWillEnterFullScreen:") sel_windowWillExitFullScreen = objc.RegisterName("windowWillExitFullScreen:") @@ -303,26 +297,6 @@ func (u *userInterfaceImpl) isNativeFullscreen() bool { return cocoa.NSWindow{ID: objc.ID(u.window.GetCocoaWindow())}.StyleMask()&cocoa.NSWindowStyleMaskFullScreen != 0 } -func (u *userInterfaceImpl) setNativeCursor(shape CursorShape) { - NSCursor := objc.ID(class_NSCursor).Send(sel_class) - cursor := NSCursor.Send(sel_performSelector, sel_arrowCursor) - switch shape { - case 0: - cursor = NSCursor.Send(sel_performSelector, sel_arrowCursor) - case 1: - cursor = NSCursor.Send(sel_performSelector, sel_IBeamCursor) - case 2: - cursor = NSCursor.Send(sel_performSelector, sel_crosshairCursor) - case 3: - cursor = NSCursor.Send(sel_performSelector, sel_pointingHandCursor) - case 4: - cursor = NSCursor.Send(sel_performSelector, sel_windowResizeEastWestCursor) - case 5: - cursor = NSCursor.Send(sel_performSelector, sel_windowResizeNorthSouthCursor) - } - cursor.Send(sel_set) -} - func (u *userInterfaceImpl) isNativeFullscreenAvailable() bool { // TODO: If the window is transparent, we should use GLFW's windowed fullscreen (#1822, #1857). // However, if the user clicks the green button, should this window be in native fullscreen mode? diff --git a/internal/ui/ui_glfw_unix.go b/internal/ui/ui_glfw_unix.go index a4a0ae6f7..ed60d5422 100644 --- a/internal/ui/ui_glfw_unix.go +++ b/internal/ui/ui_glfw_unix.go @@ -200,11 +200,6 @@ func (u *userInterfaceImpl) isNativeFullscreen() bool { return false } -func (u *userInterfaceImpl) setNativeCursor(shape CursorShape) { - // TODO: Use native API in the future (#1571) - u.window.SetCursor(glfwSystemCursors[shape]) -} - func (u *userInterfaceImpl) isNativeFullscreenAvailable() bool { return false } diff --git a/internal/ui/ui_glfw_windows.go b/internal/ui/ui_glfw_windows.go index 36d9c398c..f569edb25 100644 --- a/internal/ui/ui_glfw_windows.go +++ b/internal/ui/ui_glfw_windows.go @@ -189,11 +189,6 @@ func (u *userInterfaceImpl) isNativeFullscreen() bool { return false } -func (u *userInterfaceImpl) setNativeCursor(shape CursorShape) { - // TODO: Use native API in the future (#1571) - u.window.SetCursor(glfwSystemCursors[shape]) -} - func (u *userInterfaceImpl) isNativeFullscreenAvailable() bool { return false } diff --git a/internal/ui/ui_js.go b/internal/ui/ui_js.go index 8121afb76..689f27d97 100644 --- a/internal/ui/ui_js.go +++ b/internal/ui/ui_js.go @@ -67,6 +67,14 @@ func driverCursorShapeToCSSCursor(cursor CursorShape) string { return "ew-resize" case CursorShapeNSResize: return "ns-resize" + case CursorShapeNESWResize: + return "nesw-resize" + case CursorShapeNWSEResize: + return "nwse-resize" + case CursorShapeMove: + return "move" + case CursorShapeNotAllowed: + return "not-allowed" } return "auto" } diff --git a/run.go b/run.go index b6a845bbd..181796525 100644 --- a/run.go +++ b/run.go @@ -378,6 +378,8 @@ func CursorShape() CursorShapeType { // SetCursorShape sets the cursor shape. // +// If the platform doesn't implement the given shape, the default cursor shape is used. +// // SetCursorShape is concurrent-safe. func SetCursorShape(shape CursorShapeType) { ui.Get().SetCursorShape(shape)