From 0234b43b03744006ff225d3b81a55ef25c55940c Mon Sep 17 00:00:00 2001
From: Lucas Fialho Zawacki <lfzawacki@gmail.com>
Date: Fri, 10 Jun 2011 21:43:53 -0300
Subject: dinput: Keyboard and mouse implementation of BuildActionMap.

---
 dlls/dinput/device.c        |    4 +-
 dlls/dinput/keyboard.c      |   71 +++++++++++++++++++++++++++++++++++++++++-
 dlls/dinput/mouse.c         |   62 ++++++++++++++++++++++++++++++++++++-
 dlls/dinput8/tests/device.c |   52 +++++++++++++++++++++++++++++++
 4 files changed, 183 insertions(+), 6 deletions(-)

diff --git a/dlls/dinput/device.c b/dlls/dinput/device.c
index b457b27..accfe84 100644
--- a/dlls/dinput/device.c
+++ b/dlls/dinput/device.c
@@ -1359,7 +1359,7 @@ HRESULT WINAPI IDirectInputDevice8AImpl_BuildActionMap(LPDIRECTINPUTDEVICE8A ifa
 						       LPCSTR lpszUserName,
 						       DWORD dwFlags)
 {
-    FIXME("(%p)->(%p,%s,%08x): stub !\n", iface, lpdiaf, lpszUserName, dwFlags);
+    FIXME("(%p)->(%p,%s,%08x): semi-stub !\n", iface, lpdiaf, lpszUserName, dwFlags);
 #define X(x) if (dwFlags & x) FIXME("\tdwFlags =|"#x"\n");
 	X(DIDBAM_DEFAULT)
 	X(DIDBAM_PRESERVE)
@@ -1375,7 +1375,7 @@ HRESULT WINAPI IDirectInputDevice8WImpl_BuildActionMap(LPDIRECTINPUTDEVICE8W ifa
 						       LPCWSTR lpszUserName,
 						       DWORD dwFlags)
 {
-    FIXME("(%p)->(%p,%s,%08x): stub !\n", iface, lpdiaf, debugstr_w(lpszUserName), dwFlags);
+    FIXME("(%p)->(%p,%s,%08x): semi-stub !\n", iface, lpdiaf, debugstr_w(lpszUserName), dwFlags);
 #define X(x) if (dwFlags & x) FIXME("\tdwFlags =|"#x"\n");
 	X(DIDBAM_DEFAULT)
 	X(DIDBAM_PRESERVE)
diff --git a/dlls/dinput/keyboard.c b/dlls/dinput/keyboard.c
index c697cfd..76b921e 100644
--- a/dlls/dinput/keyboard.c
+++ b/dlls/dinput/keyboard.c
@@ -91,6 +91,13 @@ static BYTE map_dik_code(DWORD scanCode, DWORD vkCode)
     return out_code;
 }
 
+/* This is used for the action mapping of constants such as DIKEYBOARD_SPACE */
+static DWORD keyboard_semantic_to_obj_id(DWORD dwSemantic)
+{
+    /* dwSemantic=810004df is dwObjID=0xdf04 */
+    return 0x0000ffff & ( (dwSemantic << 8) | (dwSemantic >> 8) );
+}
+
 static int KeyboardCallback( LPDIRECTINPUTDEVICE8A iface, WPARAM wparam, LPARAM lparam )
 {
     SysKeyboardImpl *This = impl_from_IDirectInputDevice8A(iface);
@@ -524,6 +531,66 @@ static HRESULT WINAPI SysKeyboardAImpl_GetProperty(LPDIRECTINPUTDEVICE8A iface,
     return SysKeyboardWImpl_GetProperty(IDirectInputDevice8W_from_impl(This), rguid, pdiph);
 }
 
+HRESULT WINAPI SysKeyboardAImpl_BuildActionMap(LPDIRECTINPUTDEVICE8A iface,
+                                               LPDIACTIONFORMATA lpdiaf,
+                                               LPCSTR lpszUserName,
+                                               DWORD dwFlags)
+{
+    SysKeyboardImpl *This = impl_from_IDirectInputDevice8A(iface);
+    int i, has_actions = 0;
+
+    for (i=0; i < lpdiaf->dwNumActions; i++)
+    {
+        if ( (lpdiaf->rgoAction[i].dwSemantic & 0xff000000) == DIKEYBOARD_MASK )
+        {
+            lpdiaf->rgoAction[i].dwObjID = keyboard_semantic_to_obj_id(lpdiaf->rgoAction[i].dwSemantic);
+            lpdiaf->rgoAction[i].guidInstance = This->base.guid;
+            lpdiaf->rgoAction[i].dwHow = DIAH_DEFAULT;
+            has_actions = 1;
+        }
+        else if ( !(dwFlags & DIDBAM_PRESERVE) )
+        {
+            /* we must clear action data belonging to other devices */
+            memset(&lpdiaf->rgoAction[i].guidInstance, 0 , sizeof(GUID));
+            lpdiaf->rgoAction[i].dwHow = DIAH_UNMAPPED;
+        }
+    }
+
+    if (!has_actions) return DI_NOEFFECT;
+
+    return IDirectInputDevice8AImpl_BuildActionMap(iface, lpdiaf, lpszUserName, dwFlags);
+}
+
+HRESULT WINAPI SysKeyboardWImpl_BuildActionMap(LPDIRECTINPUTDEVICE8W iface,
+                                               LPDIACTIONFORMATW lpdiaf,
+                                               LPCWSTR lpszUserName,
+                                               DWORD dwFlags)
+{
+    SysKeyboardImpl *This = impl_from_IDirectInputDevice8W(iface);
+    int i, has_actions = 0;
+
+    for (i=0; i < lpdiaf->dwNumActions; i++)
+    {
+        if ( (lpdiaf->rgoAction[i].dwSemantic & 0xff000000) == DIKEYBOARD_MASK )
+        {
+            lpdiaf->rgoAction[i].dwObjID = keyboard_semantic_to_obj_id(lpdiaf->rgoAction[i].dwSemantic);
+            lpdiaf->rgoAction[i].guidInstance = This->base.guid;
+            lpdiaf->rgoAction[i].dwHow = DIAH_DEFAULT;
+            has_actions = 1;
+        }
+        else if ( !(dwFlags & DIDBAM_PRESERVE) )
+        {
+            /* we must clear action data belonging to other devices */
+            memset(&lpdiaf->rgoAction[i].guidInstance, 0 , sizeof(GUID));
+            lpdiaf->rgoAction[i].dwHow = DIAH_UNMAPPED;
+        }
+    }
+
+    if (!has_actions) return DI_NOEFFECT;
+
+    return  IDirectInputDevice8WImpl_BuildActionMap(iface, lpdiaf, lpszUserName, dwFlags);
+}
+
 static const IDirectInputDevice8AVtbl SysKeyboardAvt =
 {
     IDirectInputDevice2AImpl_QueryInterface,
@@ -555,7 +622,7 @@ static const IDirectInputDevice8AVtbl SysKeyboardAvt =
     IDirectInputDevice2AImpl_SendDeviceData,
     IDirectInputDevice7AImpl_EnumEffectsInFile,
     IDirectInputDevice7AImpl_WriteEffectToFile,
-    IDirectInputDevice8AImpl_BuildActionMap,
+    SysKeyboardAImpl_BuildActionMap,
     IDirectInputDevice8AImpl_SetActionMap,
     IDirectInputDevice8AImpl_GetImageInfo
 };
@@ -591,7 +658,7 @@ static const IDirectInputDevice8WVtbl SysKeyboardWvt =
     IDirectInputDevice2WImpl_SendDeviceData,
     IDirectInputDevice7WImpl_EnumEffectsInFile,
     IDirectInputDevice7WImpl_WriteEffectToFile,
-    IDirectInputDevice8WImpl_BuildActionMap,
+    SysKeyboardWImpl_BuildActionMap,
     IDirectInputDevice8WImpl_SetActionMap,
     IDirectInputDevice8WImpl_GetImageInfo
 };
diff --git a/dlls/dinput/mouse.c b/dlls/dinput/mouse.c
index 02813ff..adc5869 100644
--- a/dlls/dinput/mouse.c
+++ b/dlls/dinput/mouse.c
@@ -773,7 +773,65 @@ static HRESULT WINAPI SysMouseWImpl_GetDeviceInfo(LPDIRECTINPUTDEVICE8W iface, L
     return DI_OK;
 }
 
+HRESULT WINAPI SysMouseAImpl_BuildActionMap(LPDIRECTINPUTDEVICE8A iface,
+                                            LPDIACTIONFORMATA lpdiaf,
+                                            LPCSTR lpszUserName,
+                                            DWORD dwFlags)
+{
+    SysMouseImpl *This = impl_from_IDirectInputDevice8A(iface);
+    int i, has_actions = 0;
+
+    for (i=0; i < lpdiaf->dwNumActions; i++)
+    {
+        if ( (lpdiaf->rgoAction[i].dwSemantic & 0xff000000) == DIMOUSE_MASK )
+        {
+            /* dwObjID mapping is not ready for now */
+            lpdiaf->rgoAction[i].guidInstance = This->base.guid;
+            lpdiaf->rgoAction[i].dwHow = DIAH_DEFAULT;
+            has_actions = 1;
+        }
+        else if ( !(dwFlags & DIDBAM_PRESERVE) )
+        {
+            /* we must clear action data belonging to other devices */
+            memset(&lpdiaf->rgoAction[i].guidInstance, 0 , sizeof(GUID));
+            lpdiaf->rgoAction[i].dwHow = DIAH_UNMAPPED;
+        }
+    }
+
+    if (!has_actions) return DI_NOEFFECT;
+
+    return IDirectInputDevice8AImpl_BuildActionMap(iface, lpdiaf, lpszUserName, dwFlags);
+}
 
+HRESULT WINAPI SysMouseWImpl_BuildActionMap(LPDIRECTINPUTDEVICE8W iface,
+                                            LPDIACTIONFORMATW lpdiaf,
+                                            LPCWSTR lpszUserName,
+                                            DWORD dwFlags)
+{
+    SysMouseImpl *This = impl_from_IDirectInputDevice8W(iface);
+    int i, has_actions = 0;
+
+    for (i=0; i < lpdiaf->dwNumActions; i++)
+    {
+        if ( (lpdiaf->rgoAction[i].dwSemantic & 0xff000000) == DIMOUSE_MASK )
+        {
+            /* dwObjID mapping is not ready for now */
+            lpdiaf->rgoAction[i].guidInstance = This->base.guid;
+            lpdiaf->rgoAction[i].dwHow = DIAH_DEFAULT;
+            has_actions = 1;
+        }
+        else if ( !(dwFlags & DIDBAM_PRESERVE) )
+        {
+            /* we must clear action data belonging to other devices */
+            memset(&lpdiaf->rgoAction[i].guidInstance, 0 , sizeof(GUID));
+            lpdiaf->rgoAction[i].dwHow = DIAH_UNMAPPED;
+        }
+    }
+
+    if (!has_actions) return DI_NOEFFECT;
+
+    return IDirectInputDevice8WImpl_BuildActionMap(iface, lpdiaf, lpszUserName, dwFlags);
+}
 static const IDirectInputDevice8AVtbl SysMouseAvt =
 {
     IDirectInputDevice2AImpl_QueryInterface,
@@ -805,7 +863,7 @@ static const IDirectInputDevice8AVtbl SysMouseAvt =
     IDirectInputDevice2AImpl_SendDeviceData,
     IDirectInputDevice7AImpl_EnumEffectsInFile,
     IDirectInputDevice7AImpl_WriteEffectToFile,
-    IDirectInputDevice8AImpl_BuildActionMap,
+    SysMouseAImpl_BuildActionMap,
     IDirectInputDevice8AImpl_SetActionMap,
     IDirectInputDevice8AImpl_GetImageInfo
 };
@@ -841,7 +899,7 @@ static const IDirectInputDevice8WVtbl SysMouseWvt =
     IDirectInputDevice2WImpl_SendDeviceData,
     IDirectInputDevice7WImpl_EnumEffectsInFile,
     IDirectInputDevice7WImpl_WriteEffectToFile,
-    IDirectInputDevice8WImpl_BuildActionMap,
+    SysMouseWImpl_BuildActionMap,
     IDirectInputDevice8WImpl_SetActionMap,
     IDirectInputDevice8WImpl_GetImageInfo
 };
diff --git a/dlls/dinput8/tests/device.c b/dlls/dinput8/tests/device.c
index 5074831..e1b308f 100644
--- a/dlls/dinput8/tests/device.c
+++ b/dlls/dinput8/tests/device.c
@@ -27,6 +27,13 @@
 #include "initguid.h"
 #include "dinput.h"
 
+enum {
+    DITEST_AXIS,
+    DITEST_BUTTON,
+    DITEST_KEYBOARDSPACE,
+    DITEST_MOUSEBUTTON0,
+};
+
 struct enum_data {
     LPDIRECTINPUT8 pDI;
     LPDIACTIONFORMAT lpdiaf;
@@ -50,6 +57,37 @@ DIACTION actionMapping[]=
   { 3, DIMOUSE_BUTTON0 , 0, { "Select" } }
 };
 
+static void test_build_action_map(
+    LPDIRECTINPUTDEVICE8 lpdid,
+    LPDIACTIONFORMAT lpdiaf,
+    int action_index,
+    DWORD obj_expected
+)
+{
+    HRESULT hr;
+    DIACTION *actions;
+    DWORD obj_instance, how;
+    GUID assigned_to;
+    DIDEVICEINSTANCEA ddi;
+
+    ddi.dwSize = sizeof(ddi);
+    IDirectInputDevice_GetDeviceInfo(lpdid,&ddi);
+
+    hr = IDirectInputDevice8_BuildActionMap(lpdid,lpdiaf,NULL,DIDBAM_INITIALIZE);
+    ok (SUCCEEDED(hr), "BuildActionMap failed hr=%08x\n",hr);
+
+    actions = lpdiaf->rgoAction;
+    obj_instance = DIDFT_GETINSTANCE(actions[action_index].dwObjID);
+    how = actions[action_index].dwHow;
+    assigned_to = actions[action_index].guidInstance;
+
+    ok (how == DIAH_USERCONFIG || how == DIAH_DEFAULT, "Action was not set dwHow=%08x\n",how);
+    ok (obj_instance == obj_expected, "Action not mapped correctly instance=%08x expected=%08x\n",
+        obj_instance, obj_expected);
+    ok (IsEqualGUID(&assigned_to,&ddi.guidInstance), "Action and device GUID do not match=%d", action_index);
+
+}
+
 static BOOL CALLBACK enumeration_callback(
     LPCDIDEVICEINSTANCE lpddi,
     LPDIRECTINPUTDEVICE8 lpdid,
@@ -161,6 +199,20 @@ static void test_action_mapping(void)
     ok( data.keyboard != NULL, "EnumDevicesBySemantics should enumerate the keyboard\n");
     ok( data.mouse != NULL, "EnumDevicesBySemantics should enumerate the mouse\n");
 
+    if (data.keyboard != NULL)
+        test_build_action_map(data.keyboard,data.lpdiaf,DITEST_KEYBOARDSPACE,DIK_SPACE);
+
+    /* Test BuildActionMap with no suitable actions for a device */
+    IDirectInputDevice_Unacquire(data.keyboard);
+    af.dwDataSize = 4 * DITEST_KEYBOARDSPACE;
+    af.dwNumActions = DITEST_KEYBOARDSPACE;
+
+    hr = IDirectInputDevice8_BuildActionMap(data.keyboard,data.lpdiaf,NULL,DIDBAM_INITIALIZE);
+    ok (hr == DI_NOEFFECT, "BuildActionMap should have no effect with no actions hr=%08x\n",hr);
+
+    af.dwDataSize = 4 * sizeof(actionMapping) / sizeof(actionMapping[0]);
+    af.dwNumActions = sizeof(actionMapping) / sizeof(actionMapping[0]);
+
     /* The call fails with a zeroed GUID */
     memset(&af.guidActionMap, 0, sizeof(GUID));
     hr = IDirectInput8_EnumDevicesBySemantics(pDI,0, &af, enumeration_callback, 0, 0);
-- 
1.7.0.4

