Home | History | Annotate | Download | only in win32
      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 (at) 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 
     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  */
    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 */
    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  */
    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 
    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  */
    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 */
    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 */
    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 */
    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