src/video/windows/SDL_windowsmouse.c
author Sam Lantinga <slouken@libsdl.org>
Fri, 18 May 2018 13:09:30 -0700
changeset 11983 3a50eb90e4b2
parent 11811 5d94cb6b24d3
child 12188 f081a5675f93
child 12349 a67dedb293c8
permissions -rw-r--r--
Merged latest changes from Steam Link app
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2018 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     ICONINFO ii;
   101 
   102     SDL_zero(bmh);
   103     bmh.bV4Size = sizeof(bmh);
   104     bmh.bV4Width = surface->w;
   105     bmh.bV4Height = -surface->h; /* Invert the image */
   106     bmh.bV4Planes = 1;
   107     bmh.bV4BitCount = 32;
   108     bmh.bV4V4Compression = BI_BITFIELDS;
   109     bmh.bV4AlphaMask = 0xFF000000;
   110     bmh.bV4RedMask   = 0x00FF0000;
   111     bmh.bV4GreenMask = 0x0000FF00;
   112     bmh.bV4BlueMask  = 0x000000FF;
   113 
   114     maskbitslen = ((surface->w + (pad - (surface->w % pad))) / 8) * surface->h;
   115     maskbits = SDL_stack_alloc(Uint8,maskbitslen);
   116     if (maskbits == NULL) {
   117         SDL_OutOfMemory();
   118         return NULL;
   119     }
   120 
   121     /* AND the cursor against full bits: no change. We already have alpha. */
   122     SDL_memset(maskbits, 0xFF, maskbitslen);
   123 
   124     hdc = GetDC(NULL);
   125     SDL_zero(ii);
   126     ii.fIcon = FALSE;
   127     ii.xHotspot = (DWORD)hot_x;
   128     ii.yHotspot = (DWORD)hot_y;
   129     ii.hbmColor = CreateDIBSection(hdc, (BITMAPINFO*)&bmh, DIB_RGB_COLORS, &pixels, NULL, 0);
   130     ii.hbmMask = CreateBitmap(surface->w, surface->h, 1, 1, maskbits);
   131     ReleaseDC(NULL, hdc);
   132     SDL_stack_free(maskbits);
   133 
   134     SDL_assert(surface->format->format == SDL_PIXELFORMAT_ARGB8888);
   135     SDL_assert(surface->pitch == surface->w * 4);
   136     SDL_memcpy(pixels, surface->pixels, surface->h * surface->pitch);
   137 
   138     hicon = CreateIconIndirect(&ii);
   139 
   140     DeleteObject(ii.hbmColor);
   141     DeleteObject(ii.hbmMask);
   142 
   143     if (!hicon) {
   144         WIN_SetError("CreateIconIndirect()");
   145         return NULL;
   146     }
   147 
   148     cursor = SDL_calloc(1, sizeof(*cursor));
   149     if (cursor) {
   150         cursor->driverdata = hicon;
   151     } else {
   152         DestroyIcon(hicon);
   153         SDL_OutOfMemory();
   154     }
   155 
   156     return cursor;
   157 }
   158 
   159 static SDL_Cursor *
   160 WIN_CreateSystemCursor(SDL_SystemCursor id)
   161 {
   162     SDL_Cursor *cursor;
   163     LPCTSTR name;
   164 
   165     switch(id)
   166     {
   167     default:
   168         SDL_assert(0);
   169         return NULL;
   170     case SDL_SYSTEM_CURSOR_ARROW:     name = IDC_ARROW; break;
   171     case SDL_SYSTEM_CURSOR_IBEAM:     name = IDC_IBEAM; break;
   172     case SDL_SYSTEM_CURSOR_WAIT:      name = IDC_WAIT; break;
   173     case SDL_SYSTEM_CURSOR_CROSSHAIR: name = IDC_CROSS; break;
   174     case SDL_SYSTEM_CURSOR_WAITARROW: name = IDC_WAIT; break;
   175     case SDL_SYSTEM_CURSOR_SIZENWSE:  name = IDC_SIZENWSE; break;
   176     case SDL_SYSTEM_CURSOR_SIZENESW:  name = IDC_SIZENESW; break;
   177     case SDL_SYSTEM_CURSOR_SIZEWE:    name = IDC_SIZEWE; break;
   178     case SDL_SYSTEM_CURSOR_SIZENS:    name = IDC_SIZENS; break;
   179     case SDL_SYSTEM_CURSOR_SIZEALL:   name = IDC_SIZEALL; break;
   180     case SDL_SYSTEM_CURSOR_NO:        name = IDC_NO; break;
   181     case SDL_SYSTEM_CURSOR_HAND:      name = IDC_HAND; break;
   182     }
   183 
   184     cursor = SDL_calloc(1, sizeof(*cursor));
   185     if (cursor) {
   186         HICON hicon;
   187 
   188         hicon = LoadCursor(NULL, name);
   189 
   190         cursor->driverdata = hicon;
   191     } else {
   192         SDL_OutOfMemory();
   193     }
   194 
   195     return cursor;
   196 }
   197 
   198 static void
   199 WIN_FreeCursor(SDL_Cursor * cursor)
   200 {
   201     HICON hicon = (HICON)cursor->driverdata;
   202 
   203     DestroyIcon(hicon);
   204     SDL_free(cursor);
   205 }
   206 
   207 static int
   208 WIN_ShowCursor(SDL_Cursor * cursor)
   209 {
   210     if (cursor) {
   211         SDL_cursor = (HCURSOR)cursor->driverdata;
   212     } else {
   213         SDL_cursor = NULL;
   214     }
   215     if (SDL_GetMouseFocus() != NULL) {
   216         SetCursor(SDL_cursor);
   217     }
   218     return 0;
   219 }
   220 
   221 static void
   222 WIN_WarpMouse(SDL_Window * window, int x, int y)
   223 {
   224     SDL_WindowData *data = (SDL_WindowData *)window->driverdata;
   225     HWND hwnd = data->hwnd;
   226     POINT pt;
   227 
   228     /* Don't warp the mouse while we're doing a modal interaction */
   229     if (data->in_title_click || data->focus_click_pending) {
   230         return;
   231     }
   232 
   233     pt.x = x;
   234     pt.y = y;
   235     ClientToScreen(hwnd, &pt);
   236     SetCursorPos(pt.x, pt.y);
   237 }
   238 
   239 static int
   240 WIN_WarpMouseGlobal(int x, int y)
   241 {
   242     POINT pt;
   243 
   244     pt.x = x;
   245     pt.y = y;
   246     SetCursorPos(pt.x, pt.y);
   247     return 0;
   248 }
   249 
   250 static int
   251 WIN_SetRelativeMouseMode(SDL_bool enabled)
   252 {
   253     return ToggleRawInput(enabled);
   254 }
   255 
   256 static int
   257 WIN_CaptureMouse(SDL_Window *window)
   258 {
   259     if (!window) {
   260         SDL_Window *focusWin = SDL_GetKeyboardFocus();
   261         if (focusWin) {
   262             WIN_OnWindowEnter(SDL_GetVideoDevice(), focusWin);  /* make sure WM_MOUSELEAVE messages are (re)enabled. */
   263         }
   264     }
   265 
   266     /* While we were thinking of SetCapture() when designing this API in SDL,
   267        we didn't count on the fact that SetCapture() only tracks while the
   268        left mouse button is held down! Instead, we listen for raw mouse input
   269        and manually query the mouse when it leaves the window. :/ */
   270     return ToggleRawInput(window != NULL);
   271 }
   272 
   273 static Uint32
   274 WIN_GetGlobalMouseState(int *x, int *y)
   275 {
   276     Uint32 retval = 0;
   277     POINT pt = { 0, 0 };
   278     GetCursorPos(&pt);
   279     *x = (int) pt.x;
   280     *y = (int) pt.y;
   281 
   282     retval |= GetAsyncKeyState(VK_LBUTTON) & 0x8000 ? SDL_BUTTON_LMASK : 0;
   283     retval |= GetAsyncKeyState(VK_RBUTTON) & 0x8000 ? SDL_BUTTON_RMASK : 0;
   284     retval |= GetAsyncKeyState(VK_MBUTTON) & 0x8000 ? SDL_BUTTON_MMASK : 0;
   285     retval |= GetAsyncKeyState(VK_XBUTTON1) & 0x8000 ? SDL_BUTTON_X1MASK : 0;
   286     retval |= GetAsyncKeyState(VK_XBUTTON2) & 0x8000 ? SDL_BUTTON_X2MASK : 0;
   287 
   288     return retval;
   289 }
   290 
   291 void
   292 WIN_InitMouse(_THIS)
   293 {
   294     SDL_Mouse *mouse = SDL_GetMouse();
   295 
   296     mouse->CreateCursor = WIN_CreateCursor;
   297     mouse->CreateSystemCursor = WIN_CreateSystemCursor;
   298     mouse->ShowCursor = WIN_ShowCursor;
   299     mouse->FreeCursor = WIN_FreeCursor;
   300     mouse->WarpMouse = WIN_WarpMouse;
   301     mouse->WarpMouseGlobal = WIN_WarpMouseGlobal;
   302     mouse->SetRelativeMouseMode = WIN_SetRelativeMouseMode;
   303     mouse->CaptureMouse = WIN_CaptureMouse;
   304     mouse->GetGlobalMouseState = WIN_GetGlobalMouseState;
   305 
   306     SDL_SetDefaultCursor(WIN_CreateDefaultCursor());
   307 
   308     SDL_SetDoubleClickTime(GetDoubleClickTime());
   309 }
   310 
   311 void
   312 WIN_QuitMouse(_THIS)
   313 {
   314     if (rawInputEnableCount) {  /* force RAWINPUT off here. */
   315         rawInputEnableCount = 1;
   316         ToggleRawInput(SDL_FALSE);
   317     }
   318 }
   319 
   320 #endif /* SDL_VIDEO_DRIVER_WINDOWS */
   321 
   322 /* vi: set ts=4 sw=4 expandtab: */