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 */
49static UINT	SYS_JoystickID[MAX_JOYSTICKS];
50static JOYCAPS	SYS_Joystick[MAX_JOYSTICKS];
51static char	*SYS_JoystickName[MAX_JOYSTICKS];
52
53/* The private structure used to keep track of a joystick */
54struct 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 */
68static void SetMMerror(char *function, int code);
69
70
71static 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 */
139int 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 */
176const 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 */
190int 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
247static 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 */
274void 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 */
342void 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 */
352void 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 */
365void 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