Hey, I've got a couple patches implementing build and setactionmap for
joysticks and I'd like you guys to give them a look.

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).

Cheers :)

2011/7/5 Lucas Zawacki <lfzawa...@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 <lfzawa...@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 <lfzawa...@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 <lfzawa...@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 <lfzawa...@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



Reply via email to