src/joystick/win32/SDL_mmjoystick.c
author Sam Lantinga <slouken@libsdl.org>
Wed, 12 Nov 2008 17:23:40 +0000
branchSDL-1.2
changeset 4158 96ce26f24b01
parent 4051 209a3ef8a328
child 4159 a1b03ba2fcd0
permissions -rw-r--r--
Date: Sun, 7 Sep 2008 15:17:00 +0200
From: c2woody@gmx.net
Subject: [SDL] SDL 1.2 doube free/pointer zeroing missing

Hello,

this is about a crash/debug breakage for the current SDL 1.2
source tree (today's svn checkout, same problem in 1.2.13 and
before as far as relevant).
In some places memory is free()d but the associated pointer
is not zeroed, leading to for example double free()s.

For me this happened because SDL_StopEventThread() was executed
twice (during restart of the subsystems), once for the close
down in SDL_VideoQuit() and once at the startup, right at the
beginning of SDL_StartEventLoop(). Thus the code
SDL_DestroyMutex(SDL_EventQ.lock);
(see SDL_events.c) was called twice and executed the SDL_free(mutex);
twice as well, leading to a crash (msvc 64bit for which it was noticed).

I've tried to check all other occurrences of SDL_free and similar
code in msvc, see the attached patch (udiff against revision 4082).
Non-windows only codepaths have neither been checked nor touched.

Comments/ideas welcome.

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