This fixes not working analog inputs on game controllers in Slay the Spire.
Signed-off-by: Arkadiusz Hiler ahiler@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(); }