src/joystick/win32/SDL_dxjoystick.c
author Sam Lantinga <slouken@libsdl.org>
Sun, 28 May 2006 13:04:16 +0000
branchSDL-1.3
changeset 1662 782fd950bd46
parent 1661 281d3f4870e5
child 1668 4da1ee79c9af
permissions -rw-r--r--
Revamp of the video system in progress - adding support for multiple displays, multiple windows, and a full video mode selection API.

WARNING: None of the video drivers have been updated for the new API yet! The API is still under design and very fluid.

The code is now run through a consistent indent format:
indent -i4 -nut -nsc -br -ce

The headers are being converted to automatically generate doxygen documentation.
     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 
    42 #define WIN32_LEAN_AND_MEAN
    43 #include <windows.h>
    44 
    45 #define DIRECTINPUT_VERSION 0x0500
    46 #include <dinput.h>
    47 
    48 #define INPUT_QSIZE	32      /* Buffer up to 32 input messages */
    49 
    50 extern HINSTANCE SDL_Instance;
    51 extern int DX5_Load ();
    52 extern void DX5_Unload ();
    53 extern HRESULT (WINAPI * DInputCreate) (HINSTANCE hinst, DWORD dwVersion,
    54                                         LPDIRECTINPUT * ppDI,
    55                                         LPUNKNOWN punkOuter);
    56 
    57 static LPDIRECTINPUT dinput = NULL;
    58 
    59 #define MAX_JOYSTICKS	8
    60 #define MAX_INPUTS	256     /* each joystick can have up to 256 inputs */
    61 #define AXIS_MIN	-32768  /* minimum value for axis coordinate */
    62 #define AXIS_MAX	32767   /* maximum value for axis coordinate */
    63 #define JOY_AXIS_THRESHOLD	(((AXIS_MAX)-(AXIS_MIN))/100)   /* 1% motion */
    64 
    65 typedef enum Type
    66 { BUTTON, AXIS, HAT } Type;
    67 
    68 /* array to hold joystick ID values */
    69 static DIDEVICEINSTANCE SYS_Joystick[MAX_JOYSTICKS];
    70 static int SYS_NumJoysticks;
    71 
    72 extern HWND SDL_Window;
    73 
    74 typedef struct input_t
    75 {
    76     /* DirectInput offset for this input type: */
    77     DWORD ofs;
    78 
    79     /* Button, axis or hat: */
    80     Type type;
    81 
    82     /* SDL input offset: */
    83     Uint8 num;
    84 } input_t;
    85 
    86 /* The private structure used to keep track of a joystick */
    87 struct joystick_hwdata
    88 {
    89     LPDIRECTINPUTDEVICE2 InputDevice;
    90     int buffered;
    91 
    92     input_t Inputs[MAX_INPUTS];
    93     int NumInputs;
    94 };
    95 
    96 /* Convert a DirectInput return code to a text message */
    97 static void
    98 SetDIerror (char *function, int code)
    99 {
   100     static char *error;
   101     static char errbuf[1024];
   102 
   103     errbuf[0] = 0;
   104     switch (code) {
   105     case DIERR_GENERIC:
   106         error = "Undefined error!";
   107         break;
   108     case DIERR_OLDDIRECTINPUTVERSION:
   109         error = "Your version of DirectInput needs upgrading";
   110         break;
   111     case DIERR_INVALIDPARAM:
   112         error = "Invalid parameters";
   113         break;
   114     case DIERR_OUTOFMEMORY:
   115         error = "Out of memory";
   116         break;
   117     case DIERR_DEVICENOTREG:
   118         error = "Device not registered";
   119         break;
   120     case DIERR_NOINTERFACE:
   121         error = "Interface not supported";
   122         break;
   123     case DIERR_NOTINITIALIZED:
   124         error = "Device not initialized";
   125         break;
   126     default:
   127         sprintf (errbuf, "%s: Unknown DirectInput error: 0x%x",
   128                  function, code);
   129         break;
   130     }
   131     if (!errbuf[0]) {
   132         sprintf (errbuf, "%s: %s", function, error);
   133     }
   134     SDL_SetError ("%s", errbuf);
   135     return;
   136 }
   137 
   138 
   139 BOOL CALLBACK
   140 EnumJoysticksCallback (const DIDEVICEINSTANCE * pdidInstance, VOID * pContext)
   141 {
   142     memcpy (&SYS_Joystick[SYS_NumJoysticks], pdidInstance,
   143             sizeof (DIDEVICEINSTANCE));
   144     SYS_NumJoysticks++;
   145 
   146     if (SYS_NumJoysticks >= MAX_JOYSTICKS)
   147         return DIENUM_STOP;
   148 
   149     return DIENUM_CONTINUE;
   150 }
   151 
   152 static BOOL CALLBACK
   153 DIJoystick_EnumDevObjectsProc (LPCDIDEVICEOBJECTINSTANCE dev, LPVOID pvRef)
   154 {
   155     SDL_Joystick *joystick = (SDL_Joystick *) pvRef;
   156     HRESULT result;
   157     input_t *in = &joystick->hwdata->Inputs[joystick->hwdata->NumInputs];
   158     const int SupportedMask = DIDFT_BUTTON | DIDFT_POV | DIDFT_AXIS;
   159     if (!(dev->dwType & SupportedMask))
   160         return DIENUM_CONTINUE; /* unsupported */
   161 
   162     in->ofs = dev->dwOfs;
   163 
   164     if (dev->dwType & DIDFT_BUTTON) {
   165         in->type = BUTTON;
   166         in->num = joystick->nbuttons;
   167         joystick->nbuttons++;
   168     } else if (dev->dwType & DIDFT_POV) {
   169         in->type = HAT;
   170         in->num = joystick->nhats;
   171         joystick->nhats++;
   172     } else {                    /* dev->dwType & DIDFT_AXIS */
   173         DIPROPRANGE diprg;
   174         DIPROPDWORD dilong;
   175 
   176         in->type = AXIS;
   177         in->num = joystick->naxes;
   178 
   179         diprg.diph.dwSize = sizeof (diprg);
   180         diprg.diph.dwHeaderSize = sizeof (diprg.diph);
   181         diprg.diph.dwObj = dev->dwOfs;
   182         diprg.diph.dwHow = DIPH_BYOFFSET;
   183         diprg.lMin = AXIS_MIN;
   184         diprg.lMax = AXIS_MAX;
   185 
   186         result =
   187             IDirectInputDevice2_SetProperty (joystick->hwdata->InputDevice,
   188                                              DIPROP_RANGE, &diprg.diph);
   189         if (result != DI_OK)
   190             return DIENUM_CONTINUE;     /* don't use this axis */
   191 
   192         /* Set dead zone to 0. */
   193         dilong.diph.dwSize = sizeof (dilong);
   194         dilong.diph.dwHeaderSize = sizeof (dilong.diph);
   195         dilong.diph.dwObj = dev->dwOfs;
   196         dilong.diph.dwHow = DIPH_BYOFFSET;
   197         dilong.dwData = 0;
   198         result =
   199             IDirectInputDevice2_SetProperty (joystick->hwdata->InputDevice,
   200                                              DIPROP_DEADZONE, &dilong.diph);
   201         if (result != DI_OK)
   202             return DIENUM_CONTINUE;     /* don't use this axis */
   203 
   204         joystick->naxes++;
   205     }
   206 
   207     joystick->hwdata->NumInputs++;
   208 
   209     if (joystick->hwdata->NumInputs == MAX_INPUTS)
   210         return DIENUM_STOP;     /* too many */
   211 
   212     return DIENUM_CONTINUE;
   213 }
   214 
   215 /* Function to scan the system for joysticks.
   216  * This function should set SDL_numjoysticks to the number of available
   217  * joysticks.  Joystick 0 should be the system default joystick.
   218  * It should return 0, or -1 on an unrecoverable fatal error.
   219  */
   220 int
   221 SDL_SYS_JoystickInit (void)
   222 {
   223     HRESULT result;
   224 
   225     SYS_NumJoysticks = 0;
   226 
   227     /* Create the DirectInput object */
   228     if (DX5_Load () < 0) {
   229         SDL_SetError ("Couldn't load DirectInput");
   230         return (-1);
   231     }
   232     result = DInputCreate (SDL_Instance, DIRECTINPUT_VERSION, &dinput, NULL);
   233     if (result != DI_OK) {
   234         DX5_Unload ();
   235         SetDIerror ("DirectInputCreate", result);
   236         return (-1);
   237     }
   238 
   239     result = IDirectInput_EnumDevices (dinput,
   240                                        DIDEVTYPE_JOYSTICK,
   241                                        EnumJoysticksCallback,
   242                                        NULL, DIEDFL_ATTACHEDONLY);
   243 
   244     return SYS_NumJoysticks;
   245 }
   246 
   247 /* Function to get the device-dependent name of a joystick */
   248 const char *
   249 SDL_SYS_JoystickName (int index)
   250 {
   251         /***-> test for invalid index ? */
   252     return (SYS_Joystick[index].tszProductName);
   253 }
   254 
   255 /* Function to open a joystick for use.
   256    The joystick to open is specified by the index field of the joystick.
   257    This should fill the nbuttons and naxes fields of the joystick structure.
   258    It returns 0, or -1 if there is an error.
   259  */
   260 int
   261 SDL_SYS_JoystickOpen (SDL_Joystick * joystick)
   262 {
   263     HRESULT result;
   264     LPDIRECTINPUTDEVICE device;
   265 
   266     /* allocate memory for system specific hardware data */
   267     joystick->hwdata =
   268         (struct joystick_hwdata *) malloc (sizeof (*joystick->hwdata));
   269     if (joystick->hwdata == NULL) {
   270         SDL_OutOfMemory ();
   271         return (-1);
   272     }
   273     memset (joystick->hwdata, 0, sizeof (*joystick->hwdata));
   274     joystick->hwdata->buffered = 1;
   275 
   276     result =
   277         IDirectInput_CreateDevice (dinput,
   278                                    &SYS_Joystick[joystick->index].
   279                                    guidInstance, &device, NULL);
   280     if (result != DI_OK) {
   281         SetDIerror ("DirectInput::CreateDevice", result);
   282         return (-1);
   283     }
   284 
   285     result = IDirectInputDevice_QueryInterface (device,
   286                                                 &IID_IDirectInputDevice2,
   287                                                 (LPVOID *) & joystick->
   288                                                 hwdata->InputDevice);
   289     IDirectInputDevice_Release (device);
   290     if (result != DI_OK) {
   291         SetDIerror ("DirectInputDevice::QueryInterface", result);
   292         return (-1);
   293     }
   294 
   295     result =
   296         IDirectInputDevice2_SetCooperativeLevel (joystick->hwdata->
   297                                                  InputDevice, SDL_Window,
   298                                                  DISCL_NONEXCLUSIVE |
   299                                                  DISCL_BACKGROUND);
   300     if (result != DI_OK) {
   301         SetDIerror ("DirectInputDevice::SetCooperativeLevel", result);
   302         return (-1);
   303     }
   304 
   305     result =
   306         IDirectInputDevice2_SetDataFormat (joystick->hwdata->InputDevice,
   307                                            &c_dfDIJoystick);
   308     if (result != DI_OK) {
   309         SetDIerror ("DirectInputDevice::SetDataFormat", result);
   310         return (-1);
   311     }
   312 
   313     IDirectInputDevice2_EnumObjects (joystick->hwdata->InputDevice,
   314                                      DIJoystick_EnumDevObjectsProc,
   315                                      joystick,
   316                                      DIDFT_BUTTON | DIDFT_AXIS | DIDFT_POV);
   317 
   318     {
   319         DIPROPDWORD dipdw;
   320         memset (&dipdw, 0, sizeof (dipdw));
   321         dipdw.diph.dwSize = sizeof (dipdw);
   322         dipdw.diph.dwHeaderSize = sizeof (dipdw.diph);
   323         dipdw.diph.dwObj = 0;
   324         dipdw.diph.dwHow = DIPH_DEVICE;
   325         dipdw.dwData = INPUT_QSIZE;
   326         result =
   327             IDirectInputDevice2_SetProperty (joystick->hwdata->InputDevice,
   328                                              DIPROP_BUFFERSIZE, &dipdw.diph);
   329 
   330         if (result == DI_POLLEDDEVICE) {
   331             /* This device doesn't support buffering, so we're forced
   332              * to use less reliable polling. */
   333             joystick->hwdata->buffered = 0;
   334         } else if (result != DI_OK) {
   335             SetDIerror ("DirectInputDevice::SetProperty", result);
   336             return (-1);
   337         }
   338     }
   339 
   340     return (0);
   341 }
   342 
   343 static Uint8
   344 TranslatePOV (DWORD value)
   345 {
   346     const int HAT_VALS[] = {
   347         SDL_HAT_UP,
   348         SDL_HAT_UP | SDL_HAT_RIGHT,
   349         SDL_HAT_RIGHT,
   350         SDL_HAT_DOWN | SDL_HAT_RIGHT,
   351         SDL_HAT_DOWN,
   352         SDL_HAT_DOWN | SDL_HAT_LEFT,
   353         SDL_HAT_LEFT,
   354         SDL_HAT_UP | SDL_HAT_LEFT
   355     };
   356 
   357     if (LOWORD (value) == 0xFFFF)
   358         return SDL_HAT_CENTERED;
   359 
   360     /* Round the value up: */
   361     value += 4500 / 2;
   362     value %= 36000;
   363     value /= 4500;
   364 
   365     if (value >= 8)
   366         return SDL_HAT_CENTERED;        /* shouldn't happen */
   367 
   368     return HAT_VALS[value];
   369 }
   370 
   371 /* SDL_PrivateJoystick* doesn't discard duplicate events, so we need to
   372  * do it. */
   373 static int
   374 SDL_PrivateJoystickAxis_Int (SDL_Joystick * joystick, Uint8 axis,
   375                              Sint16 value)
   376 {
   377     if (joystick->axes[axis] != value)
   378         return SDL_PrivateJoystickAxis (joystick, axis, value);
   379     return 0;
   380 }
   381 
   382 static int
   383 SDL_PrivateJoystickHat_Int (SDL_Joystick * joystick, Uint8 hat, Uint8 value)
   384 {
   385     if (joystick->hats[hat] != value)
   386         return SDL_PrivateJoystickHat (joystick, hat, value);
   387     return 0;
   388 }
   389 
   390 static int
   391 SDL_PrivateJoystickButton_Int (SDL_Joystick * joystick, Uint8 button,
   392                                Uint8 state)
   393 {
   394     if (joystick->buttons[button] != state)
   395         return SDL_PrivateJoystickButton (joystick, button, state);
   396     return 0;
   397 }
   398 
   399 /* Function to update the state of a joystick - called as a device poll.
   400  * This function shouldn't update the joystick structure directly,
   401  * but instead should call SDL_PrivateJoystick*() to deliver events
   402  * and update joystick device state.
   403  */
   404 void
   405 SDL_SYS_JoystickUpdate_Polled (SDL_Joystick * joystick)
   406 {
   407     DIJOYSTATE state;
   408     HRESULT result;
   409     int i;
   410 
   411     result =
   412         IDirectInputDevice2_GetDeviceState (joystick->hwdata->InputDevice,
   413                                             sizeof (state), &state);
   414     if (result == DIERR_INPUTLOST || result == DIERR_NOTACQUIRED) {
   415         IDirectInputDevice2_Acquire (joystick->hwdata->InputDevice);
   416         result =
   417             IDirectInputDevice2_GetDeviceState (joystick->hwdata->
   418                                                 InputDevice, sizeof (state),
   419                                                 &state);
   420     }
   421 
   422     /* Set each known axis, button and POV. */
   423     for (i = 0; i < joystick->hwdata->NumInputs; ++i) {
   424         const input_t *in = &joystick->hwdata->Inputs[i];
   425 
   426         switch (in->type) {
   427         case AXIS:
   428             switch (in->ofs) {
   429             case DIJOFS_X:
   430                 SDL_PrivateJoystickAxis_Int (joystick, in->num,
   431                                              (Sint16) state.lX);
   432                 break;
   433             case DIJOFS_Y:
   434                 SDL_PrivateJoystickAxis_Int (joystick, in->num,
   435                                              (Sint16) state.lY);
   436                 break;
   437             case DIJOFS_Z:
   438                 SDL_PrivateJoystickAxis_Int (joystick, in->num,
   439                                              (Sint16) state.lZ);
   440                 break;
   441             case DIJOFS_RX:
   442                 SDL_PrivateJoystickAxis_Int (joystick, in->num,
   443                                              (Sint16) state.lRx);
   444                 break;
   445             case DIJOFS_RY:
   446                 SDL_PrivateJoystickAxis_Int (joystick, in->num,
   447                                              (Sint16) state.lRy);
   448                 break;
   449             case DIJOFS_RZ:
   450                 SDL_PrivateJoystickAxis_Int (joystick, in->num,
   451                                              (Sint16) state.lRz);
   452                 break;
   453             case DIJOFS_SLIDER (0):
   454                 SDL_PrivateJoystickAxis_Int (joystick, in->num,
   455                                              (Sint16) state.rglSlider[0]);
   456                 break;
   457             case DIJOFS_SLIDER (1):
   458                 SDL_PrivateJoystickAxis_Int (joystick, in->num,
   459                                              (Sint16) state.rglSlider[0]);
   460                 break;
   461             }
   462 
   463             break;
   464 
   465         case BUTTON:
   466             SDL_PrivateJoystickButton_Int (joystick, in->num,
   467                                            (Uint8) (state.
   468                                                     rgbButtons[in->ofs -
   469                                                                DIJOFS_BUTTON0]
   470                                                     ? SDL_PRESSED :
   471                                                     SDL_RELEASED));
   472             break;
   473         case HAT:
   474             {
   475                 Uint8 pos =
   476                     TranslatePOV (state.rgdwPOV[in->ofs - DIJOFS_POV (0)]);
   477                 SDL_PrivateJoystickHat_Int (joystick, in->num, pos);
   478                 break;
   479             }
   480         }
   481     }
   482 }
   483 
   484 void
   485 SDL_SYS_JoystickUpdate_Buffered (SDL_Joystick * joystick)
   486 {
   487     int i;
   488     HRESULT result;
   489     DWORD numevents;
   490     DIDEVICEOBJECTDATA evtbuf[INPUT_QSIZE];
   491 
   492     numevents = INPUT_QSIZE;
   493     result =
   494         IDirectInputDevice2_GetDeviceData (joystick->hwdata->InputDevice,
   495                                            sizeof (DIDEVICEOBJECTDATA),
   496                                            evtbuf, &numevents, 0);
   497     if (result == DIERR_INPUTLOST || result == DIERR_NOTACQUIRED) {
   498         IDirectInputDevice2_Acquire (joystick->hwdata->InputDevice);
   499         result =
   500             IDirectInputDevice2_GetDeviceData (joystick->hwdata->
   501                                                InputDevice,
   502                                                sizeof (DIDEVICEOBJECTDATA),
   503                                                evtbuf, &numevents, 0);
   504     }
   505 
   506     /* Handle the events */
   507     if (result != DI_OK)
   508         return;
   509 
   510     for (i = 0; i < (int) numevents; ++i) {
   511         int j;
   512 
   513         for (j = 0; j < joystick->hwdata->NumInputs; ++j) {
   514             const input_t *in = &joystick->hwdata->Inputs[j];
   515 
   516             if (evtbuf[i].dwOfs != in->ofs)
   517                 continue;
   518 
   519             switch (in->type) {
   520             case AXIS:
   521                 SDL_PrivateJoystickAxis (joystick, in->num,
   522                                          (Sint16) evtbuf[i].dwData);
   523                 break;
   524             case BUTTON:
   525                 SDL_PrivateJoystickButton (joystick, in->num,
   526                                            (Uint8) (evtbuf[i].
   527                                                     dwData ? SDL_PRESSED
   528                                                     : SDL_RELEASED));
   529                 break;
   530             case HAT:
   531                 {
   532                     Uint8 pos = TranslatePOV (evtbuf[i].dwData);
   533                     SDL_PrivateJoystickHat (joystick, in->num, pos);
   534                 }
   535             }
   536         }
   537     }
   538 }
   539 
   540 void
   541 SDL_SYS_JoystickUpdate (SDL_Joystick * joystick)
   542 {
   543     HRESULT result;
   544 
   545     result = IDirectInputDevice2_Poll (joystick->hwdata->InputDevice);
   546     if (result == DIERR_INPUTLOST || result == DIERR_NOTACQUIRED) {
   547         IDirectInputDevice2_Acquire (joystick->hwdata->InputDevice);
   548         IDirectInputDevice2_Poll (joystick->hwdata->InputDevice);
   549     }
   550 
   551     if (joystick->hwdata->buffered)
   552         SDL_SYS_JoystickUpdate_Buffered (joystick);
   553     else
   554         SDL_SYS_JoystickUpdate_Polled (joystick);
   555 }
   556 
   557 /* Function to close a joystick after use */
   558 void
   559 SDL_SYS_JoystickClose (SDL_Joystick * joystick)
   560 {
   561     IDirectInputDevice2_Unacquire (joystick->hwdata->InputDevice);
   562     IDirectInputDevice2_Release (joystick->hwdata->InputDevice);
   563 
   564     if (joystick->hwdata != NULL) {
   565         /* free system specific hardware data */
   566         free (joystick->hwdata);
   567     }
   568 }
   569 
   570 /* Function to perform any system-specific joystick related cleanup */
   571 void
   572 SDL_SYS_JoystickQuit (void)
   573 {
   574     IDirectInput_Release (dinput);
   575     dinput = NULL;
   576     DX5_Unload ();
   577 }
   578 
   579 #endif /* SDL_JOYSTICK_DINPUT */
   580 /* vi: set ts=4 sw=4 expandtab: */