1 /*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2021 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 DWORD SDL_last_warp_time = 0;
31 HCURSOR SDL_cursor = NULL;
32 static SDL_Cursor *SDL_blank_cursor = NULL;
33
34 static int rawInputEnableCount = 0;
35
36 static int
ToggleRawInput(SDL_bool enabled)37 ToggleRawInput(SDL_bool enabled)
38 {
39 RAWINPUTDEVICE rawMouse = { 0x01, 0x02, 0, NULL }; /* Mouse: UsagePage = 1, Usage = 2 */
40
41 if (enabled) {
42 rawInputEnableCount++;
43 if (rawInputEnableCount > 1) {
44 return 0; /* already done. */
45 }
46 } else {
47 if (rawInputEnableCount == 0) {
48 return 0; /* already done. */
49 }
50 rawInputEnableCount--;
51 if (rawInputEnableCount > 0) {
52 return 0; /* not time to disable yet */
53 }
54 }
55
56 if (!enabled) {
57 rawMouse.dwFlags |= RIDEV_REMOVE;
58 }
59
60 /* (Un)register raw input for mice */
61 if (RegisterRawInputDevices(&rawMouse, 1, sizeof(RAWINPUTDEVICE)) == FALSE) {
62 /* Reset the enable count, otherwise subsequent enable calls will
63 believe raw input is enabled */
64 rawInputEnableCount = 0;
65
66 /* Only return an error when registering. If we unregister and fail,
67 then it's probably that we unregistered twice. That's OK. */
68 if (enabled) {
69 return SDL_Unsupported();
70 }
71 }
72 return 0;
73 }
74
75
76 static SDL_Cursor *
WIN_CreateDefaultCursor()77 WIN_CreateDefaultCursor()
78 {
79 SDL_Cursor *cursor;
80
81 cursor = SDL_calloc(1, sizeof(*cursor));
82 if (cursor) {
83 cursor->driverdata = LoadCursor(NULL, IDC_ARROW);
84 } else {
85 SDL_OutOfMemory();
86 }
87
88 return cursor;
89 }
90
91 static SDL_Cursor *
WIN_CreateCursor(SDL_Surface * surface,int hot_x,int hot_y)92 WIN_CreateCursor(SDL_Surface * surface, int hot_x, int hot_y)
93 {
94 /* msdn says cursor mask has to be padded out to word alignment. Not sure
95 if that means machine word or WORD, but this handles either case. */
96 const size_t pad = (sizeof (size_t) * 8); /* 32 or 64, or whatever. */
97 SDL_Cursor *cursor;
98 HICON hicon;
99 HICON hcursor;
100 HDC hdc;
101 BITMAPV4HEADER bmh;
102 LPVOID pixels;
103 LPVOID maskbits;
104 size_t maskbitslen;
105 SDL_bool isstack;
106 ICONINFO ii;
107
108 SDL_zero(bmh);
109 bmh.bV4Size = sizeof(bmh);
110 bmh.bV4Width = surface->w;
111 bmh.bV4Height = -surface->h; /* Invert the image */
112 bmh.bV4Planes = 1;
113 bmh.bV4BitCount = 32;
114 bmh.bV4V4Compression = BI_BITFIELDS;
115 bmh.bV4AlphaMask = 0xFF000000;
116 bmh.bV4RedMask = 0x00FF0000;
117 bmh.bV4GreenMask = 0x0000FF00;
118 bmh.bV4BlueMask = 0x000000FF;
119
120 maskbitslen = ((surface->w + (pad - (surface->w % pad))) / 8) * surface->h;
121 maskbits = SDL_small_alloc(Uint8, maskbitslen, &isstack);
122 if (maskbits == NULL) {
123 SDL_OutOfMemory();
124 return NULL;
125 }
126
127 /* AND the cursor against full bits: no change. We already have alpha. */
128 SDL_memset(maskbits, 0xFF, maskbitslen);
129
130 hdc = GetDC(NULL);
131 SDL_zero(ii);
132 ii.fIcon = FALSE;
133 ii.xHotspot = (DWORD)hot_x;
134 ii.yHotspot = (DWORD)hot_y;
135 ii.hbmColor = CreateDIBSection(hdc, (BITMAPINFO*)&bmh, DIB_RGB_COLORS, &pixels, NULL, 0);
136 ii.hbmMask = CreateBitmap(surface->w, surface->h, 1, 1, maskbits);
137 ReleaseDC(NULL, hdc);
138 SDL_small_free(maskbits, isstack);
139
140 SDL_assert(surface->format->format == SDL_PIXELFORMAT_ARGB8888);
141 SDL_assert(surface->pitch == surface->w * 4);
142 SDL_memcpy(pixels, surface->pixels, surface->h * surface->pitch);
143
144 hicon = CreateIconIndirect(&ii);
145
146 DeleteObject(ii.hbmColor);
147 DeleteObject(ii.hbmMask);
148
149 if (!hicon) {
150 WIN_SetError("CreateIconIndirect()");
151 return NULL;
152 }
153
154 /* The cursor returned by CreateIconIndirect does not respect system cursor size
155 preference, use CopyImage to duplicate the cursor with desired sizes */
156 hcursor = CopyImage(hicon, IMAGE_CURSOR, 0, 0, LR_DEFAULTSIZE);
157 DestroyIcon(hicon);
158
159 if (!hcursor) {
160 WIN_SetError("CopyImage()");
161 return NULL;
162 }
163
164 cursor = SDL_calloc(1, sizeof(*cursor));
165 if (cursor) {
166 cursor->driverdata = hcursor;
167 } else {
168 DestroyIcon(hcursor);
169 SDL_OutOfMemory();
170 }
171
172 return cursor;
173 }
174
175 static SDL_Cursor *
WIN_CreateBlankCursor()176 WIN_CreateBlankCursor()
177 {
178 SDL_Surface *surface = SDL_CreateRGBSurfaceWithFormat(0, 32, 32, 32, SDL_PIXELFORMAT_ARGB8888);
179 if (surface) {
180 return WIN_CreateCursor(surface, 0, 0);
181 }
182 return NULL;
183 }
184
185 static SDL_Cursor *
WIN_CreateSystemCursor(SDL_SystemCursor id)186 WIN_CreateSystemCursor(SDL_SystemCursor id)
187 {
188 SDL_Cursor *cursor;
189 LPCTSTR name;
190
191 switch(id)
192 {
193 default:
194 SDL_assert(0);
195 return NULL;
196 case SDL_SYSTEM_CURSOR_ARROW: name = IDC_ARROW; break;
197 case SDL_SYSTEM_CURSOR_IBEAM: name = IDC_IBEAM; break;
198 case SDL_SYSTEM_CURSOR_WAIT: name = IDC_WAIT; break;
199 case SDL_SYSTEM_CURSOR_CROSSHAIR: name = IDC_CROSS; break;
200 case SDL_SYSTEM_CURSOR_WAITARROW: name = IDC_WAIT; break;
201 case SDL_SYSTEM_CURSOR_SIZENWSE: name = IDC_SIZENWSE; break;
202 case SDL_SYSTEM_CURSOR_SIZENESW: name = IDC_SIZENESW; break;
203 case SDL_SYSTEM_CURSOR_SIZEWE: name = IDC_SIZEWE; break;
204 case SDL_SYSTEM_CURSOR_SIZENS: name = IDC_SIZENS; break;
205 case SDL_SYSTEM_CURSOR_SIZEALL: name = IDC_SIZEALL; break;
206 case SDL_SYSTEM_CURSOR_NO: name = IDC_NO; break;
207 case SDL_SYSTEM_CURSOR_HAND: name = IDC_HAND; break;
208 }
209
210 cursor = SDL_calloc(1, sizeof(*cursor));
211 if (cursor) {
212 HICON hicon;
213
214 hicon = LoadCursor(NULL, name);
215
216 cursor->driverdata = hicon;
217 } else {
218 SDL_OutOfMemory();
219 }
220
221 return cursor;
222 }
223
224 static void
WIN_FreeCursor(SDL_Cursor * cursor)225 WIN_FreeCursor(SDL_Cursor * cursor)
226 {
227 HICON hicon = (HICON)cursor->driverdata;
228
229 DestroyIcon(hicon);
230 SDL_free(cursor);
231 }
232
233 static int
WIN_ShowCursor(SDL_Cursor * cursor)234 WIN_ShowCursor(SDL_Cursor * cursor)
235 {
236 if (!cursor) {
237 cursor = SDL_blank_cursor;
238 }
239 if (cursor) {
240 SDL_cursor = (HCURSOR)cursor->driverdata;
241 } else {
242 SDL_cursor = NULL;
243 }
244 if (SDL_GetMouseFocus() != NULL) {
245 SetCursor(SDL_cursor);
246 }
247 return 0;
248 }
249
250 void
WIN_SetCursorPos(int x,int y)251 WIN_SetCursorPos(int x, int y)
252 {
253 /* We need to jitter the value because otherwise Windows will occasionally inexplicably ignore the SetCursorPos() or SendInput() */
254 SetCursorPos(x, y);
255 SetCursorPos(x+1, y);
256 SetCursorPos(x, y);
257
258 /* Flush any mouse motion prior to or associated with this warp */
259 SDL_last_warp_time = GetTickCount();
260 if (!SDL_last_warp_time) {
261 SDL_last_warp_time = 1;
262 }
263 }
264
265 static void
WIN_WarpMouse(SDL_Window * window,int x,int y)266 WIN_WarpMouse(SDL_Window * window, int x, int y)
267 {
268 SDL_WindowData *data = (SDL_WindowData *)window->driverdata;
269 HWND hwnd = data->hwnd;
270 POINT pt;
271
272 /* Don't warp the mouse while we're doing a modal interaction */
273 if (data->in_title_click || data->focus_click_pending) {
274 return;
275 }
276
277 pt.x = x;
278 pt.y = y;
279 ClientToScreen(hwnd, &pt);
280 WIN_SetCursorPos(pt.x, pt.y);
281
282 /* Send the exact mouse motion associated with this warp */
283 SDL_SendMouseMotion(window, SDL_GetMouse()->mouseID, 0, x, y);
284 }
285
286 static int
WIN_WarpMouseGlobal(int x,int y)287 WIN_WarpMouseGlobal(int x, int y)
288 {
289 POINT pt;
290
291 pt.x = x;
292 pt.y = y;
293 SetCursorPos(pt.x, pt.y);
294 return 0;
295 }
296
297 static int
WIN_SetRelativeMouseMode(SDL_bool enabled)298 WIN_SetRelativeMouseMode(SDL_bool enabled)
299 {
300 return ToggleRawInput(enabled);
301 }
302
303 static int
WIN_CaptureMouse(SDL_Window * window)304 WIN_CaptureMouse(SDL_Window *window)
305 {
306 if (window) {
307 SDL_WindowData *data = (SDL_WindowData *)window->driverdata;
308 SetCapture(data->hwnd);
309 } else {
310 SDL_Window *focus_window = SDL_GetMouseFocus();
311
312 if (focus_window) {
313 SDL_WindowData *data = (SDL_WindowData *)focus_window->driverdata;
314 if (!data->mouse_tracked) {
315 SDL_SetMouseFocus(NULL);
316 }
317 }
318 ReleaseCapture();
319 }
320
321 return 0;
322 }
323
324 static Uint32
WIN_GetGlobalMouseState(int * x,int * y)325 WIN_GetGlobalMouseState(int *x, int *y)
326 {
327 Uint32 retval = 0;
328 POINT pt = { 0, 0 };
329 SDL_bool swapButtons = GetSystemMetrics(SM_SWAPBUTTON) != 0;
330
331 GetCursorPos(&pt);
332 *x = (int) pt.x;
333 *y = (int) pt.y;
334
335 retval |= GetAsyncKeyState(!swapButtons ? VK_LBUTTON : VK_RBUTTON) & 0x8000 ? SDL_BUTTON_LMASK : 0;
336 retval |= GetAsyncKeyState(!swapButtons ? VK_RBUTTON : VK_LBUTTON) & 0x8000 ? SDL_BUTTON_RMASK : 0;
337 retval |= GetAsyncKeyState(VK_MBUTTON) & 0x8000 ? SDL_BUTTON_MMASK : 0;
338 retval |= GetAsyncKeyState(VK_XBUTTON1) & 0x8000 ? SDL_BUTTON_X1MASK : 0;
339 retval |= GetAsyncKeyState(VK_XBUTTON2) & 0x8000 ? SDL_BUTTON_X2MASK : 0;
340
341 return retval;
342 }
343
344 void
WIN_InitMouse(_THIS)345 WIN_InitMouse(_THIS)
346 {
347 SDL_Mouse *mouse = SDL_GetMouse();
348
349 mouse->CreateCursor = WIN_CreateCursor;
350 mouse->CreateSystemCursor = WIN_CreateSystemCursor;
351 mouse->ShowCursor = WIN_ShowCursor;
352 mouse->FreeCursor = WIN_FreeCursor;
353 mouse->WarpMouse = WIN_WarpMouse;
354 mouse->WarpMouseGlobal = WIN_WarpMouseGlobal;
355 mouse->SetRelativeMouseMode = WIN_SetRelativeMouseMode;
356 mouse->CaptureMouse = WIN_CaptureMouse;
357 mouse->GetGlobalMouseState = WIN_GetGlobalMouseState;
358
359 SDL_SetDefaultCursor(WIN_CreateDefaultCursor());
360
361 SDL_blank_cursor = WIN_CreateBlankCursor();
362 }
363
364 void
WIN_QuitMouse(_THIS)365 WIN_QuitMouse(_THIS)
366 {
367 if (rawInputEnableCount) { /* force RAWINPUT off here. */
368 rawInputEnableCount = 1;
369 ToggleRawInput(SDL_FALSE);
370 }
371
372 if (SDL_blank_cursor) {
373 SDL_FreeCursor(SDL_blank_cursor);
374 }
375 }
376
377 #endif /* SDL_VIDEO_DRIVER_WINDOWS */
378
379 /* vi: set ts=4 sw=4 expandtab: */
380