src/video/windows/SDL_windowsmouse.c
author Ozkan Sezer <sezeroz@gmail.com>
Mon, 21 Oct 2019 22:22:28 +0300
changeset 13155 faed1bb24abb
parent 12503 806492103856
permissions -rw-r--r--
SDL_endian.h: Use endian.h for OpenBSD.

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