diff --git a/dlls/dinput/device.c b/dlls/dinput/device.c index bf5fd1b014..098efb7b3b 100644 --- a/dlls/dinput/device.c +++ b/dlls/dinput/device.c @@ -598,10 +598,142 @@ static DWORD semantic_to_obj_id(IDirectInputDeviceImpl* This, DWORD dwSemantic) return type | (0x0000ff00 & (obj_instance << 8)); } +/* + * 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 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'}; + HKEY hkey; + WCHAR *keyname; + + keyname = HeapAlloc(GetProcessHeap(), 0, + sizeof(WCHAR) * (lstrlenW(subkey) + strlenW(username) + strlenW(device) + strlenW(guid))); + 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; + + HeapFree(GetProcessHeap(), 0, keyname); + + return hkey; +} + +static HRESULT save_mapping_settings(IDirectInputDevice8W *iface, LPDIACTIONFORMATW lpdiaf, LPCWSTR lpszUsername) +{ + WCHAR *guid_str = NULL; + DIDEVICEINSTANCEW didev; + HKEY hkey; + int i; + + didev.dwSize = sizeof(didev); + IDirectInputDevice8_GetDeviceInfo(iface, &didev); + + if (StringFromCLSID(&lpdiaf->guidActionMap, &guid_str) != S_OK) + return DI_SETTINGSNOTSAVED; + + hkey = get_mapping_key(didev.tszInstanceName, lpszUsername, guid_str); + + if (!hkey) + { + CoTaskMemFree(guid_str); + return DI_SETTINGSNOTSAVED; + } + + /* Write each of the actions mapped for this device. + Format is "dwSemantic"="dwObjID" and key is of type REG_DWORD + */ + for (i = 0; i < lpdiaf->dwNumActions; i++) + { + static const WCHAR format[] = {'%','x','\0'}; + WCHAR label[9]; + + if (IsEqualGUID(&didev.guidInstance, &lpdiaf->rgoAction[i].guidInstance) && + lpdiaf->rgoAction[i].dwHow != DIAH_UNMAPPED) + { + sprintfW(label, format, lpdiaf->rgoAction[i].dwSemantic); + RegSetValueExW(hkey, label, 0, REG_DWORD, (const BYTE*) &lpdiaf->rgoAction[i].dwObjID, sizeof(DWORD)); + } + } + + RegCloseKey(hkey); + CoTaskMemFree(guid_str); + + return DI_OK; +} + +static BOOL load_mapping_settings(IDirectInputDeviceImpl *This, LPDIACTIONFORMATW lpdiaf, const WCHAR *username) +{ + HKEY hkey; + WCHAR *guid_str; + DIDEVICEINSTANCEW didev; + int i; + + didev.dwSize = sizeof(didev); + IDirectInputDevice8_GetDeviceInfo(&This->IDirectInputDevice8W_iface, &didev); + + if (StringFromCLSID(&lpdiaf->guidActionMap, &guid_str) != S_OK) + return FALSE; + + hkey = get_mapping_key(didev.tszInstanceName, username, guid_str); + + if (!hkey) + { + CoTaskMemFree(guid_str); + return FALSE; + } + + /* Try to read each action in the DIACTIONFORMAT from registry */ + for (i = 0; i < lpdiaf->dwNumActions; i++) + { + static const WCHAR format[] = {'%','x','\0'}; + DWORD id, size = sizeof(DWORD); + WCHAR label[9]; + + sprintfW(label, format, lpdiaf->rgoAction[i].dwSemantic); + + if (!RegQueryValueExW(hkey, label, 0, NULL, (LPBYTE) &id, &size)) + { + lpdiaf->rgoAction[i].dwObjID = id; + lpdiaf->rgoAction[i].guidInstance = didev.guidInstance; + lpdiaf->rgoAction[i].dwHow = DIAH_USERCONFIG; + } + } + + RegCloseKey(hkey); + CoTaskMemFree(guid_str); + + return TRUE; +} + HRESULT _build_action_map(LPDIRECTINPUTDEVICE8W iface, LPDIACTIONFORMATW lpdiaf, LPCWSTR lpszUserName, DWORD dwFlags, DWORD devMask, LPCDIDATAFORMAT df) { IDirectInputDeviceImpl *This = impl_from_IDirectInputDevice8W(iface); + WCHAR username[MAX_PATH]; + DWORD username_size = MAX_PATH; int i, has_actions = 0; + BOOL load_success = FALSE; + + /* 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(This, lpdiaf, username); + } + + if (load_success) return DI_OK; for (i=0; i < lpdiaf->dwNumActions; i++) { @@ -650,6 +782,8 @@ HRESULT _set_action_map(LPDIRECTINPUTDEVICE8W iface, LPDIACTIONFORMATW lpdiaf, L DIOBJECTDATAFORMAT *obj_df = NULL; DIPROPDWORD dp; DIPROPRANGE dpr; + WCHAR username[MAX_PATH]; + DWORD username_size = MAX_PATH; int i, action = 0, num_actions = 0; unsigned int offset = 0; @@ -722,6 +856,15 @@ HRESULT _set_action_map(LPDIRECTINPUTDEVICE8W iface, LPDIACTIONFORMATW lpdiaf, L IDirectInputDevice8_SetProperty(iface, DIPROP_BUFFERSIZE, &dp.diph); } + /* Retrieve logged user name if necessary */ + if (lpszUserName == NULL) + GetUserNameW(username, &username_size); + else + lstrcpynW(username, lpszUserName, MAX_PATH); + + /* Save the settings to disk */ + save_mapping_settings(iface, lpdiaf, username); + return IDirectInputDevice8WImpl_SetActionMap(iface, lpdiaf, lpszUserName, dwFlags); } diff --git a/dlls/dinput/dinput_private.h b/dlls/dinput/dinput_private.h index 8efb42ed0b..e593b40caa 100644 --- a/dlls/dinput/dinput_private.h +++ b/dlls/dinput/dinput_private.h @@ -70,6 +70,8 @@ extern void _copy_diactionformatWtoA(LPDIACTIONFORMATA, LPDIACTIONFORMATW) DECLS extern HRESULT _configure_devices(IDirectInput8W *iface, LPDICONFIGUREDEVICESCALLBACK lpdiCallback, LPDICONFIGUREDEVICESPARAMSW lpdiCDParams, DWORD dwFlags, LPVOID pvRefData) DECLSPEC_HIDDEN; +extern WCHAR* get_mapping_path(const WCHAR *device, const WCHAR *username) DECLSPEC_HIDDEN; + #define IS_DIPROP(x) (((ULONG_PTR)(x) >> 16) == 0) #define DIKEYBOARD_MASK 0x81000000 diff --git a/dlls/dinput8/tests/device.c b/dlls/dinput8/tests/device.c index e5b3f6a942..76f9120b8c 100644 --- a/dlls/dinput8/tests/device.c +++ b/dlls/dinput8/tests/device.c @@ -458,13 +458,13 @@ static void test_save_settings(void) hr = IDirectInputDevice8_BuildActionMap(pKey, &af, NULL, 0); ok (SUCCEEDED(hr), "BuildActionMap failed hr=%08x\n", hr); - todo_wine ok (other_results[0] == af.rgoAction[0].dwObjID, + ok (other_results[0] == af.rgoAction[0].dwObjID, "Mapped incorrectly expected: 0x%08x got: 0x%08x\n", other_results[0], af.rgoAction[0].dwObjID); - todo_wine ok (IsEqualGUID(&GUID_SysKeyboard, &af.rgoAction[0].guidInstance), "Action should be mapped to keyboard\n"); + ok (IsEqualGUID(&GUID_SysKeyboard, &af.rgoAction[0].guidInstance), "Action should be mapped to keyboard\n"); - todo_wine ok (other_results[1] == af.rgoAction[1].dwObjID, + ok (other_results[1] == af.rgoAction[1].dwObjID, "Mapped incorrectly expected: 0x%08x got: 0x%08x\n", other_results[1], af.rgoAction[1].dwObjID); - todo_wine ok (IsEqualGUID(&GUID_SysKeyboard, &af.rgoAction[1].guidInstance), "Action should be mapped to keyboard\n"); + ok (IsEqualGUID(&GUID_SysKeyboard, &af.rgoAction[1].guidInstance), "Action should be mapped to keyboard\n"); } START_TEST(device)