src/joystick/win32/SDL_dxjoystick.c
author Edgar Simo <bobbens@gmail.com>
Wed, 06 Aug 2008 08:47:35 +0000
branchgsoc2008_force_feedback
changeset 2616 acd5da848404
parent 2580 64fa227c01ce
child 2623 6deba05725ac
permissions -rw-r--r--
Moved the directinput joystick hwdata stuff into a seperate header file.
     1 /*
     2     SDL - Simple DirectMedia Layer
     3     Copyright (C) 1997-2006 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_DINPUT
    25 
    26 /* DirectInput joystick driver; written by Glenn Maynard, based on Andrei de
    27  * A. Formiga's WINMM driver. 
    28  *
    29  * Hats and sliders are completely untested; the app I'm writing this for mostly
    30  * doesn't use them and I don't own any joysticks with them. 
    31  *
    32  * We don't bother to use event notification here.  It doesn't seem to work
    33  * with polled devices, and it's fine to call IDirectInputDevice2_GetDeviceData and
    34  * let it return 0 events. */
    35 
    36 #include "SDL_error.h"
    37 #include "SDL_events.h"
    38 #include "SDL_joystick.h"
    39 #include "../SDL_sysjoystick.h"
    40 #include "../SDL_joystick_c.h"
    41 #include "SDL_dxjoystick_c.h"
    42 
    43 /* an ISO hack for VisualC++ */
    44 #ifdef _MSC_VER
    45 #define   snprintf	_snprintf
    46 #endif
    47 
    48 #define INPUT_QSIZE	32      /* Buffer up to 32 input messages */
    49 #define MAX_JOYSTICKS	8
    50 #define AXIS_MIN	-32768  /* minimum value for axis coordinate */
    51 #define AXIS_MAX	32767   /* maximum value for axis coordinate */
    52 #define JOY_AXIS_THRESHOLD	(((AXIS_MAX)-(AXIS_MIN))/100)   /* 1% motion */
    53 
    54 /* external variables referenced. */
    55 extern HINSTANCE SDL_Instance;
    56 extern HWND SDL_HelperWindow;
    57 
    58 
    59 /* local variables */
    60 static LPDIRECTINPUT dinput = NULL;
    61 extern HRESULT(WINAPI * DInputCreate) (HINSTANCE hinst, DWORD dwVersion,
    62                                        LPDIRECTINPUT * ppDI,
    63                                        LPUNKNOWN punkOuter);
    64 static DIDEVICEINSTANCE SYS_Joystick[MAX_JOYSTICKS];    /* array to hold joystick ID values */
    65 static int SYS_NumJoysticks;
    66 static HINSTANCE DInputDLL = NULL;
    67 
    68 
    69 /* local prototypes */
    70 static void SetDIerror(const char *function, HRESULT code);
    71 static BOOL CALLBACK EnumJoysticksCallback(const DIDEVICEINSTANCE *
    72                                            pdidInstance, VOID * pContext);
    73 static BOOL CALLBACK EnumDevObjectsCallback(LPCDIDEVICEOBJECTINSTANCE dev,
    74                                             LPVOID pvRef);
    75 static Uint8 TranslatePOV(DWORD value);
    76 static int SDL_PrivateJoystickAxis_Int(SDL_Joystick * joystick, Uint8 axis,
    77                                        Sint16 value);
    78 static int SDL_PrivateJoystickHat_Int(SDL_Joystick * joystick, Uint8 hat,
    79                                       Uint8 value);
    80 static int SDL_PrivateJoystickButton_Int(SDL_Joystick * joystick,
    81                                          Uint8 button, Uint8 state);
    82 
    83 
    84 /* Convert a DirectInput return code to a text message */
    85 static void
    86 SetDIerror(const char *function, HRESULT code)
    87 {
    88     SDL_SetError("%s() [%s]: %s", function,
    89                  DXGetErrorString(code), DXGetErrorDescription(code));
    90 }
    91 
    92 
    93 /* Function to scan the system for joysticks.
    94  * This function should set SDL_numjoysticks to the number of available
    95  * joysticks.  Joystick 0 should be the system default joystick.
    96  * It should return 0, or -1 on an unrecoverable fatal error.
    97  */
    98 int
    99 SDL_SYS_JoystickInit(void)
   100 {
   101     HRESULT result;
   102 
   103     SYS_NumJoysticks = 0;
   104 
   105     result = CoInitialize(NULL);
   106     if (FAILED(result)) {
   107         SetDIerror("CoInitialize", result);
   108         return (-1);
   109     }
   110 
   111     result = CoCreateInstance(&CLSID_DirectInput, NULL, CLSCTX_INPROC_SERVER,
   112                               &IID_IDirectInput, &dinput);
   113 
   114     if (FAILED(result)) {
   115         SetDIerror("CoCreateInstance", result);
   116         return (-1);
   117     }
   118 
   119     /* Because we used CoCreateInstance, we need to Initialize it, first. */
   120     result =
   121         IDirectInput_Initialize(dinput, SDL_Instance, DIRECTINPUT_VERSION);
   122 
   123     if (FAILED(result)) {
   124         SetDIerror("IDirectInput::Initialize", result);
   125         return (-1);
   126     }
   127 
   128     /* Look for joysticks, wheels, head trackers, gamepads, etc.. */
   129     result = IDirectInput_EnumDevices(dinput,
   130                                       DIDEVTYPE_JOYSTICK,
   131                                       EnumJoysticksCallback,
   132                                       NULL, DIEDFL_ATTACHEDONLY);
   133 
   134     return SYS_NumJoysticks;
   135 }
   136 
   137 static BOOL CALLBACK
   138 EnumJoysticksCallback(const DIDEVICEINSTANCE * pdidInstance, VOID * pContext)
   139 {
   140     SDL_memcpy(&SYS_Joystick[SYS_NumJoysticks], pdidInstance,
   141            sizeof(DIDEVICEINSTANCE));
   142     SYS_NumJoysticks++;
   143 
   144     if (SYS_NumJoysticks >= MAX_JOYSTICKS)
   145         return DIENUM_STOP;
   146 
   147     return DIENUM_CONTINUE;
   148 }
   149 
   150 /* Function to get the device-dependent name of a joystick */
   151 const char *
   152 SDL_SYS_JoystickName(int index)
   153 {
   154         /***-> test for invalid index ? */
   155     return (SYS_Joystick[index].tszProductName);
   156 }
   157 
   158 /* Function to open a joystick for use.
   159    The joystick to open is specified by the index field of the joystick.
   160    This should fill the nbuttons and naxes fields of the joystick structure.
   161    It returns 0, or -1 if there is an error.
   162  */
   163 int
   164 SDL_SYS_JoystickOpen(SDL_Joystick * joystick)
   165 {
   166     HRESULT result;
   167     LPDIRECTINPUTDEVICE device;
   168     DIPROPDWORD dipdw;
   169 
   170     SDL_memset(&dipdw, 0, sizeof(DIPROPDWORD));
   171     dipdw.diph.dwSize = sizeof(DIPROPDWORD);
   172     dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
   173 
   174 
   175     /* allocate memory for system specific hardware data */
   176     joystick->hwdata =
   177         (struct joystick_hwdata *) SDL_malloc(sizeof(struct joystick_hwdata));
   178     if (joystick->hwdata == NULL) {
   179         SDL_OutOfMemory();
   180         return (-1);
   181     }
   182     SDL_memset(joystick->hwdata, 0, sizeof(struct joystick_hwdata));
   183     joystick->hwdata->buffered = 1;
   184     joystick->hwdata->Capabilities.dwSize = sizeof(DIDEVCAPS);
   185 
   186     result =
   187         IDirectInput_CreateDevice(dinput,
   188                                   &SYS_Joystick[joystick->index].
   189                                   guidInstance, &device, NULL);
   190     if (FAILED(result)) {
   191         SetDIerror("IDirectInput::CreateDevice", result);
   192         return (-1);
   193     }
   194 
   195     /* Now get the IDirectInputDevice2 interface, instead. */
   196     result = IDirectInputDevice_QueryInterface(device,
   197                                                &IID_IDirectInputDevice2,
   198                                                (LPVOID *) & joystick->
   199                                                hwdata->InputDevice);
   200     /* We are done with this object.  Use the stored one from now on. */
   201     IDirectInputDevice_Release(device);
   202 
   203     if (FAILED(result)) {
   204         SetDIerror("IDirectInputDevice::QueryInterface", result);
   205         return (-1);
   206     }
   207 
   208     /* Aquire shared access. Exclusive access is required for forces,
   209      * though. */
   210     result =
   211         IDirectInputDevice2_SetCooperativeLevel(joystick->hwdata->
   212                                                 InputDevice, SDL_HelperWindow,
   213                                                 DISCL_EXCLUSIVE |
   214                                                 DISCL_BACKGROUND);
   215     if (FAILED(result)) {
   216         SetDIerror("IDirectInputDevice2::SetCooperativeLevel", result);
   217         return (-1);
   218     }
   219 
   220     /* Use the extended data structure: DIJOYSTATE2. */
   221     result =
   222         IDirectInputDevice2_SetDataFormat(joystick->hwdata->InputDevice,
   223                                           &c_dfDIJoystick2);
   224     if (FAILED(result)) {
   225         SetDIerror("IDirectInputDevice2::SetDataFormat", result);
   226         return (-1);
   227     }
   228 
   229     /* Get device capabilities */
   230     result =
   231         IDirectInputDevice2_GetCapabilities(joystick->hwdata->InputDevice,
   232                                             &joystick->hwdata->Capabilities);
   233 
   234     if (FAILED(result)) {
   235         SetDIerror("IDirectInputDevice2::GetCapabilities", result);
   236         return (-1);
   237     }
   238 
   239     /* Force capable? */
   240     if (joystick->hwdata->Capabilities.dwFlags & DIDC_FORCEFEEDBACK) {
   241 
   242         result = IDirectInputDevice2_Acquire(joystick->hwdata->InputDevice);
   243 
   244         if (FAILED(result)) {
   245             SetDIerror("IDirectInputDevice2::Acquire", result);
   246             return (-1);
   247         }
   248 
   249         /* reset all accuators. */
   250         result =
   251             IDirectInputDevice2_SendForceFeedbackCommand(joystick->hwdata->
   252                                                          InputDevice,
   253                                                          DISFFC_RESET);
   254 
   255         if (FAILED(result)) {
   256             SetDIerror("IDirectInputDevice2::SendForceFeedbackCommand",
   257                        result);
   258             return (-1);
   259         }
   260 
   261         result = IDirectInputDevice2_Unacquire(joystick->hwdata->InputDevice);
   262 
   263         if (FAILED(result)) {
   264             SetDIerror("IDirectInputDevice2::Unacquire", result);
   265             return (-1);
   266         }
   267 
   268         /* Turn on auto-centering for a ForceFeedback device (until told
   269          * otherwise). */
   270         dipdw.diph.dwObj = 0;
   271         dipdw.diph.dwHow = DIPH_DEVICE;
   272         dipdw.dwData = DIPROPAUTOCENTER_ON;
   273 
   274         result =
   275             IDirectInputDevice2_SetProperty(joystick->hwdata->InputDevice,
   276                                             DIPROP_AUTOCENTER, &dipdw.diph);
   277 
   278         if (FAILED(result)) {
   279             SetDIerror("IDirectInputDevice2::SetProperty", result);
   280             return (-1);
   281         }
   282     }
   283 
   284     /* What buttons and axes does it have? */
   285     IDirectInputDevice2_EnumObjects(joystick->hwdata->InputDevice,
   286                                     EnumDevObjectsCallback, joystick,
   287                                     DIDFT_BUTTON | DIDFT_AXIS | DIDFT_POV);
   288 
   289     dipdw.diph.dwObj = 0;
   290     dipdw.diph.dwHow = DIPH_DEVICE;
   291     dipdw.dwData = INPUT_QSIZE;
   292 
   293     /* Set the buffer size */
   294     result =
   295         IDirectInputDevice2_SetProperty(joystick->hwdata->InputDevice,
   296                                         DIPROP_BUFFERSIZE, &dipdw.diph);
   297 
   298     if (result == DI_POLLEDDEVICE) {
   299         /* This device doesn't support buffering, so we're forced
   300          * to use less reliable polling. */
   301         joystick->hwdata->buffered = 0;
   302     } else if (FAILED(result)) {
   303         SetDIerror("IDirectInputDevice2::SetProperty", result);
   304         return (-1);
   305     }
   306 
   307     return (0);
   308 }
   309 
   310 static BOOL CALLBACK
   311 EnumDevObjectsCallback(LPCDIDEVICEOBJECTINSTANCE dev, LPVOID pvRef)
   312 {
   313     SDL_Joystick *joystick = (SDL_Joystick *) pvRef;
   314     HRESULT result;
   315     input_t *in = &joystick->hwdata->Inputs[joystick->hwdata->NumInputs];
   316 
   317     in->ofs = dev->dwOfs;
   318 
   319     if (dev->dwType & DIDFT_BUTTON) {
   320         in->type = BUTTON;
   321         in->num = joystick->nbuttons;
   322         joystick->nbuttons++;
   323     } else if (dev->dwType & DIDFT_POV) {
   324         in->type = HAT;
   325         in->num = joystick->nhats;
   326         joystick->nhats++;
   327     } else if (dev->dwType & DIDFT_AXIS) {
   328         DIPROPRANGE diprg;
   329         DIPROPDWORD dilong;
   330 
   331         in->type = AXIS;
   332         in->num = joystick->naxes;
   333 
   334         diprg.diph.dwSize = sizeof(diprg);
   335         diprg.diph.dwHeaderSize = sizeof(diprg.diph);
   336         diprg.diph.dwObj = dev->dwOfs;
   337         diprg.diph.dwHow = DIPH_BYOFFSET;
   338         diprg.lMin = AXIS_MIN;
   339         diprg.lMax = AXIS_MAX;
   340 
   341         result =
   342             IDirectInputDevice2_SetProperty(joystick->hwdata->InputDevice,
   343                                             DIPROP_RANGE, &diprg.diph);
   344         if (FAILED(result)) {
   345             return DIENUM_CONTINUE;     /* don't use this axis */
   346         }
   347 
   348         /* Set dead zone to 0. */
   349         dilong.diph.dwSize = sizeof(dilong);
   350         dilong.diph.dwHeaderSize = sizeof(dilong.diph);
   351         dilong.diph.dwObj = dev->dwOfs;
   352         dilong.diph.dwHow = DIPH_BYOFFSET;
   353         dilong.dwData = 0;
   354         result =
   355             IDirectInputDevice2_SetProperty(joystick->hwdata->InputDevice,
   356                                             DIPROP_DEADZONE, &dilong.diph);
   357         if (FAILED(result)) {
   358             return DIENUM_CONTINUE;     /* don't use this axis */
   359         }
   360 
   361         joystick->naxes++;
   362     } else {
   363         /* not supported at this time */
   364         return DIENUM_CONTINUE;
   365     }
   366 
   367     joystick->hwdata->NumInputs++;
   368 
   369     if (joystick->hwdata->NumInputs == MAX_INPUTS) {
   370         return DIENUM_STOP;     /* too many */
   371     }
   372 
   373     return DIENUM_CONTINUE;
   374 }
   375 
   376 /* Function to update the state of a joystick - called as a device poll.
   377  * This function shouldn't update the joystick structure directly,
   378  * but instead should call SDL_PrivateJoystick*() to deliver events
   379  * and update joystick device state.
   380  */
   381 void
   382 SDL_SYS_JoystickUpdate_Polled(SDL_Joystick * joystick)
   383 {
   384     DIJOYSTATE2 state;
   385     HRESULT result;
   386     int i;
   387 
   388     result =
   389         IDirectInputDevice2_GetDeviceState(joystick->hwdata->InputDevice,
   390                                            sizeof(DIJOYSTATE2), &state);
   391     if (result == DIERR_INPUTLOST || result == DIERR_NOTACQUIRED) {
   392         IDirectInputDevice2_Acquire(joystick->hwdata->InputDevice);
   393         result =
   394             IDirectInputDevice2_GetDeviceState(joystick->hwdata->InputDevice,
   395                                                sizeof(DIJOYSTATE2), &state);
   396     }
   397 
   398     /* Set each known axis, button and POV. */
   399     for (i = 0; i < joystick->hwdata->NumInputs; ++i) {
   400         const input_t *in = &joystick->hwdata->Inputs[i];
   401 
   402         switch (in->type) {
   403         case AXIS:
   404             switch (in->ofs) {
   405             case DIJOFS_X:
   406                 SDL_PrivateJoystickAxis_Int(joystick, in->num,
   407                                             (Sint16) state.lX);
   408                 break;
   409             case DIJOFS_Y:
   410                 SDL_PrivateJoystickAxis_Int(joystick, in->num,
   411                                             (Sint16) state.lY);
   412                 break;
   413             case DIJOFS_Z:
   414                 SDL_PrivateJoystickAxis_Int(joystick, in->num,
   415                                             (Sint16) state.lZ);
   416                 break;
   417             case DIJOFS_RX:
   418                 SDL_PrivateJoystickAxis_Int(joystick, in->num,
   419                                             (Sint16) state.lRx);
   420                 break;
   421             case DIJOFS_RY:
   422                 SDL_PrivateJoystickAxis_Int(joystick, in->num,
   423                                             (Sint16) state.lRy);
   424                 break;
   425             case DIJOFS_RZ:
   426                 SDL_PrivateJoystickAxis_Int(joystick, in->num,
   427                                             (Sint16) state.lRz);
   428                 break;
   429             case DIJOFS_SLIDER(0):
   430                 SDL_PrivateJoystickAxis_Int(joystick, in->num,
   431                                             (Sint16) state.rglSlider[0]);
   432                 break;
   433             case DIJOFS_SLIDER(1):
   434                 SDL_PrivateJoystickAxis_Int(joystick, in->num,
   435                                             (Sint16) state.rglSlider[1]);
   436                 break;
   437             }
   438 
   439             break;
   440 
   441         case BUTTON:
   442             SDL_PrivateJoystickButton_Int(joystick, in->num,
   443                                           (Uint8) (state.
   444                                                    rgbButtons[in->ofs -
   445                                                               DIJOFS_BUTTON0]
   446                                                    ? SDL_PRESSED :
   447                                                    SDL_RELEASED));
   448             break;
   449         case HAT:
   450             {
   451                 Uint8 pos = TranslatePOV(state.rgdwPOV[in->ofs -
   452                                                        DIJOFS_POV(0)]);
   453                 SDL_PrivateJoystickHat_Int(joystick, in->num, pos);
   454                 break;
   455             }
   456         }
   457     }
   458 }
   459 
   460 void
   461 SDL_SYS_JoystickUpdate_Buffered(SDL_Joystick * joystick)
   462 {
   463     int i;
   464     HRESULT result;
   465     DWORD numevents;
   466     DIDEVICEOBJECTDATA evtbuf[INPUT_QSIZE];
   467 
   468     numevents = INPUT_QSIZE;
   469     result =
   470         IDirectInputDevice2_GetDeviceData(joystick->hwdata->InputDevice,
   471                                           sizeof(DIDEVICEOBJECTDATA), evtbuf,
   472                                           &numevents, 0);
   473     if (result == DIERR_INPUTLOST || result == DIERR_NOTACQUIRED) {
   474         IDirectInputDevice2_Acquire(joystick->hwdata->InputDevice);
   475         result =
   476             IDirectInputDevice2_GetDeviceData(joystick->hwdata->InputDevice,
   477                                               sizeof(DIDEVICEOBJECTDATA),
   478                                               evtbuf, &numevents, 0);
   479     }
   480 
   481     /* Handle the events or punt */
   482     if (FAILED(result))
   483         return;
   484 
   485     for (i = 0; i < (int) numevents; ++i) {
   486         int j;
   487 
   488         for (j = 0; j < joystick->hwdata->NumInputs; ++j) {
   489             const input_t *in = &joystick->hwdata->Inputs[j];
   490 
   491             if (evtbuf[i].dwOfs != in->ofs)
   492                 continue;
   493 
   494             switch (in->type) {
   495             case AXIS:
   496                 SDL_PrivateJoystickAxis(joystick, in->num,
   497                                         (Sint16) evtbuf[i].dwData);
   498                 break;
   499             case BUTTON:
   500                 SDL_PrivateJoystickButton(joystick, in->num,
   501                                           (Uint8) (evtbuf[i].
   502                                                    dwData ? SDL_PRESSED :
   503                                                    SDL_RELEASED));
   504                 break;
   505             case HAT:
   506                 {
   507                     Uint8 pos = TranslatePOV(evtbuf[i].dwData);
   508                     SDL_PrivateJoystickHat(joystick, in->num, pos);
   509                 }
   510             }
   511         }
   512     }
   513 }
   514 
   515 
   516 static Uint8
   517 TranslatePOV(DWORD value)
   518 {
   519     const int HAT_VALS[] = {
   520         SDL_HAT_UP,
   521         SDL_HAT_UP | SDL_HAT_RIGHT,
   522         SDL_HAT_RIGHT,
   523         SDL_HAT_DOWN | SDL_HAT_RIGHT,
   524         SDL_HAT_DOWN,
   525         SDL_HAT_DOWN | SDL_HAT_LEFT,
   526         SDL_HAT_LEFT,
   527         SDL_HAT_UP | SDL_HAT_LEFT
   528     };
   529 
   530     if (LOWORD(value) == 0xFFFF)
   531         return SDL_HAT_CENTERED;
   532 
   533     /* Round the value up: */
   534     value += 4500 / 2;
   535     value %= 36000;
   536     value /= 4500;
   537 
   538     if (value >= 8)
   539         return SDL_HAT_CENTERED;        /* shouldn't happen */
   540 
   541     return HAT_VALS[value];
   542 }
   543 
   544 /* SDL_PrivateJoystick* doesn't discard duplicate events, so we need to
   545  * do it. */
   546 static int
   547 SDL_PrivateJoystickAxis_Int(SDL_Joystick * joystick, Uint8 axis, Sint16 value)
   548 {
   549     if (joystick->axes[axis] != value)
   550         return SDL_PrivateJoystickAxis(joystick, axis, value);
   551     return 0;
   552 }
   553 
   554 static int
   555 SDL_PrivateJoystickHat_Int(SDL_Joystick * joystick, Uint8 hat, Uint8 value)
   556 {
   557     if (joystick->hats[hat] != value)
   558         return SDL_PrivateJoystickHat(joystick, hat, value);
   559     return 0;
   560 }
   561 
   562 static int
   563 SDL_PrivateJoystickButton_Int(SDL_Joystick * joystick, Uint8 button,
   564                               Uint8 state)
   565 {
   566     if (joystick->buttons[button] != state)
   567         return SDL_PrivateJoystickButton(joystick, button, state);
   568     return 0;
   569 }
   570 
   571 void
   572 SDL_SYS_JoystickUpdate(SDL_Joystick * joystick)
   573 {
   574     HRESULT result;
   575 
   576     result = IDirectInputDevice2_Poll(joystick->hwdata->InputDevice);
   577     if (result == DIERR_INPUTLOST || result == DIERR_NOTACQUIRED) {
   578         IDirectInputDevice2_Acquire(joystick->hwdata->InputDevice);
   579         IDirectInputDevice2_Poll(joystick->hwdata->InputDevice);
   580     }
   581 
   582     if (joystick->hwdata->buffered)
   583         SDL_SYS_JoystickUpdate_Buffered(joystick);
   584     else
   585         SDL_SYS_JoystickUpdate_Polled(joystick);
   586 }
   587 
   588 /* Function to close a joystick after use */
   589 void
   590 SDL_SYS_JoystickClose(SDL_Joystick * joystick)
   591 {
   592     IDirectInputDevice2_Unacquire(joystick->hwdata->InputDevice);
   593     IDirectInputDevice2_Release(joystick->hwdata->InputDevice);
   594 
   595     if (joystick->hwdata != NULL) {
   596         /* free system specific hardware data */
   597        SDL_free(joystick->hwdata);
   598     }
   599 }
   600 
   601 /* Function to perform any system-specific joystick related cleanup */
   602 void
   603 SDL_SYS_JoystickQuit(void)
   604 {
   605     IDirectInput_Release(dinput);
   606     dinput = NULL;
   607 }
   608 
   609 #endif /* SDL_JOYSTICK_DINPUT */
   610 /* vi: set ts=4 sw=4 expandtab: */