src/joystick/win32/SDL_dxjoystick.c
author Sam Lantinga <slouken@libsdl.org>
Mon, 10 Jul 2006 21:04:37 +0000
changeset 1895 c121d94672cb
child 2198 fe19afb86473
permissions -rw-r--r--
SDL 1.2 is moving to a branch, and SDL 1.3 is becoming the head.
     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, Sint16 value)
   375 {
   376     if (joystick->axes[axis] != value)
   377         return SDL_PrivateJoystickAxis(joystick, axis, value);
   378     return 0;
   379 }
   380 
   381 static int
   382 SDL_PrivateJoystickHat_Int(SDL_Joystick * joystick, Uint8 hat, Uint8 value)
   383 {
   384     if (joystick->hats[hat] != value)
   385         return SDL_PrivateJoystickHat(joystick, hat, value);
   386     return 0;
   387 }
   388 
   389 static int
   390 SDL_PrivateJoystickButton_Int(SDL_Joystick * joystick, Uint8 button,
   391                               Uint8 state)
   392 {
   393     if (joystick->buttons[button] != state)
   394         return SDL_PrivateJoystickButton(joystick, button, state);
   395     return 0;
   396 }
   397 
   398 /* Function to update the state of a joystick - called as a device poll.
   399  * This function shouldn't update the joystick structure directly,
   400  * but instead should call SDL_PrivateJoystick*() to deliver events
   401  * and update joystick device state.
   402  */
   403 void
   404 SDL_SYS_JoystickUpdate_Polled(SDL_Joystick * joystick)
   405 {
   406     DIJOYSTATE state;
   407     HRESULT result;
   408     int i;
   409 
   410     result =
   411         IDirectInputDevice2_GetDeviceState(joystick->hwdata->InputDevice,
   412                                            sizeof(state), &state);
   413     if (result == DIERR_INPUTLOST || result == DIERR_NOTACQUIRED) {
   414         IDirectInputDevice2_Acquire(joystick->hwdata->InputDevice);
   415         result =
   416             IDirectInputDevice2_GetDeviceState(joystick->hwdata->
   417                                                InputDevice, sizeof(state),
   418                                                &state);
   419     }
   420 
   421     /* Set each known axis, button and POV. */
   422     for (i = 0; i < joystick->hwdata->NumInputs; ++i) {
   423         const input_t *in = &joystick->hwdata->Inputs[i];
   424 
   425         switch (in->type) {
   426         case AXIS:
   427             switch (in->ofs) {
   428             case DIJOFS_X:
   429                 SDL_PrivateJoystickAxis_Int(joystick, in->num,
   430                                             (Sint16) state.lX);
   431                 break;
   432             case DIJOFS_Y:
   433                 SDL_PrivateJoystickAxis_Int(joystick, in->num,
   434                                             (Sint16) state.lY);
   435                 break;
   436             case DIJOFS_Z:
   437                 SDL_PrivateJoystickAxis_Int(joystick, in->num,
   438                                             (Sint16) state.lZ);
   439                 break;
   440             case DIJOFS_RX:
   441                 SDL_PrivateJoystickAxis_Int(joystick, in->num,
   442                                             (Sint16) state.lRx);
   443                 break;
   444             case DIJOFS_RY:
   445                 SDL_PrivateJoystickAxis_Int(joystick, in->num,
   446                                             (Sint16) state.lRy);
   447                 break;
   448             case DIJOFS_RZ:
   449                 SDL_PrivateJoystickAxis_Int(joystick, in->num,
   450                                             (Sint16) state.lRz);
   451                 break;
   452             case DIJOFS_SLIDER(0):
   453                 SDL_PrivateJoystickAxis_Int(joystick, in->num,
   454                                             (Sint16) state.rglSlider[0]);
   455                 break;
   456             case DIJOFS_SLIDER(1):
   457                 SDL_PrivateJoystickAxis_Int(joystick, in->num,
   458                                             (Sint16) state.rglSlider[0]);
   459                 break;
   460             }
   461 
   462             break;
   463 
   464         case BUTTON:
   465             SDL_PrivateJoystickButton_Int(joystick, in->num,
   466                                           (Uint8) (state.
   467                                                    rgbButtons[in->ofs -
   468                                                               DIJOFS_BUTTON0]
   469                                                    ? SDL_PRESSED :
   470                                                    SDL_RELEASED));
   471             break;
   472         case HAT:
   473             {
   474                 Uint8 pos =
   475                     TranslatePOV(state.rgdwPOV[in->ofs - DIJOFS_POV(0)]);
   476                 SDL_PrivateJoystickHat_Int(joystick, in->num, pos);
   477                 break;
   478             }
   479         }
   480     }
   481 }
   482 
   483 void
   484 SDL_SYS_JoystickUpdate_Buffered(SDL_Joystick * joystick)
   485 {
   486     int i;
   487     HRESULT result;
   488     DWORD numevents;
   489     DIDEVICEOBJECTDATA evtbuf[INPUT_QSIZE];
   490 
   491     numevents = INPUT_QSIZE;
   492     result =
   493         IDirectInputDevice2_GetDeviceData(joystick->hwdata->InputDevice,
   494                                           sizeof(DIDEVICEOBJECTDATA),
   495                                           evtbuf, &numevents, 0);
   496     if (result == DIERR_INPUTLOST || result == DIERR_NOTACQUIRED) {
   497         IDirectInputDevice2_Acquire(joystick->hwdata->InputDevice);
   498         result =
   499             IDirectInputDevice2_GetDeviceData(joystick->hwdata->
   500                                               InputDevice,
   501                                               sizeof(DIDEVICEOBJECTDATA),
   502                                               evtbuf, &numevents, 0);
   503     }
   504 
   505     /* Handle the events */
   506     if (result != DI_OK)
   507         return;
   508 
   509     for (i = 0; i < (int) numevents; ++i) {
   510         int j;
   511 
   512         for (j = 0; j < joystick->hwdata->NumInputs; ++j) {
   513             const input_t *in = &joystick->hwdata->Inputs[j];
   514 
   515             if (evtbuf[i].dwOfs != in->ofs)
   516                 continue;
   517 
   518             switch (in->type) {
   519             case AXIS:
   520                 SDL_PrivateJoystickAxis(joystick, in->num,
   521                                         (Sint16) evtbuf[i].dwData);
   522                 break;
   523             case BUTTON:
   524                 SDL_PrivateJoystickButton(joystick, in->num,
   525                                           (Uint8) (evtbuf[i].
   526                                                    dwData ? SDL_PRESSED
   527                                                    : SDL_RELEASED));
   528                 break;
   529             case HAT:
   530                 {
   531                     Uint8 pos = TranslatePOV(evtbuf[i].dwData);
   532                     SDL_PrivateJoystickHat(joystick, in->num, pos);
   533                 }
   534             }
   535         }
   536     }
   537 }
   538 
   539 void
   540 SDL_SYS_JoystickUpdate(SDL_Joystick * joystick)
   541 {
   542     HRESULT result;
   543 
   544     result = IDirectInputDevice2_Poll(joystick->hwdata->InputDevice);
   545     if (result == DIERR_INPUTLOST || result == DIERR_NOTACQUIRED) {
   546         IDirectInputDevice2_Acquire(joystick->hwdata->InputDevice);
   547         IDirectInputDevice2_Poll(joystick->hwdata->InputDevice);
   548     }
   549 
   550     if (joystick->hwdata->buffered)
   551         SDL_SYS_JoystickUpdate_Buffered(joystick);
   552     else
   553         SDL_SYS_JoystickUpdate_Polled(joystick);
   554 }
   555 
   556 /* Function to close a joystick after use */
   557 void
   558 SDL_SYS_JoystickClose(SDL_Joystick * joystick)
   559 {
   560     IDirectInputDevice2_Unacquire(joystick->hwdata->InputDevice);
   561     IDirectInputDevice2_Release(joystick->hwdata->InputDevice);
   562 
   563     if (joystick->hwdata != NULL) {
   564         /* free system specific hardware data */
   565         free(joystick->hwdata);
   566     }
   567 }
   568 
   569 /* Function to perform any system-specific joystick related cleanup */
   570 void
   571 SDL_SYS_JoystickQuit(void)
   572 {
   573     IDirectInput_Release(dinput);
   574     dinput = NULL;
   575     DX5_Unload();
   576 }
   577 
   578 #endif /* SDL_JOYSTICK_DINPUT */
   579 /* vi: set ts=4 sw=4 expandtab: */