src/video/windows/SDL_windowsmouse.c
author Ryan C. Gordon <icculus@icculus.org>
Fri, 17 Jul 2015 21:03:58 -0400
changeset 9807 57b448735f48
parent 9619 b94b6d0bff0f
child 9998 f67cf37e9cd4
permissions -rw-r--r--
SDL_WarpMouseGlobal() should return non-void.

There are platforms it isn't implemented on (and currently can't be
implemented on!), and there's currently no way for an app to know this.

This shouldn't break ABI on apps that moved to a revision between 2.0.3 and
2.0.4.
slouken@1895
     1
/*
slouken@5535
     2
  Simple DirectMedia Layer
slouken@9619
     3
  Copyright (C) 1997-2015 Sam Lantinga <slouken@libsdl.org>
slouken@1895
     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@1895
     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@1895
    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@1895
    20
*/
icculus@8093
    21
#include "../../SDL_internal.h"
slouken@2710
    22
slouken@6044
    23
#if SDL_VIDEO_DRIVER_WINDOWS
slouken@1895
    24
slouken@5472
    25
#include "SDL_assert.h"
slouken@5062
    26
#include "SDL_windowsvideo.h"
slouken@1895
    27
slouken@5421
    28
#include "../../events/SDL_mouse_c.h"
slouken@5421
    29
slouken@5421
    30
slouken@5421
    31
HCURSOR SDL_cursor = NULL;
slouken@5421
    32
icculus@8942
    33
static int rawInputEnableCount = 0;
icculus@8942
    34
icculus@8942
    35
static int 
icculus@8942
    36
ToggleRawInput(SDL_bool enabled)
icculus@8942
    37
{
icculus@8942
    38
    RAWINPUTDEVICE rawMouse = { 0x01, 0x02, 0, NULL }; /* Mouse: UsagePage = 1, Usage = 2 */
icculus@8942
    39
icculus@8942
    40
    if (enabled) {
icculus@8942
    41
        rawInputEnableCount++;
icculus@8942
    42
        if (rawInputEnableCount > 1) {
icculus@8942
    43
            return 0;  /* already done. */
icculus@8942
    44
        }
icculus@8942
    45
    } else {
icculus@8942
    46
        if (rawInputEnableCount == 0) {
icculus@8942
    47
            return 0;  /* already done. */
icculus@8942
    48
        }
icculus@8942
    49
        rawInputEnableCount--;
icculus@8942
    50
        if (rawInputEnableCount > 0) {
icculus@8942
    51
            return 0;  /* not time to disable yet */
icculus@8942
    52
        }
icculus@8942
    53
    }
icculus@8942
    54
icculus@8942
    55
    if (!enabled) {
icculus@8942
    56
        rawMouse.dwFlags |= RIDEV_REMOVE;
icculus@8942
    57
    }
icculus@8942
    58
icculus@8942
    59
    /* (Un)register raw input for mice */
icculus@8942
    60
    if (RegisterRawInputDevices(&rawMouse, 1, sizeof(RAWINPUTDEVICE)) == FALSE) {
icculus@8942
    61
icculus@8942
    62
        /* Only return an error when registering. If we unregister and fail,
icculus@8942
    63
           then it's probably that we unregistered twice. That's OK. */
icculus@8942
    64
        if (enabled) {
icculus@8942
    65
            return SDL_Unsupported();
icculus@8942
    66
        }
icculus@8942
    67
    }
icculus@8942
    68
    return 0;
icculus@8942
    69
}
icculus@8942
    70
slouken@5421
    71
slouken@5421
    72
static SDL_Cursor *
slouken@5421
    73
WIN_CreateDefaultCursor()
slouken@5421
    74
{
slouken@5421
    75
    SDL_Cursor *cursor;
slouken@5421
    76
slouken@5421
    77
    cursor = SDL_calloc(1, sizeof(*cursor));
slouken@5421
    78
    if (cursor) {
slouken@5421
    79
        cursor->driverdata = LoadCursor(NULL, IDC_ARROW);
slouken@5421
    80
    } else {
slouken@5421
    81
        SDL_OutOfMemory();
slouken@5421
    82
    }
slouken@5421
    83
slouken@5421
    84
    return cursor;
slouken@5421
    85
}
slouken@5421
    86
slouken@5421
    87
static SDL_Cursor *
slouken@5421
    88
WIN_CreateCursor(SDL_Surface * surface, int hot_x, int hot_y)
slouken@5421
    89
{
icculus@7489
    90
    /* msdn says cursor mask has to be padded out to word alignment. Not sure
icculus@7489
    91
        if that means machine word or WORD, but this handles either case. */
icculus@7489
    92
    const size_t pad = (sizeof (size_t) * 8);  /* 32 or 64, or whatever. */
slouken@5421
    93
    SDL_Cursor *cursor;
slouken@5421
    94
    HICON hicon;
slouken@5421
    95
    HDC hdc;
slouken@5421
    96
    BITMAPV4HEADER bmh;
slouken@5421
    97
    LPVOID pixels;
icculus@7489
    98
    LPVOID maskbits;
icculus@7489
    99
    size_t maskbitslen;
slouken@5421
   100
    ICONINFO ii;
slouken@5421
   101
slouken@5421
   102
    SDL_zero(bmh);
slouken@5421
   103
    bmh.bV4Size = sizeof(bmh);
slouken@5421
   104
    bmh.bV4Width = surface->w;
slouken@5421
   105
    bmh.bV4Height = -surface->h; /* Invert the image */
slouken@5421
   106
    bmh.bV4Planes = 1;
slouken@5421
   107
    bmh.bV4BitCount = 32;
slouken@5421
   108
    bmh.bV4V4Compression = BI_BITFIELDS;
slouken@5421
   109
    bmh.bV4AlphaMask = 0xFF000000;
slouken@5421
   110
    bmh.bV4RedMask   = 0x00FF0000;
slouken@5421
   111
    bmh.bV4GreenMask = 0x0000FF00;
slouken@5421
   112
    bmh.bV4BlueMask  = 0x000000FF;
slouken@5421
   113
icculus@7489
   114
    maskbitslen = ((surface->w + (pad - (surface->w % pad))) / 8) * surface->h;
icculus@7489
   115
    maskbits = SDL_stack_alloc(Uint8,maskbitslen);
icculus@7489
   116
    if (maskbits == NULL) {
icculus@7489
   117
        SDL_OutOfMemory();
icculus@7489
   118
        return NULL;
icculus@7489
   119
    }
icculus@7489
   120
icculus@7489
   121
    /* AND the cursor against full bits: no change. We already have alpha. */
icculus@7489
   122
    SDL_memset(maskbits, 0xFF, maskbitslen);
icculus@7489
   123
slouken@5421
   124
    hdc = GetDC(NULL);
slouken@5421
   125
    SDL_zero(ii);
slouken@5421
   126
    ii.fIcon = FALSE;
slouken@5421
   127
    ii.xHotspot = (DWORD)hot_x;
slouken@5421
   128
    ii.yHotspot = (DWORD)hot_y;
slouken@5421
   129
    ii.hbmColor = CreateDIBSection(hdc, (BITMAPINFO*)&bmh, DIB_RGB_COLORS, &pixels, NULL, 0);
icculus@7489
   130
    ii.hbmMask = CreateBitmap(surface->w, surface->h, 1, 1, maskbits);
slouken@5421
   131
    ReleaseDC(NULL, hdc);
icculus@7489
   132
    SDL_stack_free(maskbits);
slouken@5421
   133
slouken@5472
   134
    SDL_assert(surface->format->format == SDL_PIXELFORMAT_ARGB8888);
slouken@5472
   135
    SDL_assert(surface->pitch == surface->w * 4);
slouken@5472
   136
    SDL_memcpy(pixels, surface->pixels, surface->h * surface->pitch);
slouken@5421
   137
slouken@5421
   138
    hicon = CreateIconIndirect(&ii);
slouken@5421
   139
slouken@5421
   140
    DeleteObject(ii.hbmColor);
slouken@5421
   141
    DeleteObject(ii.hbmMask);
slouken@5421
   142
slouken@5421
   143
    if (!hicon) {
slouken@5421
   144
        WIN_SetError("CreateIconIndirect()");
slouken@5421
   145
        return NULL;
slouken@5421
   146
    }
slouken@5421
   147
slouken@5421
   148
    cursor = SDL_calloc(1, sizeof(*cursor));
slouken@5421
   149
    if (cursor) {
slouken@5421
   150
        cursor->driverdata = hicon;
slouken@5421
   151
    } else {
slouken@5421
   152
        DestroyIcon(hicon);
slouken@5421
   153
        SDL_OutOfMemory();
slouken@5421
   154
    }
slouken@5421
   155
slouken@5421
   156
    return cursor;
slouken@5421
   157
}
slouken@5421
   158
mikesart@6675
   159
static SDL_Cursor *
mikesart@6675
   160
WIN_CreateSystemCursor(SDL_SystemCursor id)
mikesart@6675
   161
{
mikesart@6675
   162
    SDL_Cursor *cursor;
mikesart@6675
   163
    LPCTSTR name;
mikesart@6675
   164
mikesart@6675
   165
    switch(id)
mikesart@6675
   166
    {
mikesart@6675
   167
    default:
mikesart@6675
   168
        SDL_assert(0);
mikesart@6675
   169
        return NULL;
mikesart@6675
   170
    case SDL_SYSTEM_CURSOR_ARROW:     name = IDC_ARROW; break;
mikesart@6675
   171
    case SDL_SYSTEM_CURSOR_IBEAM:     name = IDC_IBEAM; break;
mikesart@6675
   172
    case SDL_SYSTEM_CURSOR_WAIT:      name = IDC_WAIT; break;
mikesart@6675
   173
    case SDL_SYSTEM_CURSOR_CROSSHAIR: name = IDC_CROSS; break;
mikesart@6675
   174
    case SDL_SYSTEM_CURSOR_WAITARROW: name = IDC_WAIT; break;
mikesart@6675
   175
    case SDL_SYSTEM_CURSOR_SIZENWSE:  name = IDC_SIZENWSE; break;
mikesart@6675
   176
    case SDL_SYSTEM_CURSOR_SIZENESW:  name = IDC_SIZENESW; break;
mikesart@6675
   177
    case SDL_SYSTEM_CURSOR_SIZEWE:    name = IDC_SIZEWE; break;
mikesart@6675
   178
    case SDL_SYSTEM_CURSOR_SIZENS:    name = IDC_SIZENS; break;
mikesart@6675
   179
    case SDL_SYSTEM_CURSOR_SIZEALL:   name = IDC_SIZEALL; break;
mikesart@6675
   180
    case SDL_SYSTEM_CURSOR_NO:        name = IDC_NO; break;
mikesart@6675
   181
    case SDL_SYSTEM_CURSOR_HAND:      name = IDC_HAND; break;
mikesart@6675
   182
    }
mikesart@6675
   183
mikesart@6675
   184
    cursor = SDL_calloc(1, sizeof(*cursor));
mikesart@6675
   185
    if (cursor) {
mikesart@6675
   186
        HICON hicon;
mikesart@6675
   187
mikesart@6675
   188
        hicon = LoadCursor(NULL, name);
mikesart@6675
   189
mikesart@6675
   190
        cursor->driverdata = hicon;
mikesart@6675
   191
    } else {
mikesart@6675
   192
        SDL_OutOfMemory();
mikesart@6675
   193
    }
mikesart@6675
   194
mikesart@6675
   195
    return cursor;
mikesart@6675
   196
}
mikesart@6675
   197
slouken@5421
   198
static void
slouken@5421
   199
WIN_FreeCursor(SDL_Cursor * cursor)
slouken@5421
   200
{
slouken@5421
   201
    HICON hicon = (HICON)cursor->driverdata;
slouken@5421
   202
slouken@5421
   203
    DestroyIcon(hicon);
slouken@5421
   204
    SDL_free(cursor);
slouken@5421
   205
}
slouken@5421
   206
slouken@5421
   207
static int
slouken@5421
   208
WIN_ShowCursor(SDL_Cursor * cursor)
slouken@5421
   209
{
slouken@5421
   210
    if (cursor) {
slouken@5421
   211
        SDL_cursor = (HCURSOR)cursor->driverdata;
slouken@5421
   212
    } else {
slouken@5421
   213
        SDL_cursor = NULL;
slouken@5421
   214
    }
slouken@5421
   215
    if (SDL_GetMouseFocus() != NULL) {
slouken@5421
   216
        SetCursor(SDL_cursor);
slouken@5421
   217
    }
slouken@5421
   218
    return 0;
slouken@5421
   219
}
slouken@5421
   220
slouken@5421
   221
static void
slouken@5421
   222
WIN_WarpMouse(SDL_Window * window, int x, int y)
slouken@5421
   223
{
slouken@8254
   224
    SDL_WindowData *data = (SDL_WindowData *)window->driverdata;
slouken@8254
   225
    HWND hwnd = data->hwnd;
slouken@5421
   226
    POINT pt;
slouken@5421
   227
slouken@8254
   228
    /* Don't warp the mouse while we're doing a modal interaction */
slouken@8916
   229
    if (data->in_title_click || data->focus_click_pending) {
slouken@8254
   230
        return;
slouken@8254
   231
    }
slouken@8254
   232
slouken@5421
   233
    pt.x = x;
slouken@5421
   234
    pt.y = y;
slouken@5421
   235
    ClientToScreen(hwnd, &pt);
slouken@5421
   236
    SetCursorPos(pt.x, pt.y);
slouken@5421
   237
}
slouken@5421
   238
icculus@9807
   239
static int
slouken@8815
   240
WIN_WarpMouseGlobal(int x, int y)
slouken@8815
   241
{
slouken@8815
   242
    POINT pt;
slouken@8815
   243
slouken@8815
   244
    pt.x = x;
slouken@8815
   245
    pt.y = y;
slouken@8815
   246
    SetCursorPos(pt.x, pt.y);
icculus@9807
   247
    return 0;
slouken@8815
   248
}
slouken@8815
   249
slouken@5421
   250
static int
slouken@5421
   251
WIN_SetRelativeMouseMode(SDL_bool enabled)
slouken@5421
   252
{
icculus@8942
   253
    return ToggleRawInput(enabled);
slouken@5421
   254
}
slouken@5421
   255
icculus@8927
   256
static int
icculus@8927
   257
WIN_CaptureMouse(SDL_Window *window)
icculus@8927
   258
{
icculus@8927
   259
    if (!window) {
icculus@8942
   260
        SDL_Window *focusWin = SDL_GetKeyboardFocus();
icculus@8942
   261
        if (focusWin) {
icculus@8942
   262
            WIN_OnWindowEnter(SDL_GetVideoDevice(), focusWin);  /* make sure WM_MOUSELEAVE messages are (re)enabled. */
icculus@8942
   263
        }
icculus@8927
   264
    }
icculus@8927
   265
icculus@8942
   266
    /* While we were thinking of SetCapture() when designing this API in SDL,
icculus@8942
   267
       we didn't count on the fact that SetCapture() only tracks while the
icculus@8942
   268
       left mouse button is held down! Instead, we listen for raw mouse input
icculus@8942
   269
       and manually query the mouse when it leaves the window. :/ */
icculus@8942
   270
    return ToggleRawInput(window != NULL);
icculus@8927
   271
}
icculus@8927
   272
icculus@8950
   273
static Uint32
icculus@8952
   274
WIN_GetGlobalMouseState(int *x, int *y)
icculus@8950
   275
{
icculus@8950
   276
    Uint32 retval = 0;
icculus@8950
   277
    POINT pt = { 0, 0 };
icculus@8950
   278
    GetCursorPos(&pt);
icculus@8950
   279
    *x = (int) pt.x;
icculus@8950
   280
    *y = (int) pt.y;
icculus@8950
   281
icculus@8950
   282
    retval |= GetAsyncKeyState(VK_LBUTTON) & 0x8000 ? SDL_BUTTON_LMASK : 0;
icculus@8950
   283
    retval |= GetAsyncKeyState(VK_RBUTTON) & 0x8000 ? SDL_BUTTON_RMASK : 0;
icculus@8950
   284
    retval |= GetAsyncKeyState(VK_MBUTTON) & 0x8000 ? SDL_BUTTON_MMASK : 0;
icculus@8956
   285
    retval |= GetAsyncKeyState(VK_XBUTTON1) & 0x8000 ? SDL_BUTTON_X1MASK : 0;
icculus@8956
   286
    retval |= GetAsyncKeyState(VK_XBUTTON2) & 0x8000 ? SDL_BUTTON_X2MASK : 0;
icculus@8950
   287
icculus@8950
   288
    return retval;
icculus@8950
   289
}
icculus@8950
   290
slouken@1895
   291
void
slouken@1895
   292
WIN_InitMouse(_THIS)
slouken@1895
   293
{
slouken@5421
   294
    SDL_Mouse *mouse = SDL_GetMouse();
slouken@5421
   295
slouken@5421
   296
    mouse->CreateCursor = WIN_CreateCursor;
slouken@7191
   297
    mouse->CreateSystemCursor = WIN_CreateSystemCursor;
slouken@5421
   298
    mouse->ShowCursor = WIN_ShowCursor;
slouken@5421
   299
    mouse->FreeCursor = WIN_FreeCursor;
slouken@5421
   300
    mouse->WarpMouse = WIN_WarpMouse;
slouken@8815
   301
    mouse->WarpMouseGlobal = WIN_WarpMouseGlobal;
slouken@5421
   302
    mouse->SetRelativeMouseMode = WIN_SetRelativeMouseMode;
icculus@8927
   303
    mouse->CaptureMouse = WIN_CaptureMouse;
icculus@8952
   304
    mouse->GetGlobalMouseState = WIN_GetGlobalMouseState;
slouken@5421
   305
slouken@5421
   306
    SDL_SetDefaultCursor(WIN_CreateDefaultCursor());
slouken@8066
   307
slouken@8066
   308
    SDL_SetDoubleClickTime(GetDoubleClickTime());
slouken@1895
   309
}
slouken@1895
   310
slouken@1895
   311
void
slouken@1895
   312
WIN_QuitMouse(_THIS)
slouken@1895
   313
{
gabomdq@7103
   314
    SDL_Mouse *mouse = SDL_GetMouse();
gabomdq@7103
   315
    if ( mouse->def_cursor ) {
gabomdq@7103
   316
        SDL_free(mouse->def_cursor);
gabomdq@7103
   317
        mouse->def_cursor = NULL;
gabomdq@7103
   318
        mouse->cur_cursor = NULL;
gabomdq@7103
   319
    }
icculus@8942
   320
icculus@8942
   321
    if (rawInputEnableCount) {  /* force RAWINPUT off here. */
icculus@8942
   322
        rawInputEnableCount = 1;
icculus@8942
   323
        ToggleRawInput(SDL_FALSE);
icculus@8942
   324
    }
slouken@1895
   325
}
slouken@1895
   326
slouken@6044
   327
#endif /* SDL_VIDEO_DRIVER_WINDOWS */
slouken@6044
   328
slouken@1895
   329
/* vi: set ts=4 sw=4 expandtab: */