1/*
2    SDL - Simple DirectMedia Layer
3    Copyright (C) 1997-2012 Sam Lantinga
4
5    This library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Lesser General Public
7    License as published by the Free Software Foundation; either
8    version 2.1 of the License, or (at your option) any later version.
9
10    This library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Lesser General Public License for more details.
14
15    You should have received a copy of the GNU Lesser General Public
16    License along with this library; if not, write to the Free Software
17    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18
19    Sam Lantinga
20    slouken@libsdl.org
21*/
22#include "SDL_config.h"
23
24#define WIN32_LEAN_AND_MEAN 1
25#include <windows.h>
26
27#include "SDL_mouse.h"
28#include "../../events/SDL_events_c.h"
29#include "../SDL_cursor_c.h"
30#include "SDL_sysmouse_c.h"
31#include "SDL_lowvideo.h"
32
33#ifdef _WIN32_WCE
34#define USE_STATIC_CURSOR
35#endif
36
37HCURSOR	SDL_hcursor = NULL;		/* Exported for SDL_eventloop.c */
38
39/* The implementation dependent data for the window manager cursor */
40/* For some reason when creating a windows cursor, the ands and xors memory
41   is not copied, so we need to keep track of it and free it when we are done
42   with the cursor.  If we free the memory prematurely, the app crashes. :-}
43*/
44struct WMcursor {
45	HCURSOR curs;
46#ifndef USE_STATIC_CURSOR
47	Uint8 *ands;
48	Uint8 *xors;
49#endif
50};
51
52/* Convert bits to padded bytes */
53#define PAD_BITS(bits)	((bits+7)/8)
54
55#ifdef CURSOR_DEBUG
56static void PrintBITMAP(FILE *out, char *bits, int w, int h)
57{
58	int i;
59	unsigned char ch;
60
61	while ( h-- > 0 ) {
62		for ( i=0; i<w; ++i ) {
63			if ( (i%8) == 0 )
64				ch = *bits++;
65			if ( ch&0x80 )
66				fprintf(out, "X");
67			else
68				fprintf(out, " ");
69			ch <<= 1;
70		}
71		fprintf(out, "\n");
72	}
73}
74#endif
75
76#ifndef USE_STATIC_CURSOR
77/* Local functions to convert the SDL cursor mask into Windows format */
78static void memnot(Uint8 *dst, Uint8 *src, int len)
79{
80	while ( len-- > 0 )
81		*dst++ = ~*src++;
82}
83static void memxor(Uint8 *dst, Uint8 *src1, Uint8 *src2, int len)
84{
85	while ( len-- > 0 )
86		*dst++ = (*src1++)^(*src2++);
87}
88#endif /* !USE_STATIC_CURSOR */
89
90void WIN_FreeWMCursor(_THIS, WMcursor *cursor)
91{
92#ifndef USE_STATIC_CURSOR
93	if ( cursor->curs == GetCursor() )
94		SetCursor(NULL);
95	if ( cursor->curs != NULL )
96		DestroyCursor(cursor->curs);
97	if ( cursor->ands != NULL )
98		SDL_free(cursor->ands);
99	if ( cursor->xors != NULL )
100		SDL_free(cursor->xors);
101#endif /* !USE_STATIC_CURSOR */
102	SDL_free(cursor);
103}
104
105WMcursor *WIN_CreateWMCursor(_THIS,
106		Uint8 *data, Uint8 *mask, int w, int h, int hot_x, int hot_y)
107{
108#ifdef USE_STATIC_CURSOR
109	WMcursor *cursor;
110
111	/* Allocate the cursor */
112	cursor = (WMcursor *)SDL_malloc(sizeof(*cursor));
113	if ( cursor ) {
114		cursor->curs = LoadCursor(NULL, IDC_ARROW);
115	}
116	return(cursor);
117#else
118	WMcursor *cursor;
119	int allowed_x;
120	int allowed_y;
121	int run, pad, i;
122	Uint8 *aptr, *xptr;
123
124	/* Check to make sure the cursor size is okay */
125	allowed_x = GetSystemMetrics(SM_CXCURSOR);
126	allowed_y = GetSystemMetrics(SM_CYCURSOR);
127	if ( (w > allowed_x) || (h > allowed_y) ) {
128		SDL_SetError("Only cursors of dimension (%dx%d) are allowed",
129							allowed_x, allowed_y);
130		return(NULL);
131	}
132
133	/* Allocate the cursor */
134	cursor = (WMcursor *)SDL_malloc(sizeof(*cursor));
135	if ( cursor == NULL ) {
136		SDL_SetError("Out of memory");
137		return(NULL);
138	}
139	cursor->curs = NULL;
140	cursor->ands = NULL;
141	cursor->xors = NULL;
142
143	/* Pad out to the normal cursor size */
144	run = PAD_BITS(w);
145	pad = PAD_BITS(allowed_x)-run;
146	aptr = cursor->ands = (Uint8 *)SDL_malloc((run+pad)*allowed_y);
147	xptr = cursor->xors = (Uint8 *)SDL_malloc((run+pad)*allowed_y);
148	if ( (aptr == NULL) || (xptr == NULL) ) {
149		WIN_FreeWMCursor(NULL, cursor);
150		SDL_OutOfMemory();
151		return(NULL);
152	}
153	for ( i=0; i<h; ++i ) {
154		memxor(xptr, data, mask, run);
155		xptr += run;
156		data += run;
157		memnot(aptr, mask, run);
158		mask += run;
159		aptr += run;
160		SDL_memset(xptr,  0, pad);
161		xptr += pad;
162		SDL_memset(aptr, ~0, pad);
163		aptr += pad;
164	}
165	pad += run;
166	for ( ; i<allowed_y; ++i ) {
167		SDL_memset(xptr,  0, pad);
168		xptr += pad;
169		SDL_memset(aptr, ~0, pad);
170		aptr += pad;
171	}
172
173	/* Create the cursor */
174	cursor->curs = CreateCursor(
175			(HINSTANCE)GetWindowLongPtr(SDL_Window, GWLP_HINSTANCE),
176					hot_x, hot_y, allowed_x, allowed_y,
177						cursor->ands, cursor->xors);
178	if ( cursor->curs == NULL ) {
179		WIN_FreeWMCursor(NULL, cursor);
180		SDL_SetError("Windows couldn't create the requested cursor");
181		return(NULL);
182	}
183	return(cursor);
184#endif /* USE_STATIC_CURSOR */
185}
186
187int WIN_ShowWMCursor(_THIS, WMcursor *cursor)
188{
189	POINT mouse_pos;
190
191	if ( !this->screen ) {
192		return(0);
193	}
194
195	/* Set the window cursor to our cursor, if applicable */
196	if ( cursor != NULL ) {
197		SDL_hcursor = cursor->curs;
198	} else {
199		SDL_hcursor = NULL;
200	}
201	GetCursorPos(&mouse_pos);
202	if ( PtInRect(&SDL_bounds, mouse_pos) ) {
203		SetCursor(SDL_hcursor);
204	}
205	return(1);
206}
207
208void WIN_WarpWMCursor(_THIS, Uint16 x, Uint16 y)
209{
210	if ( mouse_relative) {
211		/*	RJR: March 28, 2000
212			leave physical cursor at center of screen if
213			mouse hidden and grabbed */
214		SDL_PrivateMouseMotion(0, 0, x, y);
215	} else {
216		POINT pt;
217
218		/* With DirectInput the position doesn't follow
219		 * the cursor, so it is set manually */
220		if ( DINPUT() ) {
221			SDL_PrivateMouseMotion(0, 0, x, y);
222		}
223
224		pt.x = x;
225		pt.y = y;
226		ClientToScreen(SDL_Window, &pt);
227		SetCursorPos(pt.x, pt.y);
228	}
229}
230
231/* Update the current mouse state and position */
232void WIN_UpdateMouse(_THIS)
233{
234	POINT pt;
235
236	/* Always unset SDL_APPMOUSEFOCUS to give the WM_MOUSEMOVE event
237	 * handler a chance to install a TRACKMOUSEEVENT */
238	SDL_PrivateAppActive(0, SDL_APPMOUSEFOCUS);
239
240	GetCursorPos(&pt);
241	ScreenToClient(SDL_Window, &pt);
242	SDL_PrivateMouseMotion(0,0, (Sint16)pt.x, (Sint16)pt.y);
243}
244
245/* Check to see if we need to enter or leave mouse relative mode */
246void WIN_CheckMouseMode(_THIS)
247{
248#ifndef _WIN32_WCE
249        /* If the mouse is hidden and input is grabbed, we use relative mode */
250        if ( !(SDL_cursorstate & CURSOR_VISIBLE) &&
251             (this->input_grab != SDL_GRAB_OFF) ) {
252                mouse_relative = 1;
253        } else {
254                mouse_relative = 0;
255        }
256#else
257		mouse_relative =  0;
258#endif
259}
260