1/*
2    SDL - Simple DirectMedia Layer
3    Copyright (C) 1997-2006 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
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	/* The fullscreen cursor must be done in software with DirectInput */
192	if ( !this->screen || DDRAW_FULLSCREEN() ) {
193		return(0);
194	}
195
196	/* Set the window cursor to our cursor, if applicable */
197	if ( cursor != NULL ) {
198		SDL_hcursor = cursor->curs;
199	} else {
200		SDL_hcursor = NULL;
201	}
202	GetCursorPos(&mouse_pos);
203	if ( PtInRect(&SDL_bounds, mouse_pos) ) {
204		SetCursor(SDL_hcursor);
205	}
206	return(1);
207}
208
209void WIN_WarpWMCursor(_THIS, Uint16 x, Uint16 y)
210{
211	if ( DDRAW_FULLSCREEN() ) {
212		SDL_PrivateMouseMotion(0, 0, x, y);
213	} else if ( mouse_relative) {
214		/*	RJR: March 28, 2000
215			leave physical cursor at center of screen if
216			mouse hidden and grabbed */
217		SDL_PrivateMouseMotion(0, 0, x, y);
218	} else {
219		POINT pt;
220		pt.x = x;
221		pt.y = y;
222		ClientToScreen(SDL_Window, &pt);
223		SetCursorPos(pt.x, pt.y);
224	}
225}
226
227/* Update the current mouse state and position */
228void WIN_UpdateMouse(_THIS)
229{
230	RECT rect;
231	POINT pt;
232
233	if ( ! DDRAW_FULLSCREEN() ) {
234		GetClientRect(SDL_Window, &rect);
235		GetCursorPos(&pt);
236		MapWindowPoints(NULL, SDL_Window, &pt, 1);
237		if (PtInRect(&rect, pt) && (WindowFromPoint(pt) == SDL_Window)){
238			SDL_PrivateAppActive(1, SDL_APPMOUSEFOCUS);
239			SDL_PrivateMouseMotion(0,0, (Sint16)pt.x, (Sint16)pt.y);
240		} else {
241			SDL_PrivateAppActive(0, SDL_APPMOUSEFOCUS);
242		}
243	}
244}
245
246/* Check to see if we need to enter or leave mouse relative mode */
247void WIN_CheckMouseMode(_THIS)
248{
249#ifndef _WIN32_WCE
250        /* If the mouse is hidden and input is grabbed, we use relative mode */
251        if ( !(SDL_cursorstate & CURSOR_VISIBLE) &&
252             (this->input_grab != SDL_GRAB_OFF) ) {
253                mouse_relative = 1;
254        } else {
255                mouse_relative = 0;
256        }
257#else
258		mouse_relative =  0;
259#endif
260}
261