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 #ifdef SDL_JOYSTICK_WINMM
25 
26 /* Win32 MultiMedia Joystick driver, contributed by Andrei de A. Formiga */
27 
28 #define WIN32_LEAN_AND_MEAN
29 #include <windows.h>
30 #include <mmsystem.h>
31 #include <regstr.h>
32 
33 #include "SDL_events.h"
34 #include "SDL_joystick.h"
35 #include "../SDL_sysjoystick.h"
36 #include "../SDL_joystick_c.h"
37 
38 #define MAX_JOYSTICKS	16
39 #define MAX_AXES	6	/* each joystick can have up to 6 axes */
40 #define MAX_BUTTONS	32	/* and 32 buttons                      */
41 #define AXIS_MIN	-32768  /* minimum value for axis coordinate */
42 #define AXIS_MAX	32767   /* maximum value for axis coordinate */
43 /* limit axis to 256 possible positions to filter out noise */
44 #define JOY_AXIS_THRESHOLD      (((AXIS_MAX)-(AXIS_MIN))/256)
45 #define JOY_BUTTON_FLAG(n)	(1<<n)
46 
47 
48 /* array to hold joystick ID values */
49 static UINT	SYS_JoystickID[MAX_JOYSTICKS];
50 static JOYCAPS	SYS_Joystick[MAX_JOYSTICKS];
51 static char	*SYS_JoystickName[MAX_JOYSTICKS];
52 
53 /* The private structure used to keep track of a joystick */
54 struct joystick_hwdata
55 {
56 	/* joystick ID */
57 	UINT	id;
58 
59 	/* values used to translate device-specific coordinates into
60 	   SDL-standard ranges */
61 	struct _transaxis {
62 		int offset;
63 		float scale;
64 	} transaxis[6];
65 };
66 
67 /* Convert a win32 Multimedia API return code to a text message */
68 static void SetMMerror(char *function, int code);
69 
70 
GetJoystickName(int index,const char * szRegKey)71 static char *GetJoystickName(int index, const char *szRegKey)
72 {
73 	/* added 7/24/2004 by Eckhard Stolberg */
74 	/*
75 		see if there is a joystick for the current
76 		index (1-16) listed in the registry
77 	*/
78 	char *name = NULL;
79 	HKEY hTopKey;
80 	HKEY hKey;
81 	DWORD regsize;
82 	LONG regresult;
83 	char regkey[256];
84 	char regvalue[256];
85 	char regname[256];
86 
87 	SDL_snprintf(regkey, SDL_arraysize(regkey), "%s\\%s\\%s",
88 		REGSTR_PATH_JOYCONFIG, szRegKey, REGSTR_KEY_JOYCURR);
89 	hTopKey = HKEY_LOCAL_MACHINE;
90 	regresult = RegOpenKeyExA(hTopKey, regkey, 0, KEY_READ, &hKey);
91 	if (regresult != ERROR_SUCCESS) {
92 		hTopKey = HKEY_CURRENT_USER;
93 		regresult = RegOpenKeyExA(hTopKey, regkey, 0, KEY_READ, &hKey);
94 	}
95 	if (regresult != ERROR_SUCCESS) {
96 		return NULL;
97 	}
98 
99 	/* find the registry key name for the joystick's properties */
100 	regsize = sizeof(regname);
101 	SDL_snprintf(regvalue, SDL_arraysize(regvalue), "Joystick%d%s", index+1, REGSTR_VAL_JOYOEMNAME);
102 	regresult = RegQueryValueExA(hKey, regvalue, 0, 0, (LPBYTE)regname, &regsize);
103 	RegCloseKey(hKey);
104 
105 	if (regresult != ERROR_SUCCESS) {
106 		return NULL;
107 	}
108 
109 	/* open that registry key */
110 	SDL_snprintf(regkey, SDL_arraysize(regkey), "%s\\%s", REGSTR_PATH_JOYOEM, regname);
111 	regresult = RegOpenKeyExA(hTopKey, regkey, 0, KEY_READ, &hKey);
112 	if (regresult != ERROR_SUCCESS) {
113 		return NULL;
114 	}
115 
116 	/* find the size for the OEM name text */
117 	regsize = sizeof(regvalue);
118 	regresult = RegQueryValueExA(hKey, REGSTR_VAL_JOYOEMNAME, 0, 0, NULL, &regsize);
119 	if (regresult == ERROR_SUCCESS) {
120 		/* allocate enough memory for the OEM name text ... */
121 		name = (char *) SDL_malloc(regsize);
122 		if ( name ) {
123 			/* ... and read it from the registry */
124 			regresult = RegQueryValueExA(hKey,
125 				REGSTR_VAL_JOYOEMNAME, 0, 0,
126 				(LPBYTE) name, &regsize);
127 		}
128 	}
129 	RegCloseKey(hKey);
130 
131 	return(name);
132 }
133 
134 /* Function to scan the system for joysticks.
135  * This function should set SDL_numjoysticks to the number of available
136  * joysticks.  Joystick 0 should be the system default joystick.
137  * It should return 0, or -1 on an unrecoverable fatal error.
138  */
SDL_SYS_JoystickInit(void)139 int SDL_SYS_JoystickInit(void)
140 {
141 	int	i;
142 	int maxdevs;
143 	int numdevs;
144 	JOYINFOEX joyinfo;
145 	JOYCAPS	joycaps;
146 	MMRESULT result;
147 
148 	/* Reset the joystick ID & name mapping tables */
149 	for ( i = 0; i < MAX_JOYSTICKS; ++i ) {
150 		SYS_JoystickID[i] = 0;
151 		SYS_JoystickName[i] = NULL;
152 	}
153 
154 	/* Loop over all potential joystick devices */
155 	numdevs = 0;
156 	maxdevs = joyGetNumDevs();
157 	for ( i = JOYSTICKID1; i < maxdevs && numdevs < MAX_JOYSTICKS; ++i ) {
158 
159 		joyinfo.dwSize = sizeof(joyinfo);
160 		joyinfo.dwFlags = JOY_RETURNALL;
161 		result = joyGetPosEx(i, &joyinfo);
162 		if ( result == JOYERR_NOERROR ) {
163 			result = joyGetDevCaps(i, &joycaps, sizeof(joycaps));
164 			if ( result == JOYERR_NOERROR ) {
165 				SYS_JoystickID[numdevs] = i;
166 				SYS_Joystick[numdevs] = joycaps;
167 				SYS_JoystickName[numdevs] = GetJoystickName(i, joycaps.szRegKey);
168 				numdevs++;
169 			}
170 		}
171 	}
172 	return(numdevs);
173 }
174 
175 /* Function to get the device-dependent name of a joystick */
SDL_SYS_JoystickName(int index)176 const char *SDL_SYS_JoystickName(int index)
177 {
178 	if ( SYS_JoystickName[index] != NULL ) {
179 		return(SYS_JoystickName[index]);
180 	} else {
181 		return(SYS_Joystick[index].szPname);
182 	}
183 }
184 
185 /* Function to open a joystick for use.
186    The joystick to open is specified by the index field of the joystick.
187    This should fill the nbuttons and naxes fields of the joystick structure.
188    It returns 0, or -1 if there is an error.
189  */
SDL_SYS_JoystickOpen(SDL_Joystick * joystick)190 int SDL_SYS_JoystickOpen(SDL_Joystick *joystick)
191 {
192 	int index, i;
193 	int caps_flags[MAX_AXES-2] =
194 		{ JOYCAPS_HASZ, JOYCAPS_HASR, JOYCAPS_HASU, JOYCAPS_HASV };
195 	int axis_min[MAX_AXES], axis_max[MAX_AXES];
196 
197 
198 	/* shortcut */
199 	index = joystick->index;
200 	axis_min[0] = SYS_Joystick[index].wXmin;
201 	axis_max[0] = SYS_Joystick[index].wXmax;
202 	axis_min[1] = SYS_Joystick[index].wYmin;
203 	axis_max[1] = SYS_Joystick[index].wYmax;
204 	axis_min[2] = SYS_Joystick[index].wZmin;
205 	axis_max[2] = SYS_Joystick[index].wZmax;
206 	axis_min[3] = SYS_Joystick[index].wRmin;
207 	axis_max[3] = SYS_Joystick[index].wRmax;
208 	axis_min[4] = SYS_Joystick[index].wUmin;
209 	axis_max[4] = SYS_Joystick[index].wUmax;
210 	axis_min[5] = SYS_Joystick[index].wVmin;
211 	axis_max[5] = SYS_Joystick[index].wVmax;
212 
213 	/* allocate memory for system specific hardware data */
214 	joystick->hwdata = (struct joystick_hwdata *) SDL_malloc(sizeof(*joystick->hwdata));
215 	if (joystick->hwdata == NULL)
216 	{
217 		SDL_OutOfMemory();
218 		return(-1);
219 	}
220 	SDL_memset(joystick->hwdata, 0, sizeof(*joystick->hwdata));
221 
222 	/* set hardware data */
223 	joystick->hwdata->id = SYS_JoystickID[index];
224 	for ( i = 0; i < MAX_AXES; ++i ) {
225 		if ( (i<2) || (SYS_Joystick[index].wCaps & caps_flags[i-2]) ) {
226 			joystick->hwdata->transaxis[i].offset =
227 				AXIS_MIN - axis_min[i];
228 			joystick->hwdata->transaxis[i].scale =
229 				(float)(AXIS_MAX - AXIS_MIN) / (axis_max[i] - axis_min[i]);
230 		} else {
231 			joystick->hwdata->transaxis[i].offset = 0;
232 			joystick->hwdata->transaxis[i].scale = 1.0; /* Just in case */
233 		}
234 	}
235 
236 	/* fill nbuttons, naxes, and nhats fields */
237 	joystick->nbuttons = SYS_Joystick[index].wNumButtons;
238 	joystick->naxes = SYS_Joystick[index].wNumAxes;
239 	if ( SYS_Joystick[index].wCaps & JOYCAPS_HASPOV ) {
240 		joystick->nhats = 1;
241 	} else {
242 		joystick->nhats = 0;
243 	}
244 	return(0);
245 }
246 
TranslatePOV(DWORD value)247 static Uint8 TranslatePOV(DWORD value)
248 {
249 	Uint8 pos;
250 
251 	pos = SDL_HAT_CENTERED;
252 	if ( value != JOY_POVCENTERED ) {
253 		if ( (value > JOY_POVLEFT) || (value < JOY_POVRIGHT) ) {
254 			pos |= SDL_HAT_UP;
255 		}
256 		if ( (value > JOY_POVFORWARD) && (value < JOY_POVBACKWARD) ) {
257 			pos |= SDL_HAT_RIGHT;
258 		}
259 		if ( (value > JOY_POVRIGHT) && (value < JOY_POVLEFT) ) {
260 			pos |= SDL_HAT_DOWN;
261 		}
262 		if ( value > JOY_POVBACKWARD ) {
263 			pos |= SDL_HAT_LEFT;
264 		}
265 	}
266 	return(pos);
267 }
268 
269 /* Function to update the state of a joystick - called as a device poll.
270  * This function shouldn't update the joystick structure directly,
271  * but instead should call SDL_PrivateJoystick*() to deliver events
272  * and update joystick device state.
273  */
SDL_SYS_JoystickUpdate(SDL_Joystick * joystick)274 void SDL_SYS_JoystickUpdate(SDL_Joystick *joystick)
275 {
276 	MMRESULT result;
277 	int i;
278 	DWORD flags[MAX_AXES] = { JOY_RETURNX, JOY_RETURNY, JOY_RETURNZ,
279 				  JOY_RETURNR, JOY_RETURNU, JOY_RETURNV };
280 	DWORD pos[MAX_AXES];
281 	struct _transaxis *transaxis;
282 	int value, change;
283 	JOYINFOEX joyinfo;
284 
285 	joyinfo.dwSize = sizeof(joyinfo);
286 	joyinfo.dwFlags = JOY_RETURNALL|JOY_RETURNPOVCTS;
287 	if ( ! joystick->hats ) {
288 		joyinfo.dwFlags &= ~(JOY_RETURNPOV|JOY_RETURNPOVCTS);
289 	}
290 	result = joyGetPosEx(joystick->hwdata->id, &joyinfo);
291 	if ( result != JOYERR_NOERROR ) {
292 		SetMMerror("joyGetPosEx", result);
293 		return;
294 	}
295 
296 	/* joystick motion events */
297 	pos[0] = joyinfo.dwXpos;
298 	pos[1] = joyinfo.dwYpos;
299 	pos[2] = joyinfo.dwZpos;
300 	pos[3] = joyinfo.dwRpos;
301 	pos[4] = joyinfo.dwUpos;
302 	pos[5] = joyinfo.dwVpos;
303 
304 	transaxis = joystick->hwdata->transaxis;
305 	for (i = 0; i < joystick->naxes; i++) {
306 		if (joyinfo.dwFlags & flags[i]) {
307 			value = (int)(((float)pos[i] + transaxis[i].offset) * transaxis[i].scale);
308 			change = (value - joystick->axes[i]);
309 			if ( (change < -JOY_AXIS_THRESHOLD) || (change > JOY_AXIS_THRESHOLD) ) {
310 				SDL_PrivateJoystickAxis(joystick, (Uint8)i, (Sint16)value);
311 			}
312 		}
313 	}
314 
315 	/* joystick button events */
316 	if ( joyinfo.dwFlags & JOY_RETURNBUTTONS ) {
317 		for ( i = 0; i < joystick->nbuttons; ++i ) {
318 			if ( joyinfo.dwButtons & JOY_BUTTON_FLAG(i) ) {
319 				if ( ! joystick->buttons[i] ) {
320 					SDL_PrivateJoystickButton(joystick, (Uint8)i, SDL_PRESSED);
321 				}
322 			} else {
323 				if ( joystick->buttons[i] ) {
324 					SDL_PrivateJoystickButton(joystick, (Uint8)i, SDL_RELEASED);
325 				}
326 			}
327 		}
328 	}
329 
330 	/* joystick hat events */
331 	if ( joyinfo.dwFlags & JOY_RETURNPOV ) {
332 		Uint8 pos;
333 
334 		pos = TranslatePOV(joyinfo.dwPOV);
335 		if ( pos != joystick->hats[0] ) {
336 			SDL_PrivateJoystickHat(joystick, 0, pos);
337 		}
338 	}
339 }
340 
341 /* Function to close a joystick after use */
SDL_SYS_JoystickClose(SDL_Joystick * joystick)342 void SDL_SYS_JoystickClose(SDL_Joystick *joystick)
343 {
344 	if (joystick->hwdata != NULL) {
345 		/* free system specific hardware data */
346 		SDL_free(joystick->hwdata);
347 		joystick->hwdata = NULL;
348 	}
349 }
350 
351 /* Function to perform any system-specific joystick related cleanup */
SDL_SYS_JoystickQuit(void)352 void SDL_SYS_JoystickQuit(void)
353 {
354 	int i;
355 	for (i = 0; i < MAX_JOYSTICKS; i++) {
356 		if ( SYS_JoystickName[i] != NULL ) {
357 			SDL_free(SYS_JoystickName[i]);
358 			SYS_JoystickName[i] = NULL;
359 		}
360 	}
361 }
362 
363 
364 /* implementation functions */
SetMMerror(char * function,int code)365 void SetMMerror(char *function, int code)
366 {
367 	static char *error;
368 	static char  errbuf[1024];
369 
370 	errbuf[0] = 0;
371 	switch (code)
372 	{
373 		case MMSYSERR_NODRIVER:
374 			error = "Joystick driver not present";
375 		break;
376 
377 		case MMSYSERR_INVALPARAM:
378 		case JOYERR_PARMS:
379 			error = "Invalid parameter(s)";
380 		break;
381 
382 		case MMSYSERR_BADDEVICEID:
383 			error = "Bad device ID";
384 		break;
385 
386 		case JOYERR_UNPLUGGED:
387 			error = "Joystick not attached";
388 		break;
389 
390 		case JOYERR_NOCANDO:
391 			error = "Can't capture joystick input";
392 		break;
393 
394 		default:
395 			SDL_snprintf(errbuf, SDL_arraysize(errbuf),
396 			         "%s: Unknown Multimedia system error: 0x%x",
397 								function, code);
398 		break;
399 	}
400 
401 	if ( ! errbuf[0] ) {
402 		SDL_snprintf(errbuf, SDL_arraysize(errbuf), "%s: %s", function, error);
403 	}
404 	SDL_SetError("%s", errbuf);
405 }
406 
407 #endif /* SDL_JOYSTICK_WINMM */
408