[PATCH 4/4] dinput: Implement DIPROP_APPDATA.

Arkadiusz Hiler ahiler at codeweavers.com
Mon Mar 8 06:36:11 CST 2021


This fixes not working analog inputs on game controllers in Slay the Spire.

Signed-off-by: Arkadiusz Hiler <ahiler at codeweavers.com>
---
 dlls/dinput/device.c        |  71 +++++++++++++++++
 dlls/dinput8/tests/device.c | 147 ++++++++++++++++++++++++++++++++++++
 2 files changed, 218 insertions(+)

diff --git a/dlls/dinput/device.c b/dlls/dinput/device.c
index 14c47dd3c7d..f1b334680e8 100644
--- a/dlls/dinput/device.c
+++ b/dlls/dinput/device.c
@@ -571,6 +571,22 @@ failed:
     return DIERR_OUTOFMEMORY;
 }
 
+static int verify_offset(const DataFormat *df, int offset)
+{
+    int i;
+
+    if (!df->offsets)
+        return -1;
+
+    for (i = df->wine_df->dwNumObjs - 1; i >= 0; i--)
+    {
+        if (df->offsets[i] == offset)
+            return offset;
+    }
+
+    return -1;
+}
+
 /* find an object by its offset in a data format */
 static int offset_to_object(const DataFormat *df, int offset)
 {
@@ -759,6 +775,44 @@ static BOOL load_mapping_settings(IDirectInputDeviceImpl *This, LPDIACTIONFORMAT
     return mapped > 0;
 }
 
+static BOOL set_app_data(IDirectInputDeviceImpl *dev, int offset, UINT_PTR app_data)
+{
+    int num_actions = dev->num_actions;
+    ActionMap *action_map = dev->action_map, *target_map = NULL;
+
+    if (num_actions == 0)
+    {
+        num_actions = 1;
+        action_map = HeapAlloc(GetProcessHeap(), 0, sizeof(ActionMap));
+        if (!action_map) return FALSE;
+        target_map = &action_map[0];
+    } else {
+        int i;
+        for (i = 0; i < num_actions; i++)
+        {
+            if (dev->action_map[i].offset != offset) continue;
+            target_map = &dev->action_map[i];
+            break;
+        }
+
+        if (!target_map)
+        {
+            num_actions++;
+            action_map = HeapReAlloc(GetProcessHeap(), 0, action_map, sizeof(ActionMap)*num_actions);
+            if (!action_map) return FALSE;
+            target_map = &action_map[num_actions-1];
+        }
+    }
+
+    target_map->offset = offset;
+    target_map->uAppData = app_data;
+
+    dev->action_map = action_map;
+    dev->num_actions = num_actions;
+
+    return TRUE;
+}
+
 HRESULT _build_action_map(LPDIRECTINPUTDEVICE8W iface, LPDIACTIONFORMATW lpdiaf, LPCWSTR lpszUserName, DWORD dwFlags, DWORD devMask, LPCDIDATAFORMAT df)
 {
     IDirectInputDeviceImpl *This = impl_from_IDirectInputDevice8W(iface);
@@ -1447,6 +1501,23 @@ HRESULT WINAPI IDirectInputDevice2WImpl_SetProperty(
                 lstrcpynW(device_player->username, ps->wsz, ARRAY_SIZE(device_player->username));
             break;
         }
+        case (DWORD_PTR) DIPROP_APPDATA:
+        {
+            int offset = -1;
+            LPCDIPROPPOINTER pp = (LPCDIPROPPOINTER)pdiph;
+            if (pdiph->dwSize != sizeof(DIPROPPOINTER)) return DIERR_INVALIDPARAM;
+
+            if (pdiph->dwHow == DIPH_BYID)
+                offset = id_to_offset(&This->data_format, pdiph->dwObj);
+            else if (pdiph->dwHow == DIPH_BYOFFSET)
+                offset = verify_offset(&This->data_format, pdiph->dwObj);
+            else
+                return DIERR_UNSUPPORTED;
+
+            if (offset == -1) return DIERR_OBJECTNOTFOUND;
+            if (!set_app_data(This, offset, pp->uData)) return DIERR_OUTOFMEMORY;
+            break;
+        }
         default:
             WARN("Unknown property %s\n", debugstr_guid(rguid));
             return DIERR_UNSUPPORTED;
diff --git a/dlls/dinput8/tests/device.c b/dlls/dinput8/tests/device.c
index 4a766280982..3e6da23b4ab 100644
--- a/dlls/dinput8/tests/device.c
+++ b/dlls/dinput8/tests/device.c
@@ -294,6 +294,48 @@ static BOOL CALLBACK enumeration_callback(const DIDEVICEINSTANCEA *lpddi, IDirec
     return DIENUM_CONTINUE;
 }
 
+static void test_appdata_property_vs_map(struct enum_data *data)
+{
+    HRESULT hr;
+    DIPROPPOINTER dp;
+
+    dp.diph.dwSize = sizeof(dp);
+    dp.diph.dwHeaderSize = sizeof(DIPROPHEADER);
+    dp.diph.dwHow = DIPH_BYID;
+    dp.diph.dwObj = DIDFT_MAKEINSTANCE(DIK_SPACE) | DIDFT_PSHBUTTON;
+    dp.uData = 10;
+    hr = IDirectInputDevice8_SetProperty(data->keyboard, DIPROP_APPDATA, &(dp.diph));
+    ok(SUCCEEDED(hr), "IDirectInputDevice8_SetProperty failed hr=%08x\n", hr);
+
+    test_device_input(data->keyboard, INPUT_KEYBOARD, VK_SPACE, 10);
+
+    dp.diph.dwHow = DIPH_BYID;
+    dp.diph.dwObj = DIDFT_MAKEINSTANCE(DIK_V) | DIDFT_PSHBUTTON;
+    dp.uData = 11;
+    hr = IDirectInputDevice8_SetProperty(data->keyboard, DIPROP_APPDATA, &(dp.diph));
+    ok(hr == DIERR_OBJECTNOTFOUND, "IDirectInputDevice8_SetProperty should not find key that's not in the action map hr=%08x\n", hr);
+
+    /* setting format should reset action map */
+    hr = IDirectInputDevice8_SetDataFormat(data->keyboard, &c_dfDIKeyboard);
+    ok(SUCCEEDED(hr), "SetDataFormat failed: %08x\n", hr);
+
+    test_device_input(data->keyboard, INPUT_KEYBOARD, VK_SPACE, -1);
+
+    dp.diph.dwHow = DIPH_BYID;
+    dp.diph.dwObj = DIDFT_MAKEINSTANCE(DIK_V) | DIDFT_PSHBUTTON;
+    dp.uData = 11;
+    hr = IDirectInputDevice8_SetProperty(data->keyboard, DIPROP_APPDATA, &(dp.diph));
+    ok(SUCCEEDED(hr), "IDirectInputDevice8_SetProperty failed hr=%08x\n", hr);
+
+    test_device_input(data->keyboard, INPUT_KEYBOARD, 'V', 11);
+
+    /* back to action map */
+    hr = IDirectInputDevice8_SetActionMap(data->keyboard, data->lpdiaf, NULL, 0);
+    ok(SUCCEEDED(hr), "SetActionMap failed hr=%08x\n", hr);
+
+    test_device_input(data->keyboard, INPUT_KEYBOARD, VK_SPACE, 2);
+}
+
 static void test_action_mapping(void)
 {
     HRESULT hr;
@@ -374,6 +416,8 @@ static void test_action_mapping(void)
 
         test_device_input(data.keyboard, INPUT_KEYBOARD, VK_SPACE, 2);
 
+        test_appdata_property_vs_map(&data);
+
         /* Test BuildActionMap with no suitable actions for a device */
         IDirectInputDevice_Unacquire(data.keyboard);
         af.dwDataSize = 4 * DITEST_KEYBOARDSPACE;
@@ -865,6 +909,108 @@ static void test_keyboard_events(void)
     DestroyWindow(hwnd);
 }
 
+static void test_appdata_property(void)
+{
+    HRESULT hr;
+    HINSTANCE hinst = GetModuleHandleA(NULL);
+    IDirectInputDevice8A *di_keyboard;
+    IDirectInput8A *pDI = NULL;
+    HWND hwnd;
+    DIPROPDWORD dw;
+    DIPROPPOINTER dp;
+
+    hr = CoCreateInstance(&CLSID_DirectInput8, 0, CLSCTX_INPROC_SERVER, &IID_IDirectInput8A, (LPVOID*)&pDI);
+    if (hr == DIERR_OLDDIRECTINPUTVERSION ||
+        hr == DIERR_BETADIRECTINPUTVERSION ||
+        hr == REGDB_E_CLASSNOTREG)
+    {
+        win_skip("DIPROP_APPDATA requires dinput8\n");
+        return;
+    }
+    ok(SUCCEEDED(hr), "DirectInput8 Create failed: hr=%08x\n", hr);
+    if (FAILED(hr)) return;
+
+    hr = IDirectInput8_Initialize(pDI,hinst, DIRECTINPUT_VERSION);
+    if (hr == DIERR_OLDDIRECTINPUTVERSION || hr == DIERR_BETADIRECTINPUTVERSION)
+    {
+        win_skip("DIPROP_APPDATA requires dinput8\n");
+        return;
+    }
+    ok(SUCCEEDED(hr), "DirectInput8 Initialize failed: hr=%08x\n", hr);
+    if (FAILED(hr)) return;
+
+    hwnd = CreateWindowExA(WS_EX_TOPMOST, "static", "dinput",
+            WS_POPUP | WS_VISIBLE, 0, 0, 100, 100, NULL, NULL, NULL, NULL);
+    ok(hwnd != NULL, "failed to create window\n");
+
+    hr = IDirectInput8_CreateDevice(pDI, &GUID_SysKeyboard, &di_keyboard, NULL);
+    ok(SUCCEEDED(hr), "IDirectInput8_CreateDevice failed: %08x\n", hr);
+
+    hr = IDirectInputDevice8_SetDataFormat(di_keyboard, &c_dfDIKeyboard);
+    ok(SUCCEEDED(hr), "IDirectInputDevice8_SetDataFormat failed: %08x\n", hr);
+
+    dw.diph.dwSize = sizeof(DIPROPDWORD);
+    dw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
+    dw.diph.dwObj = 0;
+    dw.diph.dwHow = DIPH_DEVICE;
+    dw.dwData = 32;
+    hr = IDirectInputDevice8_SetProperty(di_keyboard, DIPROP_BUFFERSIZE, &(dw.diph));
+    ok(SUCCEEDED(hr), "IDirectInputDevice8_SetProperty failed hr=%08x\n", hr);
+
+    /* the default value */
+    test_device_input(di_keyboard, INPUT_KEYBOARD, 'A', -1);
+
+    dp.diph.dwHow = DIPH_DEVICE;
+    dp.diph.dwObj = 0;
+    dp.uData = 1;
+    hr = IDirectInputDevice8_SetProperty(di_keyboard, DIPROP_APPDATA, &(dp.diph));
+    ok(hr == DIERR_INVALIDPARAM, "IDirectInputDevice8_SetProperty APPDATA for the device should be invalid hr=%08x\n", hr);
+
+    dp.diph.dwSize = sizeof(dp);
+    dp.diph.dwHeaderSize = sizeof(DIPROPHEADER);
+    dp.diph.dwHow = DIPH_BYUSAGE;
+    dp.diph.dwObj = 2;
+    dp.uData = 2;
+    hr = IDirectInputDevice8_SetProperty(di_keyboard, DIPROP_APPDATA, &(dp.diph));
+    ok(hr == DIERR_UNSUPPORTED, "IDirectInputDevice8_SetProperty APPDATA by usage should be unsupported hr=%08x\n", hr);
+
+    dp.diph.dwHow = DIPH_BYID;
+    dp.diph.dwObj = DIDFT_MAKEINSTANCE(DIK_SPACE) | DIDFT_PSHBUTTON;
+    dp.uData = 3;
+    hr = IDirectInputDevice8_SetProperty(di_keyboard, DIPROP_APPDATA, &(dp.diph));
+    ok(SUCCEEDED(hr), "IDirectInputDevice8_SetProperty failed hr=%08x\n", hr);
+
+    dp.diph.dwHow = DIPH_BYOFFSET;
+    dp.diph.dwObj = DIK_A;
+    dp.uData = 4;
+    hr = IDirectInputDevice8_SetProperty(di_keyboard, DIPROP_APPDATA, &(dp.diph));
+    ok(SUCCEEDED(hr), "IDirectInputDevice8_SetProperty failed hr=%08x\n", hr);
+
+    dp.diph.dwHow = DIPH_BYOFFSET;
+    dp.diph.dwObj = DIK_B;
+    dp.uData = 5;
+    hr = IDirectInputDevice8_SetProperty(di_keyboard, DIPROP_APPDATA, &(dp.diph));
+    ok(SUCCEEDED(hr), "IDirectInputDevice8_SetProperty failed hr=%08x\n", hr);
+
+    test_device_input(di_keyboard, INPUT_KEYBOARD, VK_SPACE, 3);
+    test_device_input(di_keyboard, INPUT_KEYBOARD, 'A', 4);
+    test_device_input(di_keyboard, INPUT_KEYBOARD, 'B', 5);
+    test_device_input(di_keyboard, INPUT_KEYBOARD, 'C', -1);
+
+    /* setting data format resets APPDATA */
+    hr = IDirectInputDevice8_SetDataFormat(di_keyboard, &c_dfDIKeyboard);
+    ok(SUCCEEDED(hr), "IDirectInputDevice8_SetDataFormat failed: %08x\n", hr);
+
+    test_device_input(di_keyboard, INPUT_KEYBOARD, VK_SPACE, -1);
+    test_device_input(di_keyboard, INPUT_KEYBOARD, 'A', -1);
+    test_device_input(di_keyboard, INPUT_KEYBOARD, 'B', -1);
+    test_device_input(di_keyboard, INPUT_KEYBOARD, 'C', -1);
+
+    DestroyWindow(hwnd);
+    IDirectInputDevice_Release(di_keyboard);
+    IDirectInput_Release(pDI);
+}
+
 START_TEST(device)
 {
     CoInitialize(NULL);
@@ -873,6 +1019,7 @@ START_TEST(device)
     test_save_settings();
     test_mouse_keyboard();
     test_keyboard_events();
+    test_appdata_property();
 
     CoUninitialize();
 }
-- 
2.30.1




More information about the wine-devel mailing list