wine-staging/patches/dinput-joy-mappings/0001-dinput-Allow-empty-Joystick-mappings.patch

259 lines
11 KiB
Diff
Raw Permalink Normal View History

From e7104770d4b57539d5b64a67212504910692e920 Mon Sep 17 00:00:00 2001
2019-05-02 17:37:32 -07:00
From: Jetro Jormalainen <jje-wine@jv.jetro.fi>
Date: Tue, 30 Apr 2019 09:20:54 +1000
2019-10-10 20:12:48 -07:00
Subject: [PATCH] dinput: Allow empty Joystick mappings.
2019-05-02 17:37:32 -07:00
---
dlls/dinput/device.c | 77 ++++++++++++++++++++++++++++++-------
dlls/dinput/tests/device8.c | 50 ++++++++++++++++++++++++
2 files changed, 113 insertions(+), 14 deletions(-)
2019-05-02 17:37:32 -07:00
diff --git a/dlls/dinput/device.c b/dlls/dinput/device.c
index 6cc190ee7fb..2fd9329aa68 100644
2019-05-02 17:37:32 -07:00
--- 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 )
2021-11-12 23:41:08 -08:00
return type | (0x0000ff00 & (instance << 8));
2019-05-02 17:37:32 -07:00
}
+static void del_mapping_key(const WCHAR *device, const WCHAR *username, const WCHAR *guid) {
2021-11-12 23:41:08 -08:00
+ static const WCHAR subkey[] = L"Software\\Wine\\DirectInput\\Mappings\\%s\\%s\\%s";
+ DWORD len = wcslen(subkey) + wcslen(username) + wcslen(device) + wcslen(guid);
2019-05-02 17:37:32 -07:00
+ WCHAR *keyname;
+
2021-11-12 23:41:08 -08:00
+ keyname = malloc(len * sizeof(WCHAR));
+ swprintf(keyname, len, subkey, username, device, guid);
2019-05-02 17:37:32 -07:00
+
+ /* Remove old key mappings so there will be no overlapping mappings */
+ RegDeleteKeyW(HKEY_CURRENT_USER, keyname);
+
2021-11-12 23:41:08 -08:00
+ free(keyname);
2019-05-02 17:37:32 -07:00
+}
+
/*
* 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)
{
2021-11-12 23:41:08 -08:00
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
2021-11-12 23:41:08 -08:00
swprintf( keyname, len, subkey, username, device, guid );
2019-05-02 17:37:32 -07:00
/* 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;
2021-11-12 23:41:08 -08:00
free( keyname );
2019-05-02 17:37:32 -07:00
@@ -401,7 +418,9 @@ static HRESULT save_mapping_settings(IDirectInputDevice8W *iface, LPDIACTIONFORM
2019-05-02 17:37:32 -07:00
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
2019-05-02 17:37:32 -07:00
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
2019-05-02 17:37:32 -07:00
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
2019-05-02 17:37:32 -07:00
{
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;
2021-11-12 23:41:08 -08:00
+ }
2019-05-02 17:37:32 -07:00
+ else
+ {
+ memset(&lpdiaf->rgoAction[i].guidInstance, 0, sizeof(GUID));
+ lpdiaf->rgoAction[i].dwHow = DIAH_UNMAPPED;
2021-11-12 23:41:08 -08:00
}
2019-05-02 17:37:32 -07:00
}
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;
}
2021-11-12 23:41:08 -08:00
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,
2021-11-12 23:41:08 -08:00
load_success = load_mapping_settings( impl, format, username_buf );
2019-05-02 17:37:32 -07:00
}
- if (load_success) return DI_OK;
+ if (load_success) {
+ /* Update dwCRC to track if action format has changed */
2021-11-12 23:41:08 -08:00
+ for (i=0; i < format->dwNumActions; i++)
2019-05-02 17:37:32 -07:00
+ {
2021-11-12 23:41:08 -08:00
+ 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)));
2019-05-02 17:37:32 -07:00
+ }
+ return DI_OK;
+ }
2021-11-12 23:41:08 -08:00
for (i = 0; i < format->dwNumActions; i++)
2019-05-02 17:37:32 -07:00
{
- /* Don't touch a user configured action */
2021-11-12 23:41:08 -08:00
- if (format->rgoAction[i].dwHow == DIAH_USERCONFIG) continue;
2019-05-02 17:37:32 -07:00
-
2021-11-12 23:41:08 -08:00
genre = format->rgoAction[i].dwSemantic & DIGENRE_ANY;
if (devMask == genre || (devMask == DIGENRE_ANY && genre != DIMOUSE_MASK && genre != DIKEYBOARD_MASK))
2019-05-02 17:37:32 -07:00
{
@@ -1973,6 +2002,14 @@ static HRESULT WINAPI dinput_device_BuildActionMap( IDirectInputDevice8W *iface,
2019-05-02 17:37:32 -07:00
}
}
+ /* Update dwCRC to track if action format has changed */
2021-11-12 23:41:08 -08:00
+ format->dwCRC = 0;
+ for (i=0; i < format->dwNumActions; i++)
2019-05-02 17:37:32 -07:00
+ {
2021-11-12 23:41:08 -08:00
+ 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)));
2019-05-02 17:37:32 -07:00
+ }
+
if (!has_actions) return DI_NOEFFECT;
2021-11-12 23:41:08 -08:00
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
2019-05-02 17:37:32 -07:00
DIPROPSTRING dps;
2021-11-12 23:41:08 -08:00
WCHAR username_buf[MAX_PATH];
DWORD username_len = MAX_PATH;
2019-05-02 17:37:32 -07:00
+ DWORD new_crc = 0;
int i, action = 0, num_actions = 0;
unsigned int offset = 0;
2021-11-12 23:41:08 -08:00
const DIDATAFORMAT *df;
@@ -2022,12 +2060,23 @@ static HRESULT WINAPI dinput_device_SetActionMap( IDirectInputDevice8W *iface, D
2019-05-02 17:37:32 -07:00
data_format.dwFlags = DIDF_RELAXIS;
2021-11-12 23:41:08 -08:00
data_format.dwDataSize = format->dwDataSize;
2019-05-02 17:37:32 -07:00
+ /* Calculate checksum for actionformat */
2021-11-12 23:41:08 -08:00
+ for (i=0; i < format->dwNumActions; i++)
2019-05-02 17:37:32 -07:00
+ {
2021-11-12 23:41:08 -08:00
+ 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)));
2019-05-02 17:37:32 -07:00
+ }
+
/* Count the actions */
2021-11-12 23:41:08 -08:00
for (i = 0; i < format->dwNumActions; i++)
if (IsEqualGUID( &impl->guid, &format->rgoAction[i].guidInstance ))
2019-05-02 17:37:32 -07:00
num_actions++;
- if (num_actions == 0) return DI_NOEFFECT;
+ /* Should return DI_NOEFFECT if we dont have any actions and actionformat has not changed */
2021-11-12 23:41:08 -08:00
+ if (num_actions == 0 && format->dwCRC == new_crc && !(flags & DIDSAM_FORCESAVE)) return DI_NOEFFECT;
2019-05-02 17:37:32 -07:00
+
+ /* update dwCRC to track if action format has changed */
2021-11-12 23:41:08 -08:00
+ format->dwCRC = new_crc;
2019-05-02 17:37:32 -07:00
/* Construct the dataformat and actionmap */
2021-11-12 23:41:08 -08:00
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);
}
2019-05-02 17:37:32 -07:00
--
2.34.1
2019-05-02 17:37:32 -07:00