cscg22-gearboy

CSCG 2022 Challenge 'Gearboy'
git clone https://git.sinitax.com/sinitax/cscg22-gearboy
Log | Files | Refs | sfeed.txt

SDL_cocoamouse.m (13087B)


      1/*
      2  Simple DirectMedia Layer
      3  Copyright (C) 1997-2014 Sam Lantinga <slouken@libsdl.org>
      4
      5  This software is provided 'as-is', without any express or implied
      6  warranty.  In no event will the authors be held liable for any damages
      7  arising from the use of this software.
      8
      9  Permission is granted to anyone to use this software for any purpose,
     10  including commercial applications, and to alter it and redistribute it
     11  freely, subject to the following restrictions:
     12
     13  1. The origin of this software must not be misrepresented; you must not
     14     claim that you wrote the original software. If you use this software
     15     in a product, an acknowledgment in the product documentation would be
     16     appreciated but is not required.
     17  2. Altered source versions must be plainly marked as such, and must not be
     18     misrepresented as being the original software.
     19  3. This notice may not be removed or altered from any source distribution.
     20*/
     21#include "../../SDL_internal.h"
     22
     23#if SDL_VIDEO_DRIVER_COCOA
     24
     25#include "SDL_assert.h"
     26#include "SDL_events.h"
     27#include "SDL_cocoamouse.h"
     28#include "SDL_cocoamousetap.h"
     29
     30#include "../../events/SDL_mouse_c.h"
     31
     32/* #define DEBUG_COCOAMOUSE */
     33
     34#ifdef DEBUG_COCOAMOUSE
     35#define DLog(fmt, ...) printf("%s: " fmt "\n", __func__, ##__VA_ARGS__)
     36#else
     37#define DLog(...) do { } while (0)
     38#endif
     39
     40@implementation NSCursor (InvisibleCursor)
     41+ (NSCursor *)invisibleCursor
     42{
     43    static NSCursor *invisibleCursor = NULL;
     44    if (!invisibleCursor) {
     45        /* RAW 16x16 transparent GIF */
     46        static unsigned char cursorBytes[] = {
     47            0x47, 0x49, 0x46, 0x38, 0x37, 0x61, 0x10, 0x00, 0x10, 0x00, 0x80,
     48            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0xF9, 0x04,
     49            0x01, 0x00, 0x00, 0x01, 0x00, 0x2C, 0x00, 0x00, 0x00, 0x00, 0x10,
     50            0x00, 0x10, 0x00, 0x00, 0x02, 0x0E, 0x8C, 0x8F, 0xA9, 0xCB, 0xED,
     51            0x0F, 0xA3, 0x9C, 0xB4, 0xDA, 0x8B, 0xB3, 0x3E, 0x05, 0x00, 0x3B
     52        };
     53
     54        NSData *cursorData = [NSData dataWithBytesNoCopy:&cursorBytes[0]
     55                                                  length:sizeof(cursorBytes)
     56                                            freeWhenDone:NO];
     57        NSImage *cursorImage = [[[NSImage alloc] initWithData:cursorData] autorelease];
     58        invisibleCursor = [[NSCursor alloc] initWithImage:cursorImage
     59                                                  hotSpot:NSZeroPoint];
     60    }
     61
     62    return invisibleCursor;
     63}
     64@end
     65
     66
     67static SDL_Cursor *
     68Cocoa_CreateDefaultCursor()
     69{ @autoreleasepool
     70{
     71    NSCursor *nscursor;
     72    SDL_Cursor *cursor = NULL;
     73
     74    nscursor = [NSCursor arrowCursor];
     75
     76    if (nscursor) {
     77        cursor = SDL_calloc(1, sizeof(*cursor));
     78        if (cursor) {
     79            cursor->driverdata = nscursor;
     80            [nscursor retain];
     81        }
     82    }
     83
     84    return cursor;
     85}}
     86
     87static SDL_Cursor *
     88Cocoa_CreateCursor(SDL_Surface * surface, int hot_x, int hot_y)
     89{ @autoreleasepool
     90{
     91    NSImage *nsimage;
     92    NSCursor *nscursor = NULL;
     93    SDL_Cursor *cursor = NULL;
     94
     95    nsimage = Cocoa_CreateImage(surface);
     96    if (nsimage) {
     97        nscursor = [[NSCursor alloc] initWithImage: nsimage hotSpot: NSMakePoint(hot_x, hot_y)];
     98    }
     99
    100    if (nscursor) {
    101        cursor = SDL_calloc(1, sizeof(*cursor));
    102        if (cursor) {
    103            cursor->driverdata = nscursor;
    104        } else {
    105            [nscursor release];
    106        }
    107    }
    108
    109    return cursor;
    110}}
    111
    112static SDL_Cursor *
    113Cocoa_CreateSystemCursor(SDL_SystemCursor id)
    114{ @autoreleasepool
    115{
    116    NSCursor *nscursor = NULL;
    117    SDL_Cursor *cursor = NULL;
    118
    119    switch(id) {
    120    case SDL_SYSTEM_CURSOR_ARROW:
    121        nscursor = [NSCursor arrowCursor];
    122        break;
    123    case SDL_SYSTEM_CURSOR_IBEAM:
    124        nscursor = [NSCursor IBeamCursor];
    125        break;
    126    case SDL_SYSTEM_CURSOR_WAIT:
    127        nscursor = [NSCursor arrowCursor];
    128        break;
    129    case SDL_SYSTEM_CURSOR_CROSSHAIR:
    130        nscursor = [NSCursor crosshairCursor];
    131        break;
    132    case SDL_SYSTEM_CURSOR_WAITARROW:
    133        nscursor = [NSCursor arrowCursor];
    134        break;
    135    case SDL_SYSTEM_CURSOR_SIZENWSE:
    136    case SDL_SYSTEM_CURSOR_SIZENESW:
    137        nscursor = [NSCursor closedHandCursor];
    138        break;
    139    case SDL_SYSTEM_CURSOR_SIZEWE:
    140        nscursor = [NSCursor resizeLeftRightCursor];
    141        break;
    142    case SDL_SYSTEM_CURSOR_SIZENS:
    143        nscursor = [NSCursor resizeUpDownCursor];
    144        break;
    145    case SDL_SYSTEM_CURSOR_SIZEALL:
    146        nscursor = [NSCursor closedHandCursor];
    147        break;
    148    case SDL_SYSTEM_CURSOR_NO:
    149        nscursor = [NSCursor operationNotAllowedCursor];
    150        break;
    151    case SDL_SYSTEM_CURSOR_HAND:
    152        nscursor = [NSCursor pointingHandCursor];
    153        break;
    154    default:
    155        SDL_assert(!"Unknown system cursor");
    156        return NULL;
    157    }
    158
    159    if (nscursor) {
    160        cursor = SDL_calloc(1, sizeof(*cursor));
    161        if (cursor) {
    162            /* We'll free it later, so retain it here */
    163            [nscursor retain];
    164            cursor->driverdata = nscursor;
    165        }
    166    }
    167
    168    return cursor;
    169}}
    170
    171static void
    172Cocoa_FreeCursor(SDL_Cursor * cursor)
    173{ @autoreleasepool
    174{
    175    NSCursor *nscursor = (NSCursor *)cursor->driverdata;
    176
    177    [nscursor release];
    178    SDL_free(cursor);
    179}}
    180
    181static int
    182Cocoa_ShowCursor(SDL_Cursor * cursor)
    183{ @autoreleasepool
    184{
    185    SDL_VideoDevice *device = SDL_GetVideoDevice();
    186    SDL_Window *window = (device ? device->windows : NULL);
    187    for (; window != NULL; window = window->next) {
    188        SDL_WindowData *driverdata = (SDL_WindowData *)window->driverdata;
    189        if (driverdata) {
    190            [driverdata->nswindow performSelectorOnMainThread:@selector(invalidateCursorRectsForView:)
    191                                                   withObject:[driverdata->nswindow contentView]
    192                                                waitUntilDone:NO];
    193        }
    194    }
    195    return 0;
    196}}
    197
    198static void
    199Cocoa_WarpMouseGlobal(int x, int y)
    200{
    201    SDL_Mouse *mouse = SDL_GetMouse();
    202    if (mouse->focus) {
    203        SDL_WindowData *data = (SDL_WindowData *) mouse->focus->driverdata;
    204        if ([data->listener isMoving]) {
    205            DLog("Postponing warp, window being moved.");
    206            [data->listener setPendingMoveX:x Y:y];
    207            return;
    208        }
    209    }
    210    CGPoint point = CGPointMake((float)x, (float)y);
    211
    212    Cocoa_HandleMouseWarp(point.x, point.y);
    213
    214    /* According to the docs, this was deprecated in 10.6, but it's still
    215     * around. The substitute requires a CGEventSource, but I'm not entirely
    216     * sure how we'd procure the right one for this event.
    217     */
    218    CGSetLocalEventsSuppressionInterval(0.0);
    219    CGWarpMouseCursorPosition(point);
    220    CGSetLocalEventsSuppressionInterval(0.25);
    221
    222    if (!mouse->relative_mode && mouse->focus) {
    223        /* CGWarpMouseCursorPosition doesn't generate a window event, unlike our
    224         * other implementations' APIs.
    225         */
    226        SDL_SendMouseMotion(mouse->focus, mouse->mouseID, 0, x - mouse->focus->x, y - mouse->focus->y);
    227    }
    228}
    229
    230static void
    231Cocoa_WarpMouse(SDL_Window * window, int x, int y)
    232{
    233    Cocoa_WarpMouseGlobal(x + window->x, y + window->y);
    234}
    235
    236static int
    237Cocoa_SetRelativeMouseMode(SDL_bool enabled)
    238{
    239    /* We will re-apply the relative mode when the window gets focus, if it
    240     * doesn't have focus right now.
    241     */
    242    SDL_Window *window = SDL_GetMouseFocus();
    243    if (!window) {
    244      return 0;
    245    }
    246
    247    /* We will re-apply the relative mode when the window finishes being moved,
    248     * if it is being moved right now.
    249     */
    250    SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
    251    if ([data->listener isMoving]) {
    252        return 0;
    253    }
    254
    255    CGError result;
    256    if (enabled) {
    257        DLog("Turning on.");
    258        result = CGAssociateMouseAndMouseCursorPosition(NO);
    259    } else {
    260        DLog("Turning off.");
    261        result = CGAssociateMouseAndMouseCursorPosition(YES);
    262    }
    263    if (result != kCGErrorSuccess) {
    264        return SDL_SetError("CGAssociateMouseAndMouseCursorPosition() failed");
    265    }
    266
    267    /* The hide/unhide calls are redundant most of the time, but they fix
    268     * https://bugzilla.libsdl.org/show_bug.cgi?id=2550
    269     */
    270    if (enabled) {
    271        [NSCursor hide];
    272    } else {
    273        [NSCursor unhide];
    274    }
    275    return 0;
    276}
    277
    278static int
    279Cocoa_CaptureMouse(SDL_Window *window)
    280{
    281    /* our Cocoa event code already tracks the mouse outside the window,
    282        so all we have to do here is say "okay" and do what we always do. */
    283    return 0;
    284}
    285
    286static Uint32
    287Cocoa_GetGlobalMouseState(int *x, int *y)
    288{
    289    const NSUInteger cocoaButtons = [NSEvent pressedMouseButtons];
    290    const NSPoint cocoaLocation = [NSEvent mouseLocation];
    291    Uint32 retval = 0;
    292
    293    for (NSScreen *screen in [NSScreen screens]) {
    294        NSRect frame = [screen frame];
    295        if (NSPointInRect(cocoaLocation, frame)) {
    296            *x = (int) cocoaLocation.x;
    297            *y = (int) ((frame.origin.y + frame.size.height) - cocoaLocation.y);
    298            break;
    299        }
    300    }
    301
    302    retval |= (cocoaButtons & (1 << 0)) ? SDL_BUTTON_LMASK : 0;
    303    retval |= (cocoaButtons & (1 << 1)) ? SDL_BUTTON_RMASK : 0;
    304    retval |= (cocoaButtons & (1 << 2)) ? SDL_BUTTON_MMASK : 0;
    305    retval |= (cocoaButtons & (1 << 3)) ? SDL_BUTTON_X1MASK : 0;
    306    retval |= (cocoaButtons & (1 << 4)) ? SDL_BUTTON_X2MASK : 0;
    307
    308    return retval;
    309}
    310
    311void
    312Cocoa_InitMouse(_THIS)
    313{
    314    SDL_Mouse *mouse = SDL_GetMouse();
    315
    316    mouse->driverdata = SDL_calloc(1, sizeof(SDL_MouseData));
    317
    318    mouse->CreateCursor = Cocoa_CreateCursor;
    319    mouse->CreateSystemCursor = Cocoa_CreateSystemCursor;
    320    mouse->ShowCursor = Cocoa_ShowCursor;
    321    mouse->FreeCursor = Cocoa_FreeCursor;
    322    mouse->WarpMouse = Cocoa_WarpMouse;
    323    mouse->WarpMouseGlobal = Cocoa_WarpMouseGlobal;
    324    mouse->SetRelativeMouseMode = Cocoa_SetRelativeMouseMode;
    325    mouse->CaptureMouse = Cocoa_CaptureMouse;
    326    mouse->GetGlobalMouseState = Cocoa_GetGlobalMouseState;
    327
    328    SDL_SetDefaultCursor(Cocoa_CreateDefaultCursor());
    329
    330    Cocoa_InitMouseEventTap(mouse->driverdata);
    331
    332    SDL_MouseData *driverdata = (SDL_MouseData*)mouse->driverdata;
    333    const NSPoint location =  [NSEvent mouseLocation];
    334    driverdata->lastMoveX = location.x;
    335    driverdata->lastMoveY = location.y;
    336}
    337
    338void
    339Cocoa_HandleMouseEvent(_THIS, NSEvent *event)
    340{
    341    switch ([event type]) {
    342        case NSMouseMoved:
    343        case NSLeftMouseDragged:
    344        case NSRightMouseDragged:
    345        case NSOtherMouseDragged:
    346            break;
    347
    348        default:
    349            /* Ignore any other events. */
    350            return;
    351    }
    352
    353    SDL_Mouse *mouse = SDL_GetMouse();
    354    SDL_MouseData *driverdata = (SDL_MouseData*)mouse->driverdata;
    355    if (!driverdata) {
    356        return;  /* can happen when returning from fullscreen Space on shutdown */
    357    }
    358
    359    const SDL_bool seenWarp = driverdata->seenWarp;
    360    driverdata->seenWarp = NO;
    361
    362    const NSPoint location =  [NSEvent mouseLocation];
    363    const CGFloat lastMoveX = driverdata->lastMoveX;
    364    const CGFloat lastMoveY = driverdata->lastMoveY;
    365    driverdata->lastMoveX = location.x;
    366    driverdata->lastMoveY = location.y;
    367    DLog("Last seen mouse: (%g, %g)", location.x, location.y);
    368
    369    /* Non-relative movement is handled in -[Cocoa_WindowListener mouseMoved:] */
    370    if (!mouse->relative_mode) {
    371        return;
    372    }
    373
    374    /* Ignore events that aren't inside the client area (i.e. title bar.) */
    375    if ([event window]) {
    376        NSRect windowRect = [[[event window] contentView] frame];
    377        if (!NSPointInRect([event locationInWindow], windowRect)) {
    378            return;
    379        }
    380    }
    381
    382    float deltaX = [event deltaX];
    383    float deltaY = [event deltaY];
    384
    385    if (seenWarp) {
    386        deltaX += (lastMoveX - driverdata->lastWarpX);
    387        deltaY += ((CGDisplayPixelsHigh(kCGDirectMainDisplay) - lastMoveY) - driverdata->lastWarpY);
    388
    389        DLog("Motion was (%g, %g), offset to (%g, %g)", [event deltaX], [event deltaY], deltaX, deltaY);
    390    }
    391
    392    SDL_SendMouseMotion(mouse->focus, mouse->mouseID, 1, (int)deltaX, (int)deltaY);
    393}
    394
    395void
    396Cocoa_HandleMouseWheel(SDL_Window *window, NSEvent *event)
    397{
    398    SDL_Mouse *mouse = SDL_GetMouse();
    399
    400    float x = -[event deltaX];
    401    float y = [event deltaY];
    402
    403    if (x > 0) {
    404        x += 0.9f;
    405    } else if (x < 0) {
    406        x -= 0.9f;
    407    }
    408    if (y > 0) {
    409        y += 0.9f;
    410    } else if (y < 0) {
    411        y -= 0.9f;
    412    }
    413    SDL_SendMouseWheel(window, mouse->mouseID, (int)x, (int)y);
    414}
    415
    416void
    417Cocoa_HandleMouseWarp(CGFloat x, CGFloat y)
    418{
    419    /* This makes Cocoa_HandleMouseEvent ignore the delta caused by the warp,
    420     * since it gets included in the next movement event.
    421     */
    422    SDL_MouseData *driverdata = (SDL_MouseData*)SDL_GetMouse()->driverdata;
    423    driverdata->lastWarpX = x;
    424    driverdata->lastWarpY = y;
    425    driverdata->seenWarp = SDL_TRUE;
    426
    427    DLog("(%g, %g)", x, y);
    428}
    429
    430void
    431Cocoa_QuitMouse(_THIS)
    432{
    433    SDL_Mouse *mouse = SDL_GetMouse();
    434    if (mouse) {
    435        if (mouse->driverdata) {
    436            Cocoa_QuitMouseEventTap(((SDL_MouseData*)mouse->driverdata));
    437        }
    438
    439        SDL_free(mouse->driverdata);
    440    }
    441}
    442
    443#endif /* SDL_VIDEO_DRIVER_COCOA */
    444
    445/* vi: set ts=4 sw=4 expandtab: */