1 /*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2016 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 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 *
WIN_CreateSystemCursor(SDL_SystemCursor id)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
WIN_FreeCursor(SDL_Cursor * cursor)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
WIN_ShowCursor(SDL_Cursor * cursor)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
WIN_WarpMouse(SDL_Window * window,int x,int y)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
WIN_WarpMouseGlobal(int x,int y)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
WIN_SetRelativeMouseMode(SDL_bool enabled)251 WIN_SetRelativeMouseMode(SDL_bool enabled)
252 {
253 return ToggleRawInput(enabled);
254 }
255
256 static int
WIN_CaptureMouse(SDL_Window * window)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
WIN_GetGlobalMouseState(int * x,int * y)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
WIN_InitMouse(_THIS)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
WIN_QuitMouse(_THIS)312 WIN_QuitMouse(_THIS)
313 {
314 SDL_Mouse *mouse = SDL_GetMouse();
315 if ( mouse->def_cursor ) {
316 SDL_free(mouse->def_cursor);
317 mouse->def_cursor = NULL;
318 mouse->cur_cursor = NULL;
319 }
320
321 if (rawInputEnableCount) { /* force RAWINPUT off here. */
322 rawInputEnableCount = 1;
323 ToggleRawInput(SDL_FALSE);
324 }
325 }
326
327 #endif /* SDL_VIDEO_DRIVER_WINDOWS */
328
329 /* vi: set ts=4 sw=4 expandtab: */
330