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_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
ToggleRawInput(SDL_bool enabled)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 *
WIN_CreateDefaultCursor()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 *
WIN_CreateCursor(SDL_Surface * surface,int hot_x,int hot_y)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 *
WIN_CreateSystemCursor(SDL_SystemCursor id)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
WIN_FreeCursor(SDL_Cursor * cursor)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
WIN_ShowCursor(SDL_Cursor * cursor)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
WIN_WarpMouse(SDL_Window * window,int x,int y)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
WIN_WarpMouseGlobal(int x,int y)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
WIN_SetRelativeMouseMode(SDL_bool enabled)252 WIN_SetRelativeMouseMode(SDL_bool enabled)
253 {
254 return ToggleRawInput(enabled);
255 }
256
257 static int
WIN_CaptureMouse(SDL_Window * window)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
WIN_GetGlobalMouseState(int * x,int * y)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
WIN_InitMouse(_THIS)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
WIN_QuitMouse(_THIS)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: */
322