src/joystick/win32/SDL_mmjoystick.c
author Sam Lantinga <slouken@libsdl.org>
Sat, 21 Aug 2004 03:45:58 +0000
changeset 937 1e6366bde299
parent 834 d37179d10ccc
child 938 fa2ce068b0b6
permissions -rw-r--r--
Date: Tue, 27 Jul 2004 17:14:00 +0200
From: "Eckhard Stolberg"
Subject: Controller names in SDL for Windows

I'm working on an Atari 2600 emulator for different systems that uses
the SDL. Some time ago someone created an adaptor that lets you use
your old Atari controllers with your computer through the USB port.
Some of the Atari controllers require special handling by the emulator,
so it would be nice, if it would be possible to detect if any of the
controllers connected to the computer is this adaptor.

SDL would allow that with the SDL_JoystickName function, but unfortunately
it doesn't work properly on Windows. On Linux and MacOSX this function
returns the name of the controller, but on Windows you'll only get the
name of the joystick driver. Most joysticks nowadays use the generic
Microsoft driver, so they all return the same name.

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