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