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