2021-05-27 15:10:15 -07:00
|
|
|
From 2ff7da682aa87e09966fa0fb7c8846cd82bd1eea 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
|
|
|
|
|
|
|
---
|
2019-10-10 19:30:46 -07:00
|
|
|
dlls/dinput/device.c | 82 ++++++++++++++++++++++++++++++-------
|
2021-05-27 15:10:15 -07:00
|
|
|
dlls/dinput/joystick.c | 2 +-
|
2019-10-10 19:30:46 -07:00
|
|
|
dlls/dinput8/tests/device.c | 50 ++++++++++++++++++++++
|
2021-05-27 15:10:15 -07:00
|
|
|
3 files changed, 119 insertions(+), 15 deletions(-)
|
2019-05-02 17:37:32 -07:00
|
|
|
|
|
|
|
diff --git a/dlls/dinput/device.c b/dlls/dinput/device.c
|
2021-05-27 15:10:15 -07:00
|
|
|
index e99d14b4cff..540034de1e9 100644
|
2019-05-02 17:37:32 -07:00
|
|
|
--- a/dlls/dinput/device.c
|
|
|
|
+++ b/dlls/dinput/device.c
|
2019-10-10 19:30:46 -07:00
|
|
|
@@ -30,6 +30,7 @@
|
|
|
|
#include <string.h>
|
|
|
|
#include "wine/debug.h"
|
|
|
|
#include "wine/unicode.h"
|
|
|
|
+#include "wine/heap.h"
|
|
|
|
#include "windef.h"
|
|
|
|
#include "winbase.h"
|
|
|
|
#include "winreg.h"
|
2021-05-27 15:10:15 -07:00
|
|
|
@@ -655,12 +656,29 @@ static DWORD semantic_to_obj_id(IDirectInputDeviceImpl* This, DWORD dwSemantic)
|
2019-05-02 17:37:32 -07:00
|
|
|
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;
|
|
|
|
+
|
2019-10-10 19:30:46 -07:00
|
|
|
+ keyname = heap_alloc(sizeof(WCHAR) * (lstrlenW(subkey) + strlenW(username) + strlenW(device) + strlenW(guid)));
|
2019-05-02 17:37:32 -07:00
|
|
|
+ sprintfW(keyname, subkey, username, device, guid);
|
|
|
|
+
|
|
|
|
+ /* Remove old key mappings so there will be no overlapping mappings */
|
|
|
|
+ RegDeleteKeyW(HKEY_CURRENT_USER, keyname);
|
|
|
|
+
|
2019-10-10 19:30:46 -07:00
|
|
|
+ heap_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)
|
|
|
|
{
|
|
|
|
static const WCHAR subkey[] = {
|
|
|
|
'S','o','f','t','w','a','r','e','\\',
|
2021-05-27 15:10:15 -07:00
|
|
|
@@ -675,8 +693,11 @@ static HKEY get_mapping_key(const WCHAR *device, const WCHAR *username, const WC
|
2019-05-02 17:37:32 -07:00
|
|
|
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);
|
|
|
|
|
2021-05-27 15:10:15 -07:00
|
|
|
@@ -696,7 +717,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)
|
|
|
|
{
|
2021-05-27 15:10:15 -07:00
|
|
|
@@ -731,7 +754,7 @@ BOOL load_mapping_settings(IDirectInputDeviceImpl *This, LPDIACTIONFORMATW lpdia
|
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);
|
2021-05-27 15:10:15 -07:00
|
|
|
@@ -739,7 +762,7 @@ BOOL load_mapping_settings(IDirectInputDeviceImpl *This, LPDIACTIONFORMATW lpdia
|
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)
|
|
|
|
{
|
2021-05-27 15:10:15 -07:00
|
|
|
@@ -760,15 +783,21 @@ BOOL load_mapping_settings(IDirectInputDeviceImpl *This, LPDIACTIONFORMATW lpdia
|
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-03-10 17:40:52 -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-03-10 17:40:52 -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-03-10 17:40:52 -08:00
|
|
|
static BOOL set_app_data(IDirectInputDeviceImpl *dev, int offset, UINT_PTR app_data)
|
2021-05-27 15:10:15 -07:00
|
|
|
@@ -831,13 +860,18 @@ HRESULT _build_action_map(LPDIRECTINPUTDEVICE8W iface, LPDIACTIONFORMATW lpdiaf,
|
2019-05-02 17:37:32 -07:00
|
|
|
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);
|
2021-05-27 15:10:15 -07:00
|
|
|
@@ -868,6 +902,14 @@ HRESULT _build_action_map(LPDIRECTINPUTDEVICE8W iface, LPDIACTIONFORMATW lpdiaf,
|
2019-05-02 17:37:32 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
+ /* 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);
|
2021-05-27 15:10:15 -07:00
|
|
|
@@ -883,6 +925,7 @@ HRESULT _set_action_map(LPDIRECTINPUTDEVICE8W iface, LPDIACTIONFORMATW lpdiaf, L
|
2019-05-02 17:37:32 -07:00
|
|
|
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;
|
2021-03-10 17:40:52 -08:00
|
|
|
ActionMap *action_map;
|
2021-05-27 15:10:15 -07:00
|
|
|
@@ -894,12 +937,23 @@ HRESULT _set_action_map(LPDIRECTINPUTDEVICE8W iface, LPDIACTIONFORMATW lpdiaf, L
|
2019-05-02 17:37:32 -07:00
|
|
|
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;
|
|
|
|
|
2021-03-10 17:40:52 -08:00
|
|
|
/* Construct the dataformat and actionmap */
|
|
|
|
obj_df = HeapAlloc(GetProcessHeap(), 0, sizeof(DIOBJECTDATAFORMAT)*num_actions);
|
2019-05-02 17:37:32 -07:00
|
|
|
diff --git a/dlls/dinput/joystick.c b/dlls/dinput/joystick.c
|
2021-05-27 15:10:15 -07:00
|
|
|
index 0cddfbc634b..93211ea13a6 100644
|
2019-05-02 17:37:32 -07:00
|
|
|
--- a/dlls/dinput/joystick.c
|
|
|
|
+++ b/dlls/dinput/joystick.c
|
2021-05-27 15:10:15 -07:00
|
|
|
@@ -749,7 +749,7 @@ HRESULT WINAPI JoystickWGenericImpl_BuildActionMap(LPDIRECTINPUTDEVICE8W iface,
|
2019-10-10 19:30:46 -07:00
|
|
|
else
|
2019-10-10 20:12:48 -07:00
|
|
|
lstrcpynW(username, lpszUserName, size);
|
2019-10-10 19:30:46 -07:00
|
|
|
|
|
|
|
- load_success = load_mapping_settings((IDirectInputDeviceImpl *) This, lpdiaf, username);
|
|
|
|
+ load_success = load_mapping_settings(&This->base, lpdiaf, username);
|
|
|
|
heap_free(username);
|
|
|
|
}
|
|
|
|
|
2019-05-02 17:37:32 -07:00
|
|
|
diff --git a/dlls/dinput8/tests/device.c b/dlls/dinput8/tests/device.c
|
2021-05-27 15:10:15 -07:00
|
|
|
index 17deed193dd..3bfb34eb2ca 100644
|
2019-05-02 17:37:32 -07:00
|
|
|
--- 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,
|
2021-03-10 17:40:52 -08:00
|
|
|
@@ -429,6 +431,17 @@ static void test_action_mapping(void)
|
2019-05-02 17:37:32 -07:00
|
|
|
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);
|
|
|
|
|
2021-03-10 17:40:52 -08:00
|
|
|
@@ -620,6 +633,43 @@ static void test_save_settings(void)
|
2019-05-02 17:37:32 -07:00
|
|
|
"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);
|
|
|
|
}
|
|
|
|
--
|
2021-05-27 15:10:15 -07:00
|
|
|
2.30.2
|
2019-05-02 17:37:32 -07:00
|
|
|
|