diff --git a/patches/dinput-joy-mappings/0001-dinput-Load-users-Joystick-mappings.patch b/patches/dinput-joy-mappings/0001-dinput-Load-users-Joystick-mappings.patch new file mode 100644 index 00000000..dfe75e96 --- /dev/null +++ b/patches/dinput-joy-mappings/0001-dinput-Load-users-Joystick-mappings.patch @@ -0,0 +1,79 @@ +From d5ea6db794ad1c943b4f8b03a3a5e0424a210ed3 Mon Sep 17 00:00:00 2001 +From: Jetro Jormalainen +Date: Tue, 30 Apr 2019 09:20:20 +1000 +Subject: [PATCH 1/3] dinput: Load users Joystick mappings. + +--- + dlls/dinput/device.c | 2 +- + dlls/dinput/device_private.h | 2 ++ + dlls/dinput/joystick.c | 18 ++++++++++++++++++ + 3 files changed, 21 insertions(+), 1 deletion(-) + +diff --git a/dlls/dinput/device.c b/dlls/dinput/device.c +index 6c44616..2150db7 100644 +--- a/dlls/dinput/device.c ++++ b/dlls/dinput/device.c +@@ -714,7 +714,7 @@ static HRESULT save_mapping_settings(IDirectInputDevice8W *iface, LPDIACTIONFORM + return DI_OK; + } + +-static BOOL load_mapping_settings(IDirectInputDeviceImpl *This, LPDIACTIONFORMATW lpdiaf, const WCHAR *username) ++BOOL load_mapping_settings(IDirectInputDeviceImpl *This, LPDIACTIONFORMATW lpdiaf, const WCHAR *username) + { + HKEY hkey; + WCHAR *guid_str; +diff --git a/dlls/dinput/device_private.h b/dlls/dinput/device_private.h +index d9e2997..af8d99d 100644 +--- a/dlls/dinput/device_private.h ++++ b/dlls/dinput/device_private.h +@@ -123,6 +123,8 @@ extern const char *_dump_dinput_GUID(const GUID *guid) DECLSPEC_HIDDEN; + + extern LPDIOBJECTDATAFORMAT dataformat_to_odf_by_type(LPCDIDATAFORMAT df, int n, DWORD type) DECLSPEC_HIDDEN; + ++extern BOOL load_mapping_settings(IDirectInputDeviceImpl *This, LPDIACTIONFORMATW lpdiaf, const WCHAR *username) DECLSPEC_HIDDEN; ++ + extern HRESULT _build_action_map(LPDIRECTINPUTDEVICE8W iface, LPDIACTIONFORMATW lpdiaf, LPCWSTR lpszUserName, DWORD dwFlags, DWORD devMask, LPCDIDATAFORMAT df) DECLSPEC_HIDDEN; + extern HRESULT _set_action_map(LPDIRECTINPUTDEVICE8W iface, LPDIACTIONFORMATW lpdiaf, LPCWSTR lpszUserName, DWORD dwFlags, LPCDIDATAFORMAT df) DECLSPEC_HIDDEN; + +diff --git a/dlls/dinput/joystick.c b/dlls/dinput/joystick.c +index 4809831..b146712 100644 +--- a/dlls/dinput/joystick.c ++++ b/dlls/dinput/joystick.c +@@ -28,6 +28,7 @@ + + #include + ++#include "device_private.h" + #include "joystick_private.h" + #include "wine/debug.h" + #include "winreg.h" +@@ -782,9 +783,26 @@ HRESULT WINAPI JoystickWGenericImpl_BuildActionMap(LPDIRECTINPUTDEVICE8W iface, + JoystickGenericImpl *This = impl_from_IDirectInputDevice8W(iface); + unsigned int i, j; + BOOL has_actions = FALSE; ++ WCHAR username[MAX_PATH]; ++ DWORD username_size = MAX_PATH; ++ BOOL load_success = FALSE; + + FIXME("(%p)->(%p,%s,%08x): semi-stub !\n", iface, lpdiaf, debugstr_w(lpszUserName), dwFlags); + ++ /* Unless asked the contrary by these flags, try to load a previous mapping */ ++ if (!(dwFlags & DIDBAM_HWDEFAULTS)) ++ { ++ /* Retrieve logged user name if necessary */ ++ if (lpszUserName == NULL) ++ GetUserNameW(username, &username_size); ++ else ++ lstrcpynW(username, lpszUserName, MAX_PATH); ++ ++ load_success = load_mapping_settings((IDirectInputDeviceImpl *) This, lpdiaf, username); ++ } ++ ++ if (load_success) return DI_OK; ++ + for (i=0; i < lpdiaf->dwNumActions; i++) + { + DWORD inst = (0x000000ff & (lpdiaf->rgoAction[i].dwSemantic)) - 1; +-- +1.9.1 + diff --git a/patches/dinput-joy-mappings/0002-dinput-Allow-empty-Joystick-mappings.patch b/patches/dinput-joy-mappings/0002-dinput-Allow-empty-Joystick-mappings.patch new file mode 100644 index 00000000..e2ea1f2a --- /dev/null +++ b/patches/dinput-joy-mappings/0002-dinput-Allow-empty-Joystick-mappings.patch @@ -0,0 +1,305 @@ +From 920014b62ddd2218477e3d82f45c4515742f9e28 Mon Sep 17 00:00:00 2001 +From: Jetro Jormalainen +Date: Tue, 30 Apr 2019 09:20:54 +1000 +Subject: [PATCH 2/3] dinput: Allow empty Joystick mappings. + +--- + dlls/dinput/device.c | 82 +++++++++++++++++++++++++++++++++++++-------- + dlls/dinput/joystick.c | 2 ++ + dlls/dinput/keyboard.c | 2 ++ + dlls/dinput/mouse.c | 2 ++ + dlls/dinput8/tests/device.c | 50 +++++++++++++++++++++++++++ + 5 files changed, 124 insertions(+), 14 deletions(-) + +diff --git a/dlls/dinput/device.c b/dlls/dinput/device.c +index 2150db7..a7cfe36 100644 +--- a/dlls/dinput/device.c ++++ b/dlls/dinput/device.c +@@ -643,12 +643,30 @@ 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 = HeapAlloc(GetProcessHeap(), 0, ++ 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); ++ ++ HeapFree(GetProcessHeap(), 0, 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 b146712..267f932 100644 +--- a/dlls/dinput/joystick.c ++++ b/dlls/dinput/joystick.c +@@ -907,6 +907,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 42c0759..5c5aa14 100644 +--- a/dlls/dinput/keyboard.c ++++ b/dlls/dinput/keyboard.c +@@ -693,6 +693,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 f3ec8e4..0adba03 100644 +--- a/dlls/dinput/mouse.c ++++ b/dlls/dinput/mouse.c +@@ -863,6 +863,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 1bfb34a..46a1e4a 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); + } +-- +1.9.1 + diff --git a/patches/dinput-joy-mappings/0003-dinput-Support-username-in-Config-dialog.patch b/patches/dinput-joy-mappings/0003-dinput-Support-username-in-Config-dialog.patch new file mode 100644 index 00000000..c78fb5b8 --- /dev/null +++ b/patches/dinput-joy-mappings/0003-dinput-Support-username-in-Config-dialog.patch @@ -0,0 +1,428 @@ +From 9b284de24a4bb4972f7f165cdc41281d35dab492 Mon Sep 17 00:00:00 2001 +From: Jetro Jormalainen +Date: Tue, 30 Apr 2019 09:21:24 +1000 +Subject: [PATCH 3/3] dinput: Support username in Config dialog. + +--- + dlls/dinput/config.c | 183 ++++++++++++++++++++++++++++++------------- + dlls/dinput/device.c | 2 +- + dlls/dinput/device_private.h | 1 + + dlls/dinput/dinput_main.c | 27 +++++++ + 4 files changed, 156 insertions(+), 57 deletions(-) + +diff --git a/dlls/dinput/config.c b/dlls/dinput/config.c +index db5878b..f42a44a 100644 +--- a/dlls/dinput/config.c ++++ b/dlls/dinput/config.c +@@ -29,6 +29,9 @@ typedef struct { + IDirectInputDevice8W *lpdid; + DIDEVICEINSTANCEW ddi; + DIDEVICEOBJECTINSTANCEW ddo[256]; ++ /* ActionFormat for every user. ++ * In same order as ConfigureDevicesData usernames */ ++ DIACTIONFORMATW *user_afs; + } DeviceData; + + typedef struct { +@@ -38,10 +41,11 @@ typedef struct { + + typedef struct { + IDirectInput8W *lpDI; +- LPDIACTIONFORMATW lpdiaf; + LPDIACTIONFORMATW original_lpdiaf; + DIDevicesData devices_data; + int display_only; ++ int nusernames; ++ WCHAR **usernames; + } ConfigureDevicesData; + + /* +@@ -57,27 +61,42 @@ static BOOL CALLBACK collect_objects(LPCDIDEVICEOBJECTINSTANCEW lpddo, LPVOID pv + return DIENUM_CONTINUE; + } + +-static BOOL CALLBACK count_devices(LPCDIDEVICEINSTANCEW lpddi, IDirectInputDevice8W *lpdid, DWORD dwFlags, DWORD dwRemaining, LPVOID pvRef) ++static BOOL CALLBACK collect_devices(LPCDIDEVICEINSTANCEW lpddi, IDirectInputDevice8W *lpdid, DWORD dwFlags, DWORD dwRemaining, LPVOID pvRef) + { +- DIDevicesData *data = (DIDevicesData*) pvRef; ++ ConfigureDevicesData *data = (ConfigureDevicesData*) pvRef; ++ DeviceData *device; ++ int i, j; + +- data->ndevices++; +- return DIENUM_CONTINUE; +-} ++ IDirectInputDevice_AddRef(lpdid); + +-static BOOL CALLBACK collect_devices(LPCDIDEVICEINSTANCEW lpddi, IDirectInputDevice8W *lpdid, DWORD dwFlags, DWORD dwRemaining, LPVOID pvRef) +-{ +- DIDevicesData *data = (DIDevicesData*) pvRef; +- DeviceData *device = &data->devices[data->ndevices]; ++ /* alloc array for devices if this is our first device */ ++ if (!data->devices_data.ndevices) ++ data->devices_data.devices = HeapAlloc(GetProcessHeap(), 0, sizeof(DeviceData) * (dwRemaining + 1)); ++ device = &data->devices_data.devices[data->devices_data.ndevices]; + device->lpdid = lpdid; + device->ddi = *lpddi; + +- IDirectInputDevice_AddRef(lpdid); +- + device->nobjects = 0; + IDirectInputDevice_EnumObjects(lpdid, collect_objects, (LPVOID) device, DIDFT_ALL); + +- data->ndevices++; ++ device->user_afs = HeapAlloc(GetProcessHeap(), 0, sizeof(*device->user_afs)*data->nusernames); ++ memset(device->user_afs, 0, sizeof(*device->user_afs)*data->nusernames); ++ for (i = 0; i < data->nusernames; i++) ++ { ++ DIACTIONFORMATW *user_af = &device->user_afs[i]; ++ user_af->dwNumActions = data->original_lpdiaf->dwNumActions; ++ user_af->guidActionMap = data->original_lpdiaf->guidActionMap; ++ user_af->rgoAction = HeapAlloc(GetProcessHeap(), 0, sizeof(DIACTIONW)*data->original_lpdiaf->dwNumActions); ++ memset(user_af->rgoAction, 0, sizeof(DIACTIONW)*data->original_lpdiaf->dwNumActions); ++ for (j = 0; j < user_af->dwNumActions; j++) ++ { ++ user_af->rgoAction[j].dwSemantic = data->original_lpdiaf->rgoAction[j].dwSemantic; ++ user_af->rgoAction[j].u.lptszActionName = data->original_lpdiaf->rgoAction[j].u.lptszActionName; ++ } ++ IDirectInputDevice8_BuildActionMap(lpdid, user_af, data->usernames[i], 0); ++ } ++ ++ data->devices_data.ndevices++; + return DIENUM_CONTINUE; + } + +@@ -170,10 +189,18 @@ static DeviceData* get_cur_device(HWND dialog) + return &data->devices_data.devices[sel]; + } + +-static LPDIACTIONFORMATW get_cur_lpdiaf(HWND dialog) ++static DIACTIONFORMATW *get_cur_lpdiaf(HWND dialog) ++{ ++ ConfigureDevicesData *data = (ConfigureDevicesData*) GetWindowLongPtrW(dialog, DWLP_USER); ++ int controller_sel = SendDlgItemMessageW(dialog, IDC_CONTROLLERCOMBO, CB_GETCURSEL, 0, 0); ++ int player_sel = SendDlgItemMessageW(dialog, IDC_PLAYERCOMBO, CB_GETCURSEL, 0, 0); ++ return &data->devices_data.devices[controller_sel].user_afs[player_sel]; ++} ++ ++static DIACTIONFORMATW *get_original_lpdiaf(HWND dialog) + { + ConfigureDevicesData *data = (ConfigureDevicesData*) GetWindowLongPtrW(dialog, DWLP_USER); +- return data->lpdiaf; ++ return data->original_lpdiaf; + } + + static int dialog_display_only(HWND dialog) +@@ -182,40 +209,36 @@ static int dialog_display_only(HWND dialog) + return data->display_only; + } + +-static void init_devices(HWND dialog, IDirectInput8W *lpDI, DIDevicesData *data, LPDIACTIONFORMATW lpdiaf) ++static void init_devices(HWND dialog, ConfigureDevicesData *data) + { + int i; + +- /* Count devices */ +- data->ndevices = 0; +- IDirectInput8_EnumDevicesBySemantics(lpDI, NULL, lpdiaf, count_devices, (LPVOID) data, 0); +- +- /* Allocate devices */ +- data->devices = HeapAlloc(GetProcessHeap(), 0, sizeof(DeviceData) * data->ndevices); +- + /* Collect and insert */ +- data->ndevices = 0; +- IDirectInput8_EnumDevicesBySemantics(lpDI, NULL, lpdiaf, collect_devices, (LPVOID) data, 0); ++ data->devices_data.ndevices = 0; ++ IDirectInput8_EnumDevicesBySemantics(data->lpDI, NULL, data->original_lpdiaf, collect_devices, (LPVOID) data, 0); + +- for (i=0; i < data->ndevices; i++) +- SendDlgItemMessageW(dialog, IDC_CONTROLLERCOMBO, CB_ADDSTRING, 0, (LPARAM) data->devices[i].ddi.tszProductName ); ++ for (i = 0; i < data->devices_data.ndevices; i++) ++ SendDlgItemMessageW(dialog, IDC_CONTROLLERCOMBO, CB_ADDSTRING, 0, (LPARAM) data->devices_data.devices[i].ddi.tszProductName ); ++ for (i = 0; i < data->nusernames; i++) ++ SendDlgItemMessageW(dialog, IDC_PLAYERCOMBO, CB_ADDSTRING, 0, (LPARAM) data->usernames[i]); + } + + static void destroy_data(HWND dialog) + { +- int i; ++ int i, j; + ConfigureDevicesData *data = (ConfigureDevicesData*) GetWindowLongPtrW(dialog, DWLP_USER); + DIDevicesData *devices_data = &data->devices_data; + + /* Free the devices */ + for (i=0; i < devices_data->ndevices; i++) ++ { + IDirectInputDevice8_Release(devices_data->devices[i].lpdid); ++ for (j=0; j < data->nusernames; j++) ++ HeapFree(GetProcessHeap(), 0, devices_data->devices[i].user_afs[j].rgoAction); ++ HeapFree(GetProcessHeap(), 0, devices_data->devices[i].user_afs); ++ } + + HeapFree(GetProcessHeap(), 0, devices_data->devices); +- +- /* Free the backup LPDIACTIONFORMATW */ +- HeapFree(GetProcessHeap(), 0, data->original_lpdiaf->rgoAction); +- HeapFree(GetProcessHeap(), 0, data->original_lpdiaf); + } + + static void fill_device_object_list(HWND dialog) +@@ -231,6 +254,7 @@ static void fill_device_object_list(HWND dialog) + /* Add each object */ + for (i=0; i < device->nobjects; i++) + { ++ DWORD ddo_inst, ddo_type; + int action = -1; + + item.mask = LVIF_TEXT | LVIF_PARAM; +@@ -241,12 +265,20 @@ static void fill_device_object_list(HWND dialog) + + /* Add the item */ + SendDlgItemMessageW(dialog, IDC_DEVICEOBJECTSLIST, LVM_INSERTITEMW, 0, (LPARAM) &item); ++ ddo_inst = DIDFT_GETINSTANCE(device->ddo[i].dwType); ++ ddo_type = DIDFT_GETTYPE(device->ddo[i].dwType); + +- /* Search for an assigned action for this device */ ++ /* Search for an assigned action for this device */ + for (j=0; j < lpdiaf->dwNumActions; j++) + { ++ DWORD af_inst = DIDFT_GETINSTANCE(lpdiaf->rgoAction[j].dwObjID); ++ DWORD af_type = DIDFT_GETTYPE(lpdiaf->rgoAction[j].dwObjID); ++ if (af_type == DIDFT_PSHBUTTON) af_type = DIDFT_BUTTON; ++ if (af_type == DIDFT_RELAXIS) af_type = DIDFT_AXIS; ++ /* NOTE previously compared dwType == dwObjId but default buildActionMap actions ++ * were PSHBUTTON and RELAXS and didnt show up on config */ + if (IsEqualGUID(&lpdiaf->rgoAction[j].guidInstance, &device->ddi.guidInstance) && +- lpdiaf->rgoAction[j].dwObjID == device->ddo[i].dwType) ++ ddo_inst == af_inst && ddo_type & af_type) + { + action = j; + break; +@@ -260,7 +292,7 @@ static void fill_device_object_list(HWND dialog) + static void show_suitable_actions(HWND dialog) + { + DeviceData *device = get_cur_device(dialog); +- LPDIACTIONFORMATW lpdiaf = get_cur_lpdiaf(dialog); ++ LPDIACTIONFORMATW lpdiaf = get_original_lpdiaf(dialog); + int i, added = 0; + int obj = lv_get_cur_item(dialog); + +@@ -329,24 +361,35 @@ static void assign_action(HWND dialog) + lv_set_action(dialog, obj, action, lpdiaf); + } + +-static void copy_actions(LPDIACTIONFORMATW to, LPDIACTIONFORMATW from) ++static void reset_actions(HWND dialog) + { +- DWORD i; +- for (i=0; i < from->dwNumActions; i++) ++ ConfigureDevicesData *data = (ConfigureDevicesData*) GetWindowLongPtrW(dialog, DWLP_USER); ++ DIDevicesData *ddata = (DIDevicesData*) &data->devices_data; ++ unsigned i, j; ++ ++ for (i = 0; i < data->devices_data.ndevices; i++) + { +- to->rgoAction[i].guidInstance = from->rgoAction[i].guidInstance; +- to->rgoAction[i].dwObjID = from->rgoAction[i].dwObjID; +- to->rgoAction[i].dwHow = from->rgoAction[i].dwHow; +- to->rgoAction[i].u.lptszActionName = from->rgoAction[i].u.lptszActionName; ++ DeviceData *device = &ddata->devices[i]; ++ for (j = 0; j < data->nusernames; j++) ++ IDirectInputDevice8_BuildActionMap(device->lpdid, &device->user_afs[j], data->usernames[j], DIDBAM_HWDEFAULTS); + } + } + +-static void reset_actions(HWND dialog) +-{ ++static void save_actions(HWND dialog) { + ConfigureDevicesData *data = (ConfigureDevicesData*) GetWindowLongPtrW(dialog, DWLP_USER); +- LPDIACTIONFORMATW to = data->lpdiaf, from = data->original_lpdiaf; +- +- copy_actions(to, from); ++ DIDevicesData *ddata = (DIDevicesData*) &data->devices_data; ++ unsigned i, j; ++ if (!data->display_only) { ++ for (i = 0; i < ddata->ndevices; i++) ++ { ++ DeviceData *device = &ddata->devices[i]; ++ for (j = 0; j < data->nusernames; j++) ++ { ++ if (save_mapping_settings(device->lpdid, &device->user_afs[j], data->usernames[j]) != DI_OK) ++ MessageBoxA(dialog, "Could not save settings", 0, MB_ICONERROR); ++ } ++ } ++ } + } + + static INT_PTR CALLBACK ConfigureDevicesDlgProc(HWND dialog, UINT uMsg, WPARAM wParam, LPARAM lParam) +@@ -358,21 +401,16 @@ static INT_PTR CALLBACK ConfigureDevicesDlgProc(HWND dialog, UINT uMsg, WPARAM w + ConfigureDevicesData *data = (ConfigureDevicesData*) lParam; + + /* Initialize action format and enumerate devices */ +- init_devices(dialog, data->lpDI, &data->devices_data, data->lpdiaf); ++ init_devices(dialog, data); + + /* Store information in the window */ + SetWindowLongPtrW(dialog, DWLP_USER, (LONG_PTR) data); + + init_listview_columns(dialog); + +- /* Create a backup action format for CANCEL and RESET operations */ +- data->original_lpdiaf = HeapAlloc(GetProcessHeap(), 0, sizeof(*data->original_lpdiaf)); +- data->original_lpdiaf->dwNumActions = data->lpdiaf->dwNumActions; +- data->original_lpdiaf->rgoAction = HeapAlloc(GetProcessHeap(), 0, sizeof(DIACTIONW)*data->lpdiaf->dwNumActions); +- copy_actions(data->original_lpdiaf, data->lpdiaf); +- + /* Select the first device and show its actions */ + SendDlgItemMessageW(dialog, IDC_CONTROLLERCOMBO, CB_SETCURSEL, 0, 0); ++ SendDlgItemMessageW(dialog, IDC_PLAYERCOMBO, CB_SETCURSEL, 0, 0); + fill_device_object_list(dialog); + + break; +@@ -408,6 +446,7 @@ static INT_PTR CALLBACK ConfigureDevicesDlgProc(HWND dialog, UINT uMsg, WPARAM w + break; + + case IDC_CONTROLLERCOMBO: ++ case IDC_PLAYERCOMBO: + + switch (HIWORD(wParam)) + { +@@ -418,12 +457,12 @@ static INT_PTR CALLBACK ConfigureDevicesDlgProc(HWND dialog, UINT uMsg, WPARAM w + break; + + case IDOK: ++ save_actions(dialog); + EndDialog(dialog, 0); + destroy_data(dialog); + break; + + case IDCANCEL: +- reset_actions(dialog); + EndDialog(dialog, 0); + destroy_data(dialog); + break; +@@ -446,15 +485,47 @@ HRESULT _configure_devices(IDirectInput8W *iface, + LPVOID pvRefData + ) + { ++ int i; ++ DWORD default_username_size = MAX_PATH; ++ WCHAR *default_username = 0; + ConfigureDevicesData data; + data.lpDI = iface; +- data.lpdiaf = lpdiCDParams->lprgFormats; ++ data.original_lpdiaf = lpdiCDParams->lprgFormats; + data.display_only = !(dwFlags & DICD_EDIT); ++ data.nusernames = lpdiCDParams->dwcUsers; ++ if (lpdiCDParams->lptszUserNames == NULL) ++ { ++ /* Get default user name */ ++ default_username = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR)*MAX_PATH); ++ data.nusernames = 1; ++ data.usernames = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR *)); ++ data.usernames[0] = default_username; ++ GetUserNameW(default_username, &default_username_size); ++ } ++ else ++ { ++ WCHAR *p = lpdiCDParams->lptszUserNames; ++ data.usernames = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR *)*data.nusernames); ++ for (i = 0; i < data.nusernames; i++) ++ { ++ if (*p) ++ { ++ data.usernames[i] = p; ++ while (*(p++)); ++ } ++ else ++ /* Return if there is an empty string */ ++ return DIERR_INVALIDPARAM; ++ } ++ } + + InitCommonControls(); + + DialogBoxParamW(DINPUT_instance, (const WCHAR *)MAKEINTRESOURCE(IDD_CONFIGUREDEVICES), + lpdiCDParams->hwnd, ConfigureDevicesDlgProc, (LPARAM)&data); + ++ HeapFree(GetProcessHeap(), 0, default_username); ++ HeapFree(GetProcessHeap(), 0, data.usernames); ++ + return DI_OK; + } +diff --git a/dlls/dinput/device.c b/dlls/dinput/device.c +index a7cfe36..63eb868 100644 +--- a/dlls/dinput/device.c ++++ b/dlls/dinput/device.c +@@ -692,7 +692,7 @@ static HKEY get_mapping_key(const WCHAR *device, const WCHAR *username, const WC + return hkey; + } + +-static HRESULT save_mapping_settings(IDirectInputDevice8W *iface, LPDIACTIONFORMATW lpdiaf, LPCWSTR lpszUsername) ++HRESULT save_mapping_settings(IDirectInputDevice8W *iface, LPDIACTIONFORMATW lpdiaf, LPCWSTR lpszUsername) + { + WCHAR *guid_str = NULL; + DIDEVICEINSTANCEW didev; +diff --git a/dlls/dinput/device_private.h b/dlls/dinput/device_private.h +index af8d99d..36b71f7 100644 +--- a/dlls/dinput/device_private.h ++++ b/dlls/dinput/device_private.h +@@ -123,6 +123,7 @@ extern const char *_dump_dinput_GUID(const GUID *guid) DECLSPEC_HIDDEN; + + extern LPDIOBJECTDATAFORMAT dataformat_to_odf_by_type(LPCDIDATAFORMAT df, int n, DWORD type) DECLSPEC_HIDDEN; + ++extern HRESULT save_mapping_settings(IDirectInputDevice8W *iface, LPDIACTIONFORMATW lpdiaf, LPCWSTR lpszUsername) DECLSPEC_HIDDEN; + extern BOOL load_mapping_settings(IDirectInputDeviceImpl *This, LPDIACTIONFORMATW lpdiaf, const WCHAR *username) DECLSPEC_HIDDEN; + + extern HRESULT _build_action_map(LPDIRECTINPUTDEVICE8W iface, LPDIACTIONFORMATW lpdiaf, LPCWSTR lpszUserName, DWORD dwFlags, DWORD devMask, LPCDIDATAFORMAT df) DECLSPEC_HIDDEN; +diff --git a/dlls/dinput/dinput_main.c b/dlls/dinput/dinput_main.c +index 1b2020c..9e837bd 100644 +--- a/dlls/dinput/dinput_main.c ++++ b/dlls/dinput/dinput_main.c +@@ -1264,9 +1264,34 @@ static HRESULT WINAPI IDirectInput8AImpl_ConfigureDevices( + + /* Copy parameters */ + diCDParamsW.dwSize = sizeof(DICONFIGUREDEVICESPARAMSW); ++ diCDParamsW.dwcUsers = lpdiCDParams->dwcUsers; + diCDParamsW.dwcFormats = lpdiCDParams->dwcFormats; + diCDParamsW.lprgFormats = &diafW; + diCDParamsW.hwnd = lpdiCDParams->hwnd; ++ diCDParamsW.lptszUserNames = NULL; ++ ++ if (lpdiCDParams->lptszUserNames) { ++ char *start = lpdiCDParams->lptszUserNames; ++ WCHAR *to = NULL; ++ int total_len = 0; ++ for (i = 0; i < lpdiCDParams->dwcUsers; i++) ++ { ++ char *end = start + 1; ++ int len; ++ while (*(end++)); ++ len = MultiByteToWideChar(CP_ACP, 0, start, end - start, NULL, 0); ++ total_len += len + 2; /* length of string and two null char */ ++ if (to) ++ to = HeapReAlloc(GetProcessHeap(), 0, to, sizeof(WCHAR) * total_len); ++ else ++ to = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR) * total_len); ++ ++ MultiByteToWideChar(CP_ACP, 0, start, end - start, to + (total_len - len - 2), len); ++ to[total_len] = 0; ++ to[total_len - 1] = 0; ++ } ++ diCDParamsW.lptszUserNames = to; ++ } + + diafW.rgoAction = HeapAlloc(GetProcessHeap(), 0, sizeof(DIACTIONW)*lpdiCDParams->lprgFormats->dwNumActions); + _copy_diactionformatAtoW(&diafW, lpdiCDParams->lprgFormats); +@@ -1294,6 +1319,8 @@ static HRESULT WINAPI IDirectInput8AImpl_ConfigureDevices( + + HeapFree(GetProcessHeap(), 0, diafW.rgoAction); + ++ HeapFree(GetProcessHeap(), 0, (void*) diCDParamsW.lptszUserNames); ++ + return hr; + } + +-- +1.9.1 + diff --git a/patches/dinput-joy-mappings/definition b/patches/dinput-joy-mappings/definition new file mode 100644 index 00000000..cb58ebfa --- /dev/null +++ b/patches/dinput-joy-mappings/definition @@ -0,0 +1 @@ +Fixes: [34108] dinput: Improve support for user Joystick configuration. diff --git a/patches/patchinstall.sh b/patches/patchinstall.sh index bf1ab25e..20456411 100755 --- a/patches/patchinstall.sh +++ b/patches/patchinstall.sh @@ -130,6 +130,7 @@ patch_enable_all () enable_ddraw_Write_Vtable="$1" enable_ddraw_version_check="$1" enable_dinput_axis_recalc="$1" + enable_dinput_joy_mappings="$1" enable_dinput_reconnect_joystick="$1" enable_dinput_remap_joystick="$1" enable_dsound_EAX="$1" @@ -522,6 +523,9 @@ patch_enable () dinput-axis-recalc) enable_dinput_axis_recalc="$2" ;; + dinput-joy-mappings) + enable_dinput_joy_mappings="$2" + ;; dinput-reconnect-joystick) enable_dinput_reconnect_joystick="$2" ;; @@ -3075,6 +3079,26 @@ if test "$enable_dinput_axis_recalc" -eq 1; then ) >> "$patchlist" fi +# Patchset dinput-joy-mappings +# | +# | This patchset fixes the following Wine bugs: +# | * [#34108] dinput: Improve support for user Joystick configuration. +# | +# | Modified files: +# | * dlls/dinput/config.c, dlls/dinput/device.c, dlls/dinput/device_private.h, dlls/dinput/dinput_main.c, +# | dlls/dinput/joystick.c, dlls/dinput/keyboard.c, dlls/dinput/mouse.c, dlls/dinput8/tests/device.c +# | +if test "$enable_dinput_joy_mappings" -eq 1; then + patch_apply dinput-joy-mappings/0001-dinput-Load-users-Joystick-mappings.patch + patch_apply dinput-joy-mappings/0002-dinput-Allow-empty-Joystick-mappings.patch + patch_apply dinput-joy-mappings/0003-dinput-Support-username-in-Config-dialog.patch + ( + printf '%s\n' '+ { "Jetro Jormalainen", "dinput: Load users Joystick mappings.", 1 },'; + printf '%s\n' '+ { "Jetro Jormalainen", "dinput: Allow empty Joystick mappings.", 1 },'; + printf '%s\n' '+ { "Jetro Jormalainen", "dinput: Support username in Config dialog.", 1 },'; + ) >> "$patchlist" +fi + # Patchset dinput-reconnect-joystick # | # | This patchset fixes the following Wine bugs: