diff options
| author | chai <chaifix@163.com> | 2019-05-11 22:54:56 +0800 | 
|---|---|---|
| committer | chai <chaifix@163.com> | 2019-05-11 22:54:56 +0800 | 
| commit | 9645be0af1b1d5cb0ad5892d5464e1b23c51b550 (patch) | |
| tree | 129c716bed8e93312421c3adb2f8e7c4f811602d /source/3rd-party/SDL2/src/video/cocoa/SDL_cocoamouse.m | |
Diffstat (limited to 'source/3rd-party/SDL2/src/video/cocoa/SDL_cocoamouse.m')
| -rw-r--r-- | source/3rd-party/SDL2/src/video/cocoa/SDL_cocoamouse.m | 477 | 
1 files changed, 477 insertions, 0 deletions
| diff --git a/source/3rd-party/SDL2/src/video/cocoa/SDL_cocoamouse.m b/source/3rd-party/SDL2/src/video/cocoa/SDL_cocoamouse.m new file mode 100644 index 0000000..c9db253 --- /dev/null +++ b/source/3rd-party/SDL2/src/video/cocoa/SDL_cocoamouse.m @@ -0,0 +1,477 @@ +/* +  Simple DirectMedia Layer +  Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org> + +  This software is provided 'as-is', without any express or implied +  warranty.  In no event will the authors be held liable for any damages +  arising from the use of this software. + +  Permission is granted to anyone to use this software for any purpose, +  including commercial applications, and to alter it and redistribute it +  freely, subject to the following restrictions: + +  1. The origin of this software must not be misrepresented; you must not +     claim that you wrote the original software. If you use this software +     in a product, an acknowledgment in the product documentation would be +     appreciated but is not required. +  2. Altered source versions must be plainly marked as such, and must not be +     misrepresented as being the original software. +  3. This notice may not be removed or altered from any source distribution. +*/ +#include "../../SDL_internal.h" + +#if SDL_VIDEO_DRIVER_COCOA + +#include "SDL_assert.h" +#include "SDL_events.h" +#include "SDL_cocoamouse.h" +#include "SDL_cocoamousetap.h" +#include "SDL_cocoavideo.h" + +#include "../../events/SDL_mouse_c.h" + +/* #define DEBUG_COCOAMOUSE */ + +#ifdef DEBUG_COCOAMOUSE +#define DLog(fmt, ...) printf("%s: " fmt "\n", __func__, ##__VA_ARGS__) +#else +#define DLog(...) do { } while (0) +#endif + +@implementation NSCursor (InvisibleCursor) ++ (NSCursor *)invisibleCursor +{ +    static NSCursor *invisibleCursor = NULL; +    if (!invisibleCursor) { +        /* RAW 16x16 transparent GIF */ +        static unsigned char cursorBytes[] = { +            0x47, 0x49, 0x46, 0x38, 0x37, 0x61, 0x10, 0x00, 0x10, 0x00, 0x80, +            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0xF9, 0x04, +            0x01, 0x00, 0x00, 0x01, 0x00, 0x2C, 0x00, 0x00, 0x00, 0x00, 0x10, +            0x00, 0x10, 0x00, 0x00, 0x02, 0x0E, 0x8C, 0x8F, 0xA9, 0xCB, 0xED, +            0x0F, 0xA3, 0x9C, 0xB4, 0xDA, 0x8B, 0xB3, 0x3E, 0x05, 0x00, 0x3B +        }; + +        NSData *cursorData = [NSData dataWithBytesNoCopy:&cursorBytes[0] +                                                  length:sizeof(cursorBytes) +                                            freeWhenDone:NO]; +        NSImage *cursorImage = [[[NSImage alloc] initWithData:cursorData] autorelease]; +        invisibleCursor = [[NSCursor alloc] initWithImage:cursorImage +                                                  hotSpot:NSZeroPoint]; +    } + +    return invisibleCursor; +} +@end + + +static SDL_Cursor * +Cocoa_CreateDefaultCursor() +{ @autoreleasepool +{ +    NSCursor *nscursor; +    SDL_Cursor *cursor = NULL; + +    nscursor = [NSCursor arrowCursor]; + +    if (nscursor) { +        cursor = SDL_calloc(1, sizeof(*cursor)); +        if (cursor) { +            cursor->driverdata = nscursor; +            [nscursor retain]; +        } +    } + +    return cursor; +}} + +static SDL_Cursor * +Cocoa_CreateCursor(SDL_Surface * surface, int hot_x, int hot_y) +{ @autoreleasepool +{ +    NSImage *nsimage; +    NSCursor *nscursor = NULL; +    SDL_Cursor *cursor = NULL; + +    nsimage = Cocoa_CreateImage(surface); +    if (nsimage) { +        nscursor = [[NSCursor alloc] initWithImage: nsimage hotSpot: NSMakePoint(hot_x, hot_y)]; +    } + +    if (nscursor) { +        cursor = SDL_calloc(1, sizeof(*cursor)); +        if (cursor) { +            cursor->driverdata = nscursor; +        } else { +            [nscursor release]; +        } +    } + +    return cursor; +}} + +static SDL_Cursor * +Cocoa_CreateSystemCursor(SDL_SystemCursor id) +{ @autoreleasepool +{ +    NSCursor *nscursor = NULL; +    SDL_Cursor *cursor = NULL; + +    switch(id) { +    case SDL_SYSTEM_CURSOR_ARROW: +        nscursor = [NSCursor arrowCursor]; +        break; +    case SDL_SYSTEM_CURSOR_IBEAM: +        nscursor = [NSCursor IBeamCursor]; +        break; +    case SDL_SYSTEM_CURSOR_WAIT: +        nscursor = [NSCursor arrowCursor]; +        break; +    case SDL_SYSTEM_CURSOR_CROSSHAIR: +        nscursor = [NSCursor crosshairCursor]; +        break; +    case SDL_SYSTEM_CURSOR_WAITARROW: +        nscursor = [NSCursor arrowCursor]; +        break; +    case SDL_SYSTEM_CURSOR_SIZENWSE: +    case SDL_SYSTEM_CURSOR_SIZENESW: +        nscursor = [NSCursor closedHandCursor]; +        break; +    case SDL_SYSTEM_CURSOR_SIZEWE: +        nscursor = [NSCursor resizeLeftRightCursor]; +        break; +    case SDL_SYSTEM_CURSOR_SIZENS: +        nscursor = [NSCursor resizeUpDownCursor]; +        break; +    case SDL_SYSTEM_CURSOR_SIZEALL: +        nscursor = [NSCursor closedHandCursor]; +        break; +    case SDL_SYSTEM_CURSOR_NO: +        nscursor = [NSCursor operationNotAllowedCursor]; +        break; +    case SDL_SYSTEM_CURSOR_HAND: +        nscursor = [NSCursor pointingHandCursor]; +        break; +    default: +        SDL_assert(!"Unknown system cursor"); +        return NULL; +    } + +    if (nscursor) { +        cursor = SDL_calloc(1, sizeof(*cursor)); +        if (cursor) { +            /* We'll free it later, so retain it here */ +            [nscursor retain]; +            cursor->driverdata = nscursor; +        } +    } + +    return cursor; +}} + +static void +Cocoa_FreeCursor(SDL_Cursor * cursor) +{ @autoreleasepool +{ +    NSCursor *nscursor = (NSCursor *)cursor->driverdata; + +    [nscursor release]; +    SDL_free(cursor); +}} + +static int +Cocoa_ShowCursor(SDL_Cursor * cursor) +{ @autoreleasepool +{ +    SDL_VideoDevice *device = SDL_GetVideoDevice(); +    SDL_Window *window = (device ? device->windows : NULL); +    for (; window != NULL; window = window->next) { +        SDL_WindowData *driverdata = (SDL_WindowData *)window->driverdata; +        if (driverdata) { +            [driverdata->nswindow performSelectorOnMainThread:@selector(invalidateCursorRectsForView:) +                                                   withObject:[driverdata->nswindow contentView] +                                                waitUntilDone:NO]; +        } +    } +    return 0; +}} + +static SDL_Window * +SDL_FindWindowAtPoint(const int x, const int y) +{ +    const SDL_Point pt = { x, y }; +    SDL_Window *i; +    for (i = SDL_GetVideoDevice()->windows; i; i = i->next) { +        const SDL_Rect r = { i->x, i->y, i->w, i->h }; +        if (SDL_PointInRect(&pt, &r)) { +            return i; +        } +    } + +    return NULL; +} + +static int +Cocoa_WarpMouseGlobal(int x, int y) +{ +    SDL_Mouse *mouse = SDL_GetMouse(); +    if (mouse->focus) { +        SDL_WindowData *data = (SDL_WindowData *) mouse->focus->driverdata; +        if ([data->listener isMoving]) { +            DLog("Postponing warp, window being moved."); +            [data->listener setPendingMoveX:x Y:y]; +            return 0; +        } +    } +    const CGPoint point = CGPointMake((float)x, (float)y); + +    Cocoa_HandleMouseWarp(point.x, point.y); + +    CGWarpMouseCursorPosition(point); + +    /* CGWarpMouse causes a short delay by default, which is preventable by +     * Calling this directly after. CGSetLocalEventsSuppressionInterval can also +     * prevent it, but it's deprecated as of OS X 10.6. +     */ +    if (!mouse->relative_mode) { +        CGAssociateMouseAndMouseCursorPosition(YES); +    } + +    /* CGWarpMouseCursorPosition doesn't generate a window event, unlike our +     * other implementations' APIs. Send what's appropriate. +     */ +    if (!mouse->relative_mode) { +        SDL_Window *win = SDL_FindWindowAtPoint(x, y); +        SDL_SetMouseFocus(win); +        if (win) { +            SDL_assert(win == mouse->focus); +            SDL_SendMouseMotion(win, mouse->mouseID, 0, x - win->x, y - win->y); +        } +    } + +    return 0; +} + +static void +Cocoa_WarpMouse(SDL_Window * window, int x, int y) +{ +    Cocoa_WarpMouseGlobal(x + window->x, y + window->y); +} + +static int +Cocoa_SetRelativeMouseMode(SDL_bool enabled) +{ +    /* We will re-apply the relative mode when the window gets focus, if it +     * doesn't have focus right now. +     */ +    SDL_Window *window = SDL_GetMouseFocus(); +    if (!window) { +      return 0; +    } + +    /* We will re-apply the relative mode when the window finishes being moved, +     * if it is being moved right now. +     */ +    SDL_WindowData *data = (SDL_WindowData *) window->driverdata; +    if ([data->listener isMoving]) { +        return 0; +    } + +    CGError result; +    if (enabled) { +        DLog("Turning on."); +        result = CGAssociateMouseAndMouseCursorPosition(NO); +    } else { +        DLog("Turning off."); +        result = CGAssociateMouseAndMouseCursorPosition(YES); +    } +    if (result != kCGErrorSuccess) { +        return SDL_SetError("CGAssociateMouseAndMouseCursorPosition() failed"); +    } + +    /* The hide/unhide calls are redundant most of the time, but they fix +     * https://bugzilla.libsdl.org/show_bug.cgi?id=2550 +     */ +    if (enabled) { +        [NSCursor hide]; +    } else { +        [NSCursor unhide]; +    } +    return 0; +} + +static int +Cocoa_CaptureMouse(SDL_Window *window) +{ +    /* our Cocoa event code already tracks the mouse outside the window, +        so all we have to do here is say "okay" and do what we always do. */ +    return 0; +} + +static Uint32 +Cocoa_GetGlobalMouseState(int *x, int *y) +{ +    const NSUInteger cocoaButtons = [NSEvent pressedMouseButtons]; +    const NSPoint cocoaLocation = [NSEvent mouseLocation]; +    Uint32 retval = 0; + +    for (NSScreen *screen in [NSScreen screens]) { +        NSRect frame = [screen frame]; +        if (NSMouseInRect(cocoaLocation, frame, NO)) { +            *x = (int) cocoaLocation.x; +            *y = (int) ((frame.origin.y + frame.size.height) - cocoaLocation.y); +            break; +        } +    } + +    retval |= (cocoaButtons & (1 << 0)) ? SDL_BUTTON_LMASK : 0; +    retval |= (cocoaButtons & (1 << 1)) ? SDL_BUTTON_RMASK : 0; +    retval |= (cocoaButtons & (1 << 2)) ? SDL_BUTTON_MMASK : 0; +    retval |= (cocoaButtons & (1 << 3)) ? SDL_BUTTON_X1MASK : 0; +    retval |= (cocoaButtons & (1 << 4)) ? SDL_BUTTON_X2MASK : 0; + +    return retval; +} + +void +Cocoa_InitMouse(_THIS) +{ +    SDL_Mouse *mouse = SDL_GetMouse(); + +    mouse->driverdata = SDL_calloc(1, sizeof(SDL_MouseData)); + +    mouse->CreateCursor = Cocoa_CreateCursor; +    mouse->CreateSystemCursor = Cocoa_CreateSystemCursor; +    mouse->ShowCursor = Cocoa_ShowCursor; +    mouse->FreeCursor = Cocoa_FreeCursor; +    mouse->WarpMouse = Cocoa_WarpMouse; +    mouse->WarpMouseGlobal = Cocoa_WarpMouseGlobal; +    mouse->SetRelativeMouseMode = Cocoa_SetRelativeMouseMode; +    mouse->CaptureMouse = Cocoa_CaptureMouse; +    mouse->GetGlobalMouseState = Cocoa_GetGlobalMouseState; + +    SDL_SetDefaultCursor(Cocoa_CreateDefaultCursor()); + +    Cocoa_InitMouseEventTap(mouse->driverdata); + +    SDL_MouseData *driverdata = (SDL_MouseData*)mouse->driverdata; +    const NSPoint location =  [NSEvent mouseLocation]; +    driverdata->lastMoveX = location.x; +    driverdata->lastMoveY = location.y; +} + +void +Cocoa_HandleMouseEvent(_THIS, NSEvent *event) +{ +    switch ([event type]) { +        case NSEventTypeMouseMoved: +        case NSEventTypeLeftMouseDragged: +        case NSEventTypeRightMouseDragged: +        case NSEventTypeOtherMouseDragged: +            break; + +        default: +            /* Ignore any other events. */ +            return; +    } + +    SDL_Mouse *mouse = SDL_GetMouse(); +    SDL_MouseData *driverdata = (SDL_MouseData*)mouse->driverdata; +    if (!driverdata) { +        return;  /* can happen when returning from fullscreen Space on shutdown */ +    } + +    const SDL_bool seenWarp = driverdata->seenWarp; +    driverdata->seenWarp = NO; + +    const NSPoint location =  [NSEvent mouseLocation]; +    const CGFloat lastMoveX = driverdata->lastMoveX; +    const CGFloat lastMoveY = driverdata->lastMoveY; +    driverdata->lastMoveX = location.x; +    driverdata->lastMoveY = location.y; +    DLog("Last seen mouse: (%g, %g)", location.x, location.y); + +    /* Non-relative movement is handled in -[Cocoa_WindowListener mouseMoved:] */ +    if (!mouse->relative_mode) { +        return; +    } + +    /* Ignore events that aren't inside the client area (i.e. title bar.) */ +    if ([event window]) { +        NSRect windowRect = [[[event window] contentView] frame]; +        if (!NSMouseInRect([event locationInWindow], windowRect, NO)) { +            return; +        } +    } + +    float deltaX = [event deltaX]; +    float deltaY = [event deltaY]; + +    if (seenWarp) { +        deltaX += (lastMoveX - driverdata->lastWarpX); +        deltaY += ((CGDisplayPixelsHigh(kCGDirectMainDisplay) - lastMoveY) - driverdata->lastWarpY); + +        DLog("Motion was (%g, %g), offset to (%g, %g)", [event deltaX], [event deltaY], deltaX, deltaY); +    } + +    SDL_SendMouseMotion(mouse->focus, mouse->mouseID, 1, (int)deltaX, (int)deltaY); +} + +void +Cocoa_HandleMouseWheel(SDL_Window *window, NSEvent *event) +{ +    SDL_Mouse *mouse = SDL_GetMouse(); + +    CGFloat x = -[event deltaX]; +    CGFloat y = [event deltaY]; +    SDL_MouseWheelDirection direction = SDL_MOUSEWHEEL_NORMAL; + +    if ([event respondsToSelector:@selector(isDirectionInvertedFromDevice)]) { +        if ([event isDirectionInvertedFromDevice] == YES) { +            direction = SDL_MOUSEWHEEL_FLIPPED; +        } +    } + +    if (x > 0) { +        x = SDL_ceil(x); +    } else if (x < 0) { +        x = SDL_floor(x); +    } +    if (y > 0) { +        y = SDL_ceil(y); +    } else if (y < 0) { +        y = SDL_floor(y); +    } +    SDL_SendMouseWheel(window, mouse->mouseID, x, y, direction); +} + +void +Cocoa_HandleMouseWarp(CGFloat x, CGFloat y) +{ +    /* This makes Cocoa_HandleMouseEvent ignore the delta caused by the warp, +     * since it gets included in the next movement event. +     */ +    SDL_MouseData *driverdata = (SDL_MouseData*)SDL_GetMouse()->driverdata; +    driverdata->lastWarpX = x; +    driverdata->lastWarpY = y; +    driverdata->seenWarp = SDL_TRUE; + +    DLog("(%g, %g)", x, y); +} + +void +Cocoa_QuitMouse(_THIS) +{ +    SDL_Mouse *mouse = SDL_GetMouse(); +    if (mouse) { +        if (mouse->driverdata) { +            Cocoa_QuitMouseEventTap(((SDL_MouseData*)mouse->driverdata)); +        } + +        SDL_free(mouse->driverdata); +    } +} + +#endif /* SDL_VIDEO_DRIVER_COCOA */ + +/* vi: set ts=4 sw=4 expandtab: */ | 
