SDL_windowsmouse.c (8879B)
1 /* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2020 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_windowsvideo.h" 26 27 #include "../../events/SDL_mouse_c.h" 28 29 30 HCURSOR SDL_cursor = NULL; 31 32 static int rawInputEnableCount = 0; 33 34 static int 35 ToggleRawInput(SDL_bool enabled) 36 { 37 RAWINPUTDEVICE rawMouse = { 0x01, 0x02, 0, NULL }; /* Mouse: UsagePage = 1, Usage = 2 */ 38 39 if (enabled) { 40 rawInputEnableCount++; 41 if (rawInputEnableCount > 1) { 42 return 0; /* already done. */ 43 } 44 } else { 45 if (rawInputEnableCount == 0) { 46 return 0; /* already done. */ 47 } 48 rawInputEnableCount--; 49 if (rawInputEnableCount > 0) { 50 return 0; /* not time to disable yet */ 51 } 52 } 53 54 if (!enabled) { 55 rawMouse.dwFlags |= RIDEV_REMOVE; 56 } 57 58 /* (Un)register raw input for mice */ 59 if (RegisterRawInputDevices(&rawMouse, 1, sizeof(RAWINPUTDEVICE)) == FALSE) { 60 61 /* Only return an error when registering. If we unregister and fail, 62 then it's probably that we unregistered twice. That's OK. */ 63 if (enabled) { 64 return SDL_Unsupported(); 65 } 66 } 67 return 0; 68 } 69 70 71 static SDL_Cursor * 72 WIN_CreateDefaultCursor() 73 { 74 SDL_Cursor *cursor; 75 76 cursor = SDL_calloc(1, sizeof(*cursor)); 77 if (cursor) { 78 cursor->driverdata = LoadCursor(NULL, IDC_ARROW); 79 } else { 80 SDL_OutOfMemory(); 81 } 82 83 return cursor; 84 } 85 86 static SDL_Cursor * 87 WIN_CreateCursor(SDL_Surface * surface, int hot_x, int hot_y) 88 { 89 /* msdn says cursor mask has to be padded out to word alignment. Not sure 90 if that means machine word or WORD, but this handles either case. */ 91 const size_t pad = (sizeof (size_t) * 8); /* 32 or 64, or whatever. */ 92 SDL_Cursor *cursor; 93 HICON hicon; 94 HDC hdc; 95 BITMAPV4HEADER bmh; 96 LPVOID pixels; 97 LPVOID maskbits; 98 size_t maskbitslen; 99 SDL_bool isstack; 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_small_alloc(Uint8, maskbitslen, &isstack); 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_small_free(maskbits, isstack); 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 SDL_bool swapButtons = GetSystemMetrics(SM_SWAPBUTTON) != 0; 279 280 GetCursorPos(&pt); 281 *x = (int) pt.x; 282 *y = (int) pt.y; 283 284 retval |= GetAsyncKeyState(!swapButtons ? VK_LBUTTON : VK_RBUTTON) & 0x8000 ? SDL_BUTTON_LMASK : 0; 285 retval |= GetAsyncKeyState(!swapButtons ? VK_RBUTTON : VK_LBUTTON) & 0x8000 ? SDL_BUTTON_RMASK : 0; 286 retval |= GetAsyncKeyState(VK_MBUTTON) & 0x8000 ? SDL_BUTTON_MMASK : 0; 287 retval |= GetAsyncKeyState(VK_XBUTTON1) & 0x8000 ? SDL_BUTTON_X1MASK : 0; 288 retval |= GetAsyncKeyState(VK_XBUTTON2) & 0x8000 ? SDL_BUTTON_X2MASK : 0; 289 290 return retval; 291 } 292 293 void 294 WIN_InitMouse(_THIS) 295 { 296 SDL_Mouse *mouse = SDL_GetMouse(); 297 298 mouse->CreateCursor = WIN_CreateCursor; 299 mouse->CreateSystemCursor = WIN_CreateSystemCursor; 300 mouse->ShowCursor = WIN_ShowCursor; 301 mouse->FreeCursor = WIN_FreeCursor; 302 mouse->WarpMouse = WIN_WarpMouse; 303 mouse->WarpMouseGlobal = WIN_WarpMouseGlobal; 304 mouse->SetRelativeMouseMode = WIN_SetRelativeMouseMode; 305 mouse->CaptureMouse = WIN_CaptureMouse; 306 mouse->GetGlobalMouseState = WIN_GetGlobalMouseState; 307 308 SDL_SetDefaultCursor(WIN_CreateDefaultCursor()); 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: */