src/video/cocoa/SDL_cocoamouse.m
author Jørgen P. Tjernø <jorgen@valvesoftware.com>
Tue, 25 Feb 2014 17:27:41 -0800
changeset 8260 028ed8da2524
parent 8232 8976fb30952f
child 8261 841b66e4397a
permissions -rw-r--r--
Mac: Improve moving relative mode windows.

This makes it possible to move windows by their title bar, even if they're in
relative mode, if you click the title bar when the window does not have focus.

Bug: https://bugzilla.libsdl.org/show_bug.cgi?id=2396
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@7114
   226
    {
jorgen@7114
   227
        /* This makes Cocoa_HandleMouseEvent ignore this delta in the next
jorgen@7114
   228
         * movement event.
jorgen@7114
   229
         */
jorgen@7114
   230
        SDL_MouseData *driverdata = (SDL_MouseData*)mouse->driverdata;
jorgen@7114
   231
        NSPoint location =  [NSEvent mouseLocation];
jorgen@7114
   232
        driverdata->deltaXOffset = location.x - point.x;
jorgen@7114
   233
        driverdata->deltaYOffset = point.y - location.y;
jorgen@8260
   234
        DLog("Warp to (%g, %g), offsetting next movement event by (%i, %i)", point.x, point.y, driverdata->deltaXOffset, driverdata->deltaYOffset);
jorgen@7114
   235
    }
jorgen@7114
   236
jorgen@7113
   237
    /* According to the docs, this was deprecated in 10.6, but it's still
jorgen@7113
   238
     * around. The substitute requires a CGEventSource, but I'm not entirely
jorgen@7113
   239
     * sure how we'd procure the right one for this event.
jorgen@7113
   240
     */
jorgen@7113
   241
    CGSetLocalEventsSuppressionInterval(0.0);
slouken@5376
   242
    CGWarpMouseCursorPosition(point);
jorgen@7113
   243
    CGSetLocalEventsSuppressionInterval(0.25);
jorgen@7106
   244
jorgen@7271
   245
    if (!mouse->relative_mode) {
jorgen@7271
   246
        /* CGWarpMouseCursorPosition doesn't generate a window event, unlike our
jorgen@7271
   247
         * other implementations' APIs.
jorgen@7271
   248
         */
jorgen@7271
   249
        SDL_SendMouseMotion(mouse->focus, mouse->mouseID, 0, x, y);
jorgen@7271
   250
    }
slouken@5376
   251
}
slouken@5376
   252
slouken@5406
   253
static int
slouken@5406
   254
Cocoa_SetRelativeMouseMode(SDL_bool enabled)
slouken@5406
   255
{
slouken@5406
   256
    CGError result;
slouken@5406
   257
slouken@5406
   258
    if (enabled) {
jorgen@8260
   259
        DLog("Turning on.");
slouken@5406
   260
        result = CGAssociateMouseAndMouseCursorPosition(NO);
slouken@5406
   261
    } else {
jorgen@8260
   262
        DLog("Turning off.");
slouken@5406
   263
        result = CGAssociateMouseAndMouseCursorPosition(YES);
slouken@5406
   264
    }
slouken@5406
   265
    if (result != kCGErrorSuccess) {
icculus@7037
   266
        return SDL_SetError("CGAssociateMouseAndMouseCursorPosition() failed");
slouken@5406
   267
    }
slouken@5406
   268
    return 0;
slouken@5406
   269
}
slouken@5406
   270
slouken@1931
   271
void
slouken@1931
   272
Cocoa_InitMouse(_THIS)
slouken@1931
   273
{
slouken@5376
   274
    SDL_Mouse *mouse = SDL_GetMouse();
slouken@5376
   275
jorgen@7114
   276
    mouse->driverdata = SDL_calloc(1, sizeof(SDL_MouseData));
jorgen@7114
   277
slouken@5376
   278
    mouse->CreateCursor = Cocoa_CreateCursor;
slouken@6676
   279
    mouse->CreateSystemCursor = Cocoa_CreateSystemCursor;
slouken@5376
   280
    mouse->ShowCursor = Cocoa_ShowCursor;
slouken@5406
   281
    mouse->FreeCursor = Cocoa_FreeCursor;
slouken@5376
   282
    mouse->WarpMouse = Cocoa_WarpMouse;
slouken@5406
   283
    mouse->SetRelativeMouseMode = Cocoa_SetRelativeMouseMode;
slouken@5376
   284
slouken@5405
   285
    SDL_SetDefaultCursor(Cocoa_CreateDefaultCursor());
jorgen@7593
   286
jorgen@7593
   287
    Cocoa_InitMouseEventTap(mouse->driverdata);
slouken@1931
   288
}
slouken@1931
   289
slouken@3517
   290
void
slouken@3517
   291
Cocoa_HandleMouseEvent(_THIS, NSEvent *event)
slouken@3517
   292
{
slouken@5406
   293
    SDL_Mouse *mouse = SDL_GetMouse();
slouken@5406
   294
jorgen@8260
   295
    /* Non-relative movement is handled in -[Cocoa_WindowListener mouseMoved:] */
jorgen@8260
   296
    if (!mouse->relative_mode) {
jorgen@8260
   297
        return;
jorgen@8260
   298
    }
jorgen@7114
   299
jorgen@8260
   300
    switch ([event type])
jorgen@8260
   301
    {
jorgen@8260
   302
        case NSMouseMoved:
jorgen@8260
   303
        case NSLeftMouseDragged:
jorgen@8260
   304
        case NSRightMouseDragged:
jorgen@8260
   305
        case NSOtherMouseDragged:
jorgen@8260
   306
            break;
jorgen@8260
   307
jorgen@8260
   308
        default:
jorgen@8260
   309
            /* Ignore any other events. */
jorgen@8260
   310
            return;
slouken@5406
   311
    }
jorgen@8260
   312
jorgen@8260
   313
    /* Ignore events that aren't inside the client area (i.e. title bar.) */
jorgen@8260
   314
    if ([event window]) {
jorgen@8260
   315
        NSRect windowRect = [[[event window] contentView] frame];
jorgen@8260
   316
        if (!NSPointInRect([event locationInWindow], windowRect)) {
jorgen@8260
   317
            return;
jorgen@8260
   318
        }
jorgen@8260
   319
    }
jorgen@8260
   320
jorgen@8260
   321
    SDL_MouseData *driverdata = (SDL_MouseData*)mouse->driverdata;
jorgen@8260
   322
    float x = [event deltaX] + driverdata->deltaXOffset;
jorgen@8260
   323
    float y = [event deltaY] + driverdata->deltaYOffset;
jorgen@8260
   324
    driverdata->deltaXOffset = driverdata->deltaYOffset = 0;
jorgen@8260
   325
jorgen@8260
   326
    if (driverdata->deltaYOffset > 0 || driverdata->deltaXOffset > 0) {
jorgen@8260
   327
        DLog("Relative move was (%g, %g), offset to (%g, %g)", [event deltaX], [event deltaY], x, y);
jorgen@8260
   328
    }
jorgen@8260
   329
jorgen@8260
   330
    SDL_SendMouseMotion(mouse->focus, mouse->mouseID, 1, (int)x, (int)y);
slouken@3517
   331
}
slouken@3517
   332
slouken@1931
   333
void
gzjjgod@5057
   334
Cocoa_HandleMouseWheel(SDL_Window *window, NSEvent *event)
gzjjgod@5057
   335
{
slouken@6950
   336
    SDL_Mouse *mouse = SDL_GetMouse();
slouken@6950
   337
slouken@8232
   338
    float x = -[event deltaX];
gzjjgod@5057
   339
    float y = [event deltaY];
gzjjgod@5057
   340
gzjjgod@5057
   341
    if (x > 0) {
gzjjgod@5057
   342
        x += 0.9f;
gzjjgod@5057
   343
    } else if (x < 0) {
gzjjgod@5057
   344
        x -= 0.9f;
gzjjgod@5057
   345
    }
gzjjgod@5057
   346
    if (y > 0) {
gzjjgod@5057
   347
        y += 0.9f;
gzjjgod@5057
   348
    } else if (y < 0) {
gzjjgod@5057
   349
        y -= 0.9f;
gzjjgod@5057
   350
    }
slouken@6950
   351
    SDL_SendMouseWheel(window, mouse->mouseID, (int)x, (int)y);
gzjjgod@5057
   352
}
gzjjgod@5057
   353
slouken@5058
   354
void
slouken@5058
   355
Cocoa_QuitMouse(_THIS)
slouken@5058
   356
{
jorgen@7114
   357
    SDL_Mouse *mouse = SDL_GetMouse();
jorgen@7114
   358
    if (mouse) {
jorgen@7593
   359
        if (mouse->driverdata) {
jorgen@7593
   360
            Cocoa_QuitMouseEventTap(((SDL_MouseData*)mouse->driverdata));
jorgen@7593
   361
        }
jorgen@7593
   362
jorgen@7114
   363
        SDL_free(mouse->driverdata);
jorgen@7114
   364
    }
slouken@5058
   365
}
slouken@5058
   366
slouken@6044
   367
#endif /* SDL_VIDEO_DRIVER_COCOA */
slouken@6044
   368
slouken@1931
   369
/* vi: set ts=4 sw=4 expandtab: */