dinput: Implement DIPROP_APPDATA.

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

Signed-off-by: Arkadiusz Hiler <ahiler@codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
Arkadiusz Hiler 2021-03-08 14:36:11 +02:00 committed by Alexandre Julliard
parent fb56afae15
commit 8d206333fd
2 changed files with 220 additions and 0 deletions

View File

@ -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,46 @@ 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 +1503,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;

View File

@ -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();
}