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