1ddeae24beabdc3cc5d70ce60d2ec48aae3e5f35
[platform/upstream/SDL.git] / src / video / windows / SDL_windowsmouse.c
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: */