From bce769d9179ffbcbc634e73ad124528e28c5c7a8 Mon Sep 17 00:00:00 2001 From: Jetro Jormalainen Date: Tue, 30 Apr 2019 09:20:54 +1000 Subject: [PATCH] dinput: Allow empty Joystick mappings. --- dlls/dinput/device.c | 82 ++++++++++++++++++++++++++++++------- dlls/dinput/joystick.c | 4 +- dlls/dinput/keyboard.c | 2 + dlls/dinput/mouse.c | 2 + dlls/dinput8/tests/device.c | 50 ++++++++++++++++++++++ 5 files changed, 125 insertions(+), 15 deletions(-) diff --git a/dlls/dinput/device.c b/dlls/dinput/device.c index d1bf934a827..6107ab26538 100644 --- a/dlls/dinput/device.c +++ b/dlls/dinput/device.c @@ -30,6 +30,7 @@ #include #include "wine/debug.h" #include "wine/unicode.h" +#include "wine/heap.h" #include "windef.h" #include "winbase.h" #include "winreg.h" @@ -643,12 +644,29 @@ static DWORD semantic_to_obj_id(IDirectInputDeviceImpl* This, DWORD dwSemantic) return type | (0x0000ff00 & (obj_instance << 8)); } +static void del_mapping_key(const WCHAR *device, const WCHAR *username, const WCHAR *guid) { + static const WCHAR subkey[] = { + 'S','o','f','t','w','a','r','e','\\', + 'W','i','n','e','\\', + 'D','i','r','e','c','t','I','n','p','u','t','\\', + 'M','a','p','p','i','n','g','s','\\','%','s','\\','%','s','\\','%','s','\0'}; + WCHAR *keyname; + + keyname = heap_alloc(sizeof(WCHAR) * (lstrlenW(subkey) + strlenW(username) + strlenW(device) + strlenW(guid))); + sprintfW(keyname, subkey, username, device, guid); + + /* Remove old key mappings so there will be no overlapping mappings */ + RegDeleteKeyW(HKEY_CURRENT_USER, keyname); + + heap_free(keyname); +} + /* * get_mapping_key * Retrieves an open registry key to save the mapping, parametrized for an username, * specific device and specific action mapping guid. */ -static HKEY get_mapping_key(const WCHAR *device, const WCHAR *username, const WCHAR *guid) +static HKEY get_mapping_key(const WCHAR *device, const WCHAR *username, const WCHAR *guid, BOOL create) { static const WCHAR subkey[] = { 'S','o','f','t','w','a','r','e','\\', @@ -663,8 +681,11 @@ static HKEY get_mapping_key(const WCHAR *device, const WCHAR *username, const WC sprintfW(keyname, subkey, username, device, guid); /* The key used is HKCU\Software\Wine\DirectInput\Mappings\[username]\[device]\[mapping_guid] */ - if (RegCreateKeyW(HKEY_CURRENT_USER, keyname, &hkey)) - hkey = 0; + if (create) { + if (RegCreateKeyW(HKEY_CURRENT_USER, keyname, &hkey)) + hkey = 0; + } else if (RegOpenKeyW(HKEY_CURRENT_USER, keyname, &hkey)) + hkey = 0; HeapFree(GetProcessHeap(), 0, keyname); @@ -684,7 +705,9 @@ static HRESULT save_mapping_settings(IDirectInputDevice8W *iface, LPDIACTIONFORM if (StringFromCLSID(&lpdiaf->guidActionMap, &guid_str) != S_OK) return DI_SETTINGSNOTSAVED; - hkey = get_mapping_key(didev.tszInstanceName, lpszUsername, guid_str); + del_mapping_key(didev.tszInstanceName, lpszUsername, guid_str); + + hkey = get_mapping_key(didev.tszInstanceName, lpszUsername, guid_str, TRUE); if (!hkey) { @@ -719,7 +742,7 @@ BOOL load_mapping_settings(IDirectInputDeviceImpl *This, LPDIACTIONFORMATW lpdia HKEY hkey; WCHAR *guid_str; DIDEVICEINSTANCEW didev; - int i, mapped = 0; + int i; didev.dwSize = sizeof(didev); IDirectInputDevice8_GetDeviceInfo(&This->IDirectInputDevice8W_iface, &didev); @@ -727,7 +750,7 @@ BOOL load_mapping_settings(IDirectInputDeviceImpl *This, LPDIACTIONFORMATW lpdia if (StringFromCLSID(&lpdiaf->guidActionMap, &guid_str) != S_OK) return FALSE; - hkey = get_mapping_key(didev.tszInstanceName, username, guid_str); + hkey = get_mapping_key(didev.tszInstanceName, username, guid_str, FALSE); if (!hkey) { @@ -748,15 +771,21 @@ BOOL load_mapping_settings(IDirectInputDeviceImpl *This, LPDIACTIONFORMATW lpdia { lpdiaf->rgoAction[i].dwObjID = id; lpdiaf->rgoAction[i].guidInstance = didev.guidInstance; - lpdiaf->rgoAction[i].dwHow = DIAH_DEFAULT; - mapped += 1; + lpdiaf->rgoAction[i].dwHow = DIAH_USERCONFIG; + } + else + { + memset(&lpdiaf->rgoAction[i].guidInstance, 0, sizeof(GUID)); + lpdiaf->rgoAction[i].dwHow = DIAH_UNMAPPED; } + } RegCloseKey(hkey); CoTaskMemFree(guid_str); - return mapped > 0; + /* On Windows BuildActionMap can open empty mapping, so always return TRUE if get_mapping_key is success */ + return TRUE; } HRESULT _build_action_map(LPDIRECTINPUTDEVICE8W iface, LPDIACTIONFORMATW lpdiaf, LPCWSTR lpszUserName, DWORD dwFlags, DWORD devMask, LPCDIDATAFORMAT df) @@ -779,13 +808,18 @@ HRESULT _build_action_map(LPDIRECTINPUTDEVICE8W iface, LPDIACTIONFORMATW lpdiaf, load_success = load_mapping_settings(This, lpdiaf, username); } - if (load_success) return DI_OK; + if (load_success) { + /* Update dwCRC to track if action format has changed */ + for (i=0; i < lpdiaf->dwNumActions; i++) + { + lpdiaf->dwCRC ^= (lpdiaf->rgoAction[i].dwObjID << i * 2) | (lpdiaf->rgoAction[i].dwObjID >> (sizeof(lpdiaf->dwCRC) * 8 - i * 2)); + lpdiaf->dwCRC ^= (lpdiaf->rgoAction[i].dwSemantic << (i * 2 + 5)) | (lpdiaf->rgoAction[i].dwSemantic >> (sizeof(lpdiaf->dwCRC) * 8 - (i * 2 + 5))); + } + return DI_OK; + } for (i=0; i < lpdiaf->dwNumActions; i++) { - /* Don't touch a user configured action */ - if (lpdiaf->rgoAction[i].dwHow == DIAH_USERCONFIG) continue; - if ((lpdiaf->rgoAction[i].dwSemantic & devMask) == devMask) { DWORD obj_id = semantic_to_obj_id(This, lpdiaf->rgoAction[i].dwSemantic); @@ -816,6 +850,14 @@ HRESULT _build_action_map(LPDIRECTINPUTDEVICE8W iface, LPDIACTIONFORMATW lpdiaf, } } + /* Update dwCRC to track if action format has changed */ + lpdiaf->dwCRC = 0; + for (i=0; i < lpdiaf->dwNumActions; i++) + { + lpdiaf->dwCRC ^= (lpdiaf->rgoAction[i].dwObjID << i * 2) | (lpdiaf->rgoAction[i].dwObjID >> (sizeof(lpdiaf->dwCRC) * 8 - i * 2)); + lpdiaf->dwCRC ^= (lpdiaf->rgoAction[i].dwSemantic << (i * 2 + 5)) | (lpdiaf->rgoAction[i].dwSemantic >> (sizeof(lpdiaf->dwCRC) * 8 - (i * 2 + 5))); + } + if (!has_actions) return DI_NOEFFECT; return IDirectInputDevice8WImpl_BuildActionMap(iface, lpdiaf, lpszUserName, dwFlags); @@ -831,6 +873,7 @@ HRESULT _set_action_map(LPDIRECTINPUTDEVICE8W iface, LPDIACTIONFORMATW lpdiaf, L DIPROPSTRING dps; WCHAR username[MAX_PATH]; DWORD username_size = MAX_PATH; + DWORD new_crc = 0; int i, action = 0, num_actions = 0; unsigned int offset = 0; @@ -841,12 +884,23 @@ HRESULT _set_action_map(LPDIRECTINPUTDEVICE8W iface, LPDIACTIONFORMATW lpdiaf, L data_format.dwFlags = DIDF_RELAXIS; data_format.dwDataSize = lpdiaf->dwDataSize; + /* Calculate checksum for actionformat */ + for (i=0; i < lpdiaf->dwNumActions; i++) + { + new_crc ^= (lpdiaf->rgoAction[i].dwObjID << i * 2) | (lpdiaf->rgoAction[i].dwObjID >> (sizeof(lpdiaf->dwCRC) * 8 - i * 2)); + new_crc ^= (lpdiaf->rgoAction[i].dwSemantic << (i * 2 + 5)) | (lpdiaf->rgoAction[i].dwSemantic >> (sizeof(lpdiaf->dwCRC) * 8 - (i * 2 + 5))); + } + /* Count the actions */ for (i=0; i < lpdiaf->dwNumActions; i++) if (IsEqualGUID(&This->guid, &lpdiaf->rgoAction[i].guidInstance)) num_actions++; - if (num_actions == 0) return DI_NOEFFECT; + /* Should return DI_NOEFFECT if we dont have any actions and actionformat has not changed */ + if (num_actions == 0 && lpdiaf->dwCRC == new_crc && !(dwFlags & DIDSAM_FORCESAVE)) return DI_NOEFFECT; + + /* update dwCRC to track if action format has changed */ + lpdiaf->dwCRC = new_crc; This->num_actions = num_actions; diff --git a/dlls/dinput/joystick.c b/dlls/dinput/joystick.c index 66d6cfdffb9..73215dac0d5 100644 --- a/dlls/dinput/joystick.c +++ b/dlls/dinput/joystick.c @@ -847,7 +847,7 @@ HRESULT WINAPI JoystickWGenericImpl_BuildActionMap(LPDIRECTINPUTDEVICE8W iface, else lstrcpynW(username, lpszUserName, size); - load_success = load_mapping_settings((IDirectInputDeviceImpl *) This, lpdiaf, username); + load_success = load_mapping_settings(&This->base, lpdiaf, username); heap_free(username); } @@ -957,6 +957,8 @@ HRESULT WINAPI JoystickAGenericImpl_SetActionMap(LPDIRECTINPUTDEVICE8A iface, hr = JoystickWGenericImpl_SetActionMap(&This->base.IDirectInputDevice8W_iface, &diafW, lpszUserNameW, dwFlags); + lpdiaf->dwCRC = diafW.dwCRC; + HeapFree(GetProcessHeap(), 0, diafW.rgoAction); HeapFree(GetProcessHeap(), 0, lpszUserNameW); diff --git a/dlls/dinput/keyboard.c b/dlls/dinput/keyboard.c index b5e665933ec..0747f5d8c8b 100644 --- a/dlls/dinput/keyboard.c +++ b/dlls/dinput/keyboard.c @@ -690,6 +690,8 @@ static HRESULT WINAPI SysKeyboardAImpl_SetActionMap(LPDIRECTINPUTDEVICE8A iface, hr = SysKeyboardWImpl_SetActionMap(&This->base.IDirectInputDevice8W_iface, &diafW, lpszUserNameW, dwFlags); + lpdiaf->dwCRC = diafW.dwCRC; + HeapFree(GetProcessHeap(), 0, diafW.rgoAction); HeapFree(GetProcessHeap(), 0, lpszUserNameW); diff --git a/dlls/dinput/mouse.c b/dlls/dinput/mouse.c index 52a766b2a1a..796f1fa2977 100644 --- a/dlls/dinput/mouse.c +++ b/dlls/dinput/mouse.c @@ -862,6 +862,8 @@ static HRESULT WINAPI SysMouseAImpl_SetActionMap(LPDIRECTINPUTDEVICE8A iface, hr = SysMouseWImpl_SetActionMap(&This->base.IDirectInputDevice8W_iface, &diafW, lpszUserNameW, dwFlags); + lpdiaf->dwCRC = diafW.dwCRC; + HeapFree(GetProcessHeap(), 0, diafW.rgoAction); HeapFree(GetProcessHeap(), 0, lpszUserNameW); diff --git a/dlls/dinput8/tests/device.c b/dlls/dinput8/tests/device.c index f3e7b542355..bec2a6b863c 100644 --- a/dlls/dinput8/tests/device.c +++ b/dlls/dinput8/tests/device.c @@ -38,6 +38,8 @@ struct enum_data { /* Dummy GUID */ static const GUID ACTION_MAPPING_GUID = { 0x1, 0x2, 0x3, { 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb } }; +static const GUID NULL_GUID = { 0, 0, 0, { 0, 0, 0, 0, 0, 0, 0, 0 } }; + enum { DITEST_AXIS, DITEST_BUTTON, @@ -365,6 +367,17 @@ static void test_action_mapping(void) hr = IDirectInputDevice8_SetActionMap(data.keyboard, data.lpdiaf, NULL, 0); ok (hr == DI_NOEFFECT, "SetActionMap should have no effect with no actions to map hr=%08x\n", hr); + /* Test that after changing actionformat SetActionMap has effect and that second + * SetActionMap call with same empty actionformat has no effect */ + af.dwDataSize = 4 * 1; + af.dwNumActions = 1; + + hr = IDirectInputDevice8_SetActionMap(data.keyboard, data.lpdiaf, NULL, 0); + ok (hr != DI_NOEFFECT, "SetActionMap should have effect as actionformat has changed hr=%08x\n", hr); + + hr = IDirectInputDevice8_SetActionMap(data.keyboard, data.lpdiaf, NULL, 0); + ok (hr == DI_NOEFFECT, "SetActionMap should have no effect with no actions to map hr=%08x\n", hr); + af.dwDataSize = 4 * ARRAY_SIZE(actionMapping); af.dwNumActions = ARRAY_SIZE(actionMapping); @@ -556,6 +569,43 @@ static void test_save_settings(void) "Mapped incorrectly expected: 0x%08x got: 0x%08x\n", other_results[1], af.rgoAction[1].dwObjID); ok (IsEqualGUID(&GUID_SysKeyboard, &af.rgoAction[1].guidInstance), "Action should be mapped to keyboard\n"); + /* Save and load empty mapping */ + af.rgoAction[0].dwObjID = 0; + af.rgoAction[0].dwHow = 0; + memset(&af.rgoAction[0].guidInstance, 0, sizeof(GUID)); + af.rgoAction[1].dwObjID = 0; + af.rgoAction[1].dwHow = 0; + memset(&af.rgoAction[1].guidInstance, 0, sizeof(GUID)); + + hr = IDirectInputDevice8_SetActionMap(pKey, &af, NULL, DIDSAM_FORCESAVE); + ok (SUCCEEDED(hr), "SetActionMap failed hr=%08x\n", hr); + + if (hr == DI_SETTINGSNOTSAVED) + { + skip ("Can't test saving settings if SetActionMap returns DI_SETTINGSNOTSAVED\n"); + return; + } + + af.rgoAction[0].dwObjID = other_results[0]; + af.rgoAction[0].dwHow = DIAH_USERCONFIG; + af.rgoAction[0].guidInstance = GUID_SysKeyboard; + af.rgoAction[1].dwObjID = other_results[1]; + af.rgoAction[1].dwHow = DIAH_USERCONFIG; + af.rgoAction[1].guidInstance = GUID_SysKeyboard; + + hr = IDirectInputDevice8_BuildActionMap(pKey, &af, NULL, 0); + ok (SUCCEEDED(hr), "BuildActionMap failed hr=%08x\n", hr); + + ok (other_results[0] == af.rgoAction[0].dwObjID, + "Mapped incorrectly expected: 0x%08x got: 0x%08x\n", other_results[0], af.rgoAction[0].dwObjID); + ok (af.rgoAction[0].dwHow == DIAH_UNMAPPED, "dwHow should have been DIAH_UNMAPPED\n"); + ok (IsEqualGUID(&NULL_GUID, &af.rgoAction[0].guidInstance), "Action should not be mapped\n"); + + ok (other_results[1] == af.rgoAction[1].dwObjID, + "Mapped incorrectly expected: 0x%08x got: 0x%08x\n", other_results[1], af.rgoAction[1].dwObjID); + ok (af.rgoAction[1].dwHow == DIAH_UNMAPPED, "dwHow should have been DIAH_UNMAPPED\n"); + ok (IsEqualGUID(&NULL_GUID, &af.rgoAction[1].guidInstance), "Action should not be mapped\n"); + IDirectInputDevice_Release(pKey); IDirectInput_Release(pDI); } -- 2.17.1