mirror of
https://gitlab.winehq.org/wine/wine-staging.git
synced 2024-09-13 09:17:20 -07:00
09634de79c
The patch set is disabled now. We want to make sure that these tests are ported when it's rewritten.
259 lines
11 KiB
Diff
259 lines
11 KiB
Diff
From e7104770d4b57539d5b64a67212504910692e920 Mon Sep 17 00:00:00 2001
|
|
From: Jetro Jormalainen <jje-wine@jv.jetro.fi>
|
|
Date: Tue, 30 Apr 2019 09:20:54 +1000
|
|
Subject: [PATCH] dinput: Allow empty Joystick mappings.
|
|
|
|
---
|
|
dlls/dinput/device.c | 77 ++++++++++++++++++++++++++++++-------
|
|
dlls/dinput/tests/device8.c | 50 ++++++++++++++++++++++++
|
|
2 files changed, 113 insertions(+), 14 deletions(-)
|
|
|
|
diff --git a/dlls/dinput/device.c b/dlls/dinput/device.c
|
|
index 6cc190ee7fb..2fd9329aa68 100644
|
|
--- a/dlls/dinput/device.c
|
|
+++ b/dlls/dinput/device.c
|
|
@@ -364,12 +364,26 @@ static DWORD semantic_to_obj_id( struct dinput_device *This, DWORD dwSemantic )
|
|
return type | (0x0000ff00 & (instance << 8));
|
|
}
|
|
|
|
+static void del_mapping_key(const WCHAR *device, const WCHAR *username, const WCHAR *guid) {
|
|
+ static const WCHAR subkey[] = L"Software\\Wine\\DirectInput\\Mappings\\%s\\%s\\%s";
|
|
+ DWORD len = wcslen(subkey) + wcslen(username) + wcslen(device) + wcslen(guid);
|
|
+ WCHAR *keyname;
|
|
+
|
|
+ keyname = malloc(len * sizeof(WCHAR));
|
|
+ swprintf(keyname, len, subkey, username, device, guid);
|
|
+
|
|
+ /* Remove old key mappings so there will be no overlapping mappings */
|
|
+ RegDeleteKeyW(HKEY_CURRENT_USER, keyname);
|
|
+
|
|
+ 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 = L"Software\\Wine\\DirectInput\\Mappings\\%s\\%s\\%s";
|
|
HKEY hkey;
|
|
@@ -380,8 +394,11 @@ static HKEY get_mapping_key(const WCHAR *device, const WCHAR *username, const WC
|
|
swprintf( keyname, len, 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;
|
|
|
|
free( keyname );
|
|
|
|
@@ -401,7 +418,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)
|
|
{
|
|
@@ -436,7 +455,7 @@ static BOOL load_mapping_settings( struct dinput_device *This, LPDIACTIONFORMATW
|
|
HKEY hkey;
|
|
WCHAR *guid_str;
|
|
DIDEVICEINSTANCEW didev;
|
|
- int i, mapped = 0;
|
|
+ int i;
|
|
|
|
didev.dwSize = sizeof(didev);
|
|
IDirectInputDevice8_GetDeviceInfo(&This->IDirectInputDevice8W_iface, &didev);
|
|
@@ -444,7 +463,7 @@ static BOOL load_mapping_settings( struct dinput_device *This, LPDIACTIONFORMATW
|
|
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)
|
|
{
|
|
@@ -464,15 +483,20 @@ static BOOL load_mapping_settings( struct dinput_device *This, LPDIACTIONFORMATW
|
|
{
|
|
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;
|
|
}
|
|
|
|
static BOOL set_app_data( struct dinput_device *dev, int offset, UINT_PTR app_data )
|
|
@@ -1935,13 +1959,18 @@ static HRESULT WINAPI dinput_device_BuildActionMap( IDirectInputDevice8W *iface,
|
|
load_success = load_mapping_settings( impl, format, username_buf );
|
|
}
|
|
|
|
- if (load_success) return DI_OK;
|
|
+ if (load_success) {
|
|
+ /* Update dwCRC to track if action format has changed */
|
|
+ for (i=0; i < format->dwNumActions; i++)
|
|
+ {
|
|
+ format->dwCRC ^= (format->rgoAction[i].dwObjID << i * 2) | (format->rgoAction[i].dwObjID >> (sizeof(format->dwCRC) * 8 - i * 2));
|
|
+ format->dwCRC ^= (format->rgoAction[i].dwSemantic << (i * 2 + 5)) | (format->rgoAction[i].dwSemantic >> (sizeof(format->dwCRC) * 8 - (i * 2 + 5)));
|
|
+ }
|
|
+ return DI_OK;
|
|
+ }
|
|
|
|
for (i = 0; i < format->dwNumActions; i++)
|
|
{
|
|
- /* Don't touch a user configured action */
|
|
- if (format->rgoAction[i].dwHow == DIAH_USERCONFIG) continue;
|
|
-
|
|
genre = format->rgoAction[i].dwSemantic & DIGENRE_ANY;
|
|
if (devMask == genre || (devMask == DIGENRE_ANY && genre != DIMOUSE_MASK && genre != DIKEYBOARD_MASK))
|
|
{
|
|
@@ -1973,6 +2002,14 @@ static HRESULT WINAPI dinput_device_BuildActionMap( IDirectInputDevice8W *iface,
|
|
}
|
|
}
|
|
|
|
+ /* Update dwCRC to track if action format has changed */
|
|
+ format->dwCRC = 0;
|
|
+ for (i=0; i < format->dwNumActions; i++)
|
|
+ {
|
|
+ format->dwCRC ^= (format->rgoAction[i].dwObjID << i * 2) | (format->rgoAction[i].dwObjID >> (sizeof(format->dwCRC) * 8 - i * 2));
|
|
+ format->dwCRC ^= (format->rgoAction[i].dwSemantic << (i * 2 + 5)) | (format->rgoAction[i].dwSemantic >> (sizeof(format->dwCRC) * 8 - (i * 2 + 5)));
|
|
+ }
|
|
+
|
|
if (!has_actions) return DI_NOEFFECT;
|
|
if (flags & (DIDBAM_DEFAULT|DIDBAM_PRESERVE|DIDBAM_INITIALIZE|DIDBAM_HWDEFAULTS))
|
|
FIXME( "Unimplemented flags %#lx\n", flags );
|
|
@@ -1990,6 +2027,7 @@ static HRESULT WINAPI dinput_device_SetActionMap( IDirectInputDevice8W *iface, D
|
|
DIPROPSTRING dps;
|
|
WCHAR username_buf[MAX_PATH];
|
|
DWORD username_len = MAX_PATH;
|
|
+ DWORD new_crc = 0;
|
|
int i, action = 0, num_actions = 0;
|
|
unsigned int offset = 0;
|
|
const DIDATAFORMAT *df;
|
|
@@ -2022,12 +2060,23 @@ static HRESULT WINAPI dinput_device_SetActionMap( IDirectInputDevice8W *iface, D
|
|
data_format.dwFlags = DIDF_RELAXIS;
|
|
data_format.dwDataSize = format->dwDataSize;
|
|
|
|
+ /* Calculate checksum for actionformat */
|
|
+ for (i=0; i < format->dwNumActions; i++)
|
|
+ {
|
|
+ new_crc ^= (format->rgoAction[i].dwObjID << i * 2) | (format->rgoAction[i].dwObjID >> (sizeof(format->dwCRC) * 8 - i * 2));
|
|
+ new_crc ^= (format->rgoAction[i].dwSemantic << (i * 2 + 5)) | (format->rgoAction[i].dwSemantic >> (sizeof(format->dwCRC) * 8 - (i * 2 + 5)));
|
|
+ }
|
|
+
|
|
/* Count the actions */
|
|
for (i = 0; i < format->dwNumActions; i++)
|
|
if (IsEqualGUID( &impl->guid, &format->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 && format->dwCRC == new_crc && !(flags & DIDSAM_FORCESAVE)) return DI_NOEFFECT;
|
|
+
|
|
+ /* update dwCRC to track if action format has changed */
|
|
+ format->dwCRC = new_crc;
|
|
|
|
/* Construct the dataformat and actionmap */
|
|
obj_df = malloc( sizeof(DIOBJECTDATAFORMAT) * num_actions );
|
|
diff --git a/dlls/dinput/tests/device8.c b/dlls/dinput/tests/device8.c
|
|
index 2586736cb1d..e2be36ad7d3 100644
|
|
--- a/dlls/dinput/tests/device8.c
|
|
+++ b/dlls/dinput/tests/device8.c
|
|
@@ -48,6 +48,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,
|
|
@@ -472,6 +474,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=%#lx\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);
|
|
|
|
@@ -663,6 +676,43 @@ static void test_save_settings(void)
|
|
"Mapped incorrectly expected: 0x%#lx got: 0x%#lx\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.34.1
|
|
|