GSoC: dinput8 Action Mapping

Marcus Meissner marcus at jet.franken.de
Thu Jul 21 03:09:45 CDT 2011


On Tue, Jul 19, 2011 at 09:40:37PM -0300, Lucas Zawacki wrote:
> Hey, I've got a couple patches implementing build and setactionmap for
> joysticks and I'd like you guys to give them a look.

The patches look good to my eyes.


This part:
> +        if ((lpdiaf->rgoAction[i].dwSemantic & genre) == genre)

still looks strange (as the first genre should be a some kind
of bitmask instead of genre?), but will likely work as-is for now.

> These implementations are very generic and I think I could use them
> for the keyboard and the mouse too. If everything's alright with these
> patches that'll be my next step.
> 
> The mapping can be tested this small app I built:
> https://github.com/downloads/lfzawacki/dinput-samples/dolphin.exe
> provided that you have one joystick attached (it will chose the first
> one if there are more).

Works with my Joystick here at least.

Not sure what it should output, A and X button works, the 
ranges go to 10/-10 expect for "Down" just going to -5 (might be a issue with the joystick).

Ciao, Marcus

> 2011/7/5 Lucas Zawacki <lfzawacki at gmail.com>:
> > Hi guys.
> >
> > Now that all the initial patches are in I have several smaller tasks
> > to work on as listed here
> > http://lfzawacki.heroku.com/wine/published/Road+Map and hopefully
> > they're more straightforward. I've already started working on getting
> > EnumDevicesBySemantics  correct with joysticks and the passed flags
> > and, after that, BuildActionMap for the joysticks will follow. Maybe
> > now it's time to discuss how to implement EnumDevicesBySemantics as a
> > crosscall, but I really don't know how to do it (or if it's worth it)
> > and everywhere I look in dinput I see similar cases of this
> > "duplication pattern".
> >
> > Another thing that I've been thinking is that I might as well end up
> > rolling a version of ConfigureDevices
> > (http://msdn.microsoft.com/en-us/library/microsoft.directx_sdk.idirectinput8.idirectinput8.configuredevices%28v=VS.85%29.aspx)
> > because so far I've seen two of the games affected by bug 8754 use it
> > to configure controls. I've not had time to find and test all of them,
> > but if someone on the list knows about other games that use it I'd
> > like to be informed.
> >
> > Last but not least, thanks for Wylda for testing NFSU and keeping bug
> > 8754 informed. I actually can't run the game properly, most likely
> > because of the crappy intel graphics card in my laptop.
> >
> > Anyway, feedback on the tasks is welcome.
> >
> > ---
> > lfz
> >

> From b492c54d5ca7179e0905f63cec183d0920555e1d Mon Sep 17 00:00:00 2001
> From: Lucas Fialho Zawacki <lfzawacki at gmail.com>
> Date: Tue, 19 Jul 2011 17:52:12 -0300
> Subject: [PATCH 1/4] dinput: EnumDevicesBySemantics enumerating joysticks with priority flags
> 
> Added an utility function that checks the priority of a device for a given mapping. This can be modified later to return priority 2 mappings, if necessary.
> ---
>  dlls/dinput/dinput_main.c |   50 ++++++++++++++++++++++++++++++++------------
>  1 files changed, 36 insertions(+), 14 deletions(-)
> 
> diff --git a/dlls/dinput/dinput_main.c b/dlls/dinput/dinput_main.c
> index b653307..1d59cdc 100644
> --- a/dlls/dinput/dinput_main.c
> +++ b/dlls/dinput/dinput_main.c
> @@ -317,6 +317,38 @@ void _copy_diactionformatWtoA(LPDIACTIONFORMATA to, LPDIACTIONFORMATW from)
>      }
>  }
>  
> +/* _diactionformat_priorityA
> + *
> + *  Given a DIACTIONFORMAT structure and a DI genre, returns the enumeration
> + *  priority. Joysticks should pass the game genre, and mouse or keyboard their
> + *  respective DI*_MASK
> + */
> +static DWORD _diactionformat_priorityA(LPDIACTIONFORMATA lpdiaf, DWORD genre)
> +{
> +    int i;
> +    DWORD priorityFlags = 0;
> +
> +    /* If there's at least one action for the device it's priority 1 */
> +    for(i=0; i < lpdiaf->dwActionSize; i++)
> +        if ((lpdiaf->rgoAction[i].dwSemantic & genre) == genre)
> +            priorityFlags |= DIEDBS_MAPPEDPRI1;
> +
> +    return priorityFlags;
> +}
> +
> +static DWORD _diactionformat_priorityW(LPDIACTIONFORMATW lpdiaf, DWORD genre)
> +{
> +    int i;
> +    DWORD priorityFlags = 0;
> +
> +    /* If there's at least one action for the device it's priority 1 */
> +    for(i=0; i < lpdiaf->dwActionSize; i++)
> +        if ((lpdiaf->rgoAction[i].dwSemantic & genre) == genre)
> +            priorityFlags |= DIEDBS_MAPPEDPRI1;
> +
> +    return priorityFlags;
> +}
> +
>  /******************************************************************************
>   *	IDirectInputA_EnumDevices
>   */
> @@ -877,7 +909,7 @@ static HRESULT WINAPI IDirectInput8AImpl_EnumDevicesBySemantics(
>          {
>              TRACE(" - checking device %u ('%s')\n", i, dinput_devices[i]->name);
>  
> -            callbackFlags = 0;
> +            callbackFlags = _diactionformat_priorityA(lpdiActionFormat, lpdiActionFormat->dwGenre);
>              /* Default behavior is to enumerate attached game controllers */
>              enumSuccess = dinput_devices[i]->enum_deviceA(DI8DEVCLASS_GAMECTRL, DIEDFL_ATTACHEDONLY | dwFlags, &didevi, This->dwVersion, j);
>              if (enumSuccess)
> @@ -895,16 +927,11 @@ static HRESULT WINAPI IDirectInput8AImpl_EnumDevicesBySemantics(
>      /* Enumerate keyboard and mouse */
>      for(i=0; i < sizeof(guids)/sizeof(guids[0]); i++)
>      {
> -        callbackFlags = 0;
> +        callbackFlags = _diactionformat_priorityA(lpdiActionFormat, actionMasks[i]);
>  
>          IDirectInput_CreateDevice(iface, guids[i], &lpdid, NULL);
>          IDirectInputDevice_GetDeviceInfo(lpdid, &didevi);
>  
> -        /* If there's at least one action for the device it's priority 1 */
> -        for(j=0; j < lpdiActionFormat->dwActionSize; j++)
> -            if ((lpdiActionFormat->rgoAction[j].dwSemantic & actionMasks[i]) == actionMasks[i])
> -                callbackFlags |= DIEDBS_MAPPEDPRI1;
> -
>          if (lpCallback(&didevi, lpdid, callbackFlags, sizeof(guids)/sizeof(guids[0]) - (i+1), pvRef) == DIENUM_STOP)
>              return DI_OK;
>      }
> @@ -942,7 +969,7 @@ static HRESULT WINAPI IDirectInput8WImpl_EnumDevicesBySemantics(
>          {
>              TRACE(" - checking device %u ('%s')\n", i, dinput_devices[i]->name);
>  
> -            callbackFlags = 0;
> +            callbackFlags = _diactionformat_priorityW(lpdiActionFormat, lpdiActionFormat->dwGenre);
>              /* Default behavior is to enumerate attached game controllers */
>              enumSuccess = dinput_devices[i]->enum_deviceW(DI8DEVCLASS_GAMECTRL, DIEDFL_ATTACHEDONLY | dwFlags, &didevi, This->dwVersion, j);
>              if (enumSuccess)
> @@ -960,16 +987,11 @@ static HRESULT WINAPI IDirectInput8WImpl_EnumDevicesBySemantics(
>      /* Enumerate keyboard and mouse */
>      for(i=0; i < sizeof(guids)/sizeof(guids[0]); i++)
>      {
> -        callbackFlags = 0;
> +        callbackFlags = _diactionformat_priorityW(lpdiActionFormat, actionMasks[i]);
>  
>          IDirectInput_CreateDevice(iface, guids[i], &lpdid, NULL);
>          IDirectInputDevice_GetDeviceInfo(lpdid, &didevi);
>  
> -        /* If there's at least one action for the device it's priority 1 */
> -        for(j=0; j < lpdiActionFormat->dwActionSize; j++)
> -            if ((lpdiActionFormat->rgoAction[j].dwSemantic & actionMasks[i]) == actionMasks[i])
> -                callbackFlags |= DIEDBS_MAPPEDPRI1;
> -
>          if (lpCallback(&didevi, lpdid, callbackFlags, sizeof(guids)/sizeof(guids[0]) - (i+1), pvRef) == DIENUM_STOP)
>              return DI_OK;
>      }
> -- 
> 1.7.0.4
> 

> From 2c97c4d622a4731721705b81416191b47b738d71 Mon Sep 17 00:00:00 2001
> From: Lucas Fialho Zawacki <lfzawacki at gmail.com>
> Date: Tue, 19 Jul 2011 20:25:48 -0300
> Subject: [PATCH 2/4] dinput: BuildActionMap for all joysticks. For the moment only for buttons and axis.
> 
> Added an utility function that finds the Nth dataformat object of a given type.
> ---
>  dlls/dinput/device.c         |   23 +++++++++++++++++++++++
>  dlls/dinput/device_private.h |    1 +
>  dlls/dinput/joystick.c       |   40 +++++++++++++++++++++++++++++++++++++++-
>  3 files changed, 63 insertions(+), 1 deletions(-)
> 
> diff --git a/dlls/dinput/device.c b/dlls/dinput/device.c
> index 22632ea..2bd5d6c 100644
> --- a/dlls/dinput/device.c
> +++ b/dlls/dinput/device.c
> @@ -366,6 +366,29 @@ static inline LPDIOBJECTDATAFORMAT dataformat_to_odf(LPCDIDATAFORMAT df, int idx
>      return (LPDIOBJECTDATAFORMAT)((LPBYTE)df->rgodf + idx * df->dwObjSize);
>  }
>  
> +/* dataformat_to_odf_by_type
> + *  Find the Nth object of the selected type in the DataFormat
> + */
> +LPDIOBJECTDATAFORMAT dataformat_to_odf_by_type(LPCDIDATAFORMAT df, int n, DWORD type)
> +{
> +    int i, nfound = 0;
> +
> +    for (i=0; i < df->dwNumObjs; i++)
> +    {
> +        LPDIOBJECTDATAFORMAT odf = dataformat_to_odf(df, i);
> +
> +        if (odf->dwType & type)
> +        {
> +            if (n == nfound)
> +                return odf;
> +
> +            nfound++;
> +        }
> +    }
> +
> +    return NULL;
> +}
> +
>  static HRESULT create_DataFormat(LPCDIDATAFORMAT asked_format, DataFormat *format)
>  {
>      DataTransform *dt;
> diff --git a/dlls/dinput/device_private.h b/dlls/dinput/device_private.h
> index 503698a..6c061b2 100644
> --- a/dlls/dinput/device_private.h
> +++ b/dlls/dinput/device_private.h
> @@ -125,6 +125,7 @@ extern void _dump_DIDATAFORMAT(const DIDATAFORMAT *df)  DECLSPEC_HIDDEN;
>  extern const char *_dump_dinput_GUID(const GUID *guid)  DECLSPEC_HIDDEN;
>  
>  extern DWORD semantic_to_obj_id(IDirectInputDeviceImpl* This, DWORD dwSemantic)  DECLSPEC_HIDDEN;
> +extern LPDIOBJECTDATAFORMAT dataformat_to_odf_by_type(LPCDIDATAFORMAT df, int n, DWORD type)   DECLSPEC_HIDDEN;
>  
>  /* And the stubs */
>  extern HRESULT WINAPI IDirectInputDevice2AImpl_Acquire(LPDIRECTINPUTDEVICE8A iface) DECLSPEC_HIDDEN;
> diff --git a/dlls/dinput/joystick.c b/dlls/dinput/joystick.c
> index 68dcc6a..d821748 100644
> --- a/dlls/dinput/joystick.c
> +++ b/dlls/dinput/joystick.c
> @@ -432,9 +432,47 @@ HRESULT WINAPI JoystickWGenericImpl_BuildActionMap(LPDIRECTINPUTDEVICE8W iface,
>                                                     LPCWSTR lpszUserName,
>                                                     DWORD dwFlags)
>  {
> +    JoystickGenericImpl *This = impl_from_IDirectInputDevice8W(iface);
> +    int i, j, has_actions = 0;
> +    DWORD object_types[] = { DIDFT_BUTTON, DIDFT_AXIS };
> +    DWORD type_map[] = { DIDFT_PSHBUTTON, DIDFT_RELAXIS };
> +
>      FIXME("(%p)->(%p,%s,%08x): semi-stub !\n", iface, lpdiaf, debugstr_w(lpszUserName), dwFlags);
>  
> -    return DI_NOEFFECT;
> +    for (i=0; i < lpdiaf->dwNumActions; i++)
> +    {
> +        DWORD inst = (0x000000ff & (lpdiaf->rgoAction[i].dwSemantic)) - 1;
> +        DWORD type = 0x000000ff & (lpdiaf->rgoAction[i].dwSemantic >> 8);
> +        DWORD genre = 0xff000000 & lpdiaf->rgoAction[i].dwSemantic;
> +
> +        /* Only consider actions of the right genre */
> +        if (lpdiaf->dwGenre != genre) continue;
> +
> +        for (j=0; j < sizeof(object_types)/sizeof(object_types[0]); j++)
> +        {
> +            if (type & object_types[j])
> +            {
> +                /* Assure that the object exists */
> +                LPDIOBJECTDATAFORMAT odf = dataformat_to_odf_by_type(This->base.data_format.wine_df, inst, DIDFT_BUTTON);
> +
> +                if (odf != NULL)
> +                {
> +                    lpdiaf->rgoAction[i].dwObjID = type_map[j] | (0x0000ff00 & (inst << 8));
> +                    lpdiaf->rgoAction[i].guidInstance = This->base.guid;
> +                    lpdiaf->rgoAction[i].dwHow = DIAH_DEFAULT;
> +
> +                    has_actions = 1;
> +
> +                    /* No need to try other types if the action was already mapped */
> +                    break;
> +                }
> +            }
> +        }
> +    }
> +
> +    if (!has_actions) return DI_NOEFFECT;
> +
> +    return IDirectInputDevice8WImpl_BuildActionMap(iface, lpdiaf, lpszUserName, dwFlags);
>  }
>  
>  HRESULT WINAPI JoystickAGenericImpl_BuildActionMap(LPDIRECTINPUTDEVICE8A iface,
> -- 
> 1.7.0.4
> 

> From a71ddba6092dd7a843c5359b26c4e9254311edfb Mon Sep 17 00:00:00 2001
> From: Lucas Fialho Zawacki <lfzawacki at gmail.com>
> Date: Tue, 19 Jul 2011 20:44:36 -0300
> Subject: [PATCH 3/4] dinput: SetActionMap for joysticks.
> 
> ---
>  dlls/dinput/joystick.c |   60 +++++++++++++++++++++++++++++++++++++++++++++++-
>  1 files changed, 59 insertions(+), 1 deletions(-)
> 
> diff --git a/dlls/dinput/joystick.c b/dlls/dinput/joystick.c
> index d821748..c63def9 100644
> --- a/dlls/dinput/joystick.c
> +++ b/dlls/dinput/joystick.c
> @@ -500,9 +500,67 @@ HRESULT WINAPI JoystickWGenericImpl_SetActionMap(LPDIRECTINPUTDEVICE8W iface,
>                                                   LPCWSTR lpszUserName,
>                                                   DWORD dwFlags)
>  {
> +    JoystickGenericImpl *This = impl_from_IDirectInputDevice8W(iface);
> +    DIDATAFORMAT data_format;
> +    DIOBJECTDATAFORMAT *obj_df = NULL;
> +    int i, action = 0, num_actions = 0;
> +    unsigned int offset = 0;
> +
>      FIXME("(%p)->(%p,%s,%08x): semi-stub !\n", iface, lpdiaf, debugstr_w(lpszUserName), dwFlags);
>  
> -    return DI_NOEFFECT;
> +    if (This->base.acquired) return DIERR_ACQUIRED;
> +
> +    data_format.dwSize = sizeof(data_format);
> +    data_format.dwObjSize = sizeof(DIOBJECTDATAFORMAT);
> +    data_format.dwFlags = DIDF_RELAXIS;
> +    data_format.dwDataSize = lpdiaf->dwDataSize;
> +
> +    /* count the actions */
> +    for (i=0; i < lpdiaf->dwNumActions; i++)
> +        if (IsEqualGUID(&This->base.guid, &lpdiaf->rgoAction[i].guidInstance))
> +            num_actions++;
> +
> +    if (num_actions == 0) return DI_NOEFFECT;
> +
> +    This->base.num_actions = num_actions;
> +
> +    /* Construct the dataformat and actionmap */
> +    obj_df = HeapAlloc(GetProcessHeap(), 0, sizeof(DIOBJECTDATAFORMAT)*num_actions);
> +    data_format.rgodf = (LPDIOBJECTDATAFORMAT)obj_df;
> +    data_format.dwNumObjs = num_actions;
> +
> +    This->base.action_map = HeapAlloc(GetProcessHeap(), 0, sizeof(ActionMap)*num_actions);
> +
> +    for (i = 0; i < lpdiaf->dwNumActions; i++)
> +    {
> +        if (IsEqualGUID(&This->base.guid, &lpdiaf->rgoAction[i].guidInstance))
> +        {
> +            LPDIDATAFORMAT df = This->base.data_format.wine_df;
> +            DWORD inst = DIDFT_GETINSTANCE(lpdiaf->rgoAction[i].dwObjID);
> +            DWORD type = DIDFT_GETTYPE(lpdiaf->rgoAction[i].dwObjID);
> +            LPDIOBJECTDATAFORMAT obj;
> +
> +            if (type == DIDFT_PSHBUTTON) type = DIDFT_BUTTON;
> +            if (type == DIDFT_RELAXIS) type = DIDFT_AXIS;
> +
> +            obj = dataformat_to_odf_by_type(df, inst, type);
> +
> +            memcpy(&obj_df[action], obj, df->dwObjSize);
> +
> +            This->base.action_map[action].uAppData = lpdiaf->rgoAction[i].uAppData;
> +            This->base.action_map[action].offset = offset;
> +            obj_df[action].dwOfs = offset;
> +            offset += (type & DIDFT_BUTTON) ? 1 : 4;
> +
> +            action++;
> +        }
> +    }
> +
> +    IDirectInputDevice8_SetDataFormat(iface, &data_format);
> +
> +    HeapFree(GetProcessHeap(), 0, obj_df);
> +
> +    return IDirectInputDevice8WImpl_SetActionMap(iface, lpdiaf, lpszUserName, dwFlags);
>  }
>  
>  HRESULT WINAPI JoystickAGenericImpl_SetActionMap(LPDIRECTINPUTDEVICE8A iface,
> -- 
> 1.7.0.4
> 

> From 4f18cfb471707c461135ecfde5f975324b37e696 Mon Sep 17 00:00:00 2001
> From: Lucas Fialho Zawacki <lfzawacki at gmail.com>
> Date: Tue, 19 Jul 2011 21:03:16 -0300
> Subject: [PATCH 4/4] dinput: SetActionMap setting the axis range according to the action format
> 
> ---
>  dlls/dinput/device.c        |    8 ++++++++
>  dlls/dinput8/tests/device.c |   15 +++++++++++++++
>  2 files changed, 23 insertions(+), 0 deletions(-)
> 
> diff --git a/dlls/dinput/device.c b/dlls/dinput/device.c
> index 2bd5d6c..58d4e14 100644
> --- a/dlls/dinput/device.c
> +++ b/dlls/dinput/device.c
> @@ -1483,9 +1483,17 @@ HRESULT WINAPI IDirectInputDevice8WImpl_SetActionMap(LPDIRECTINPUTDEVICE8W iface
>  						     DWORD dwFlags)
>  {
>      DIPROPDWORD dp;
> +    DIPROPRANGE dpr;
>  
>      FIXME("(%p)->(%p,%s,%08x): semi-stub !\n", iface, lpdiaf, debugstr_w(lpszUserName), dwFlags);
>  
> +    dpr.diph.dwSize = sizeof(DIPROPRANGE);
> +    dpr.lMin = lpdiaf->lAxisMin;
> +    dpr.lMax = lpdiaf->lAxisMax;
> +    dpr.diph.dwHeaderSize = sizeof(DIPROPHEADER);
> +    dpr.diph.dwHow = DIPH_DEVICE;
> +    IDirectInputDevice8_SetProperty(iface, DIPROP_RANGE, &dpr.diph);
> +
>      if (lpdiaf->dwBufferSize > 0)
>      {
>          dp.diph.dwSize = sizeof(DIPROPDWORD);
> diff --git a/dlls/dinput8/tests/device.c b/dlls/dinput8/tests/device.c
> index cedad80..31e2e3f 100644
> --- a/dlls/dinput8/tests/device.c
> +++ b/dlls/dinput8/tests/device.c
> @@ -132,6 +132,7 @@ static BOOL CALLBACK enumeration_callback(
>  {
>      HRESULT hr;
>      DIPROPDWORD dp;
> +    DIPROPRANGE dpr;
>      struct enum_data *data = pvRef;
>      if (!data) return DIENUM_CONTINUE;
>  
> @@ -179,6 +180,20 @@ static BOOL CALLBACK enumeration_callback(
>      ok (SUCCEEDED(hr), "GetProperty failed hr=%08x\n", hr);
>      ok (dp.dwData == data->lpdiaf->dwBufferSize, "SetActionMap must set the buffer, buffersize=%d\n", dp.dwData);
>  
> +    /* Test axis range */
> +    memset(&dpr, 0, sizeof(dpr));
> +    dpr.diph.dwSize = sizeof(dpr);
> +    dpr.diph.dwHeaderSize = sizeof(DIPROPHEADER);
> +    dpr.diph.dwHow  = DIPH_DEVICE;
> +
> +    hr = IDirectInputDevice_GetProperty(lpdid, DIPROP_RANGE, &dpr.diph);
> +    /* Only test if device supports the range property */
> +    if (SUCCEEDED(hr))
> +    {
> +        ok (dpr.lMin == data->lpdiaf->lAxisMin, "SetActionMap must set the min axis range expected=%d got=%d\n", data->lpdiaf->lAxisMin, dpr.lMin);
> +        ok (dpr.lMax == data->lpdiaf->lAxisMax, "SetActionMap must set the max axis range expected=%d got=%d\n", data->lpdiaf->lAxisMax, dpr.lMax);
> +    }
> +
>      /* SetActionMap has set the data format so now it should work */
>      hr = IDirectInputDevice8_Acquire(lpdid);
>      ok (SUCCEEDED(hr), "Acquire failed hr=%08x\n", hr);
> -- 
> 1.7.0.4
> 

> 




More information about the wine-devel mailing list