Hey, I've got a couple patches implementing build and setactionmap for joysticks and I'd like you guys to give them a look.
These implementations are very generic and I think I could use them for the keyboard and the mouse too. If everything's alright with these patches that'll be my next step. The mapping can be tested this small app I built: https://github.com/downloads/lfzawacki/dinput-samples/dolphin.exe provided that you have one joystick attached (it will chose the first one if there are more). Cheers :) 2011/7/5 Lucas Zawacki <lfzawa...@gmail.com>: > Hi guys. > > Now that all the initial patches are in I have several smaller tasks > to work on as listed here > http://lfzawacki.heroku.com/wine/published/Road+Map and hopefully > they're more straightforward. I've already started working on getting > EnumDevicesBySemantics correct with joysticks and the passed flags > and, after that, BuildActionMap for the joysticks will follow. Maybe > now it's time to discuss how to implement EnumDevicesBySemantics as a > crosscall, but I really don't know how to do it (or if it's worth it) > and everywhere I look in dinput I see similar cases of this > "duplication pattern". > > Another thing that I've been thinking is that I might as well end up > rolling a version of ConfigureDevices > (http://msdn.microsoft.com/en-us/library/microsoft.directx_sdk.idirectinput8.idirectinput8.configuredevices%28v=VS.85%29.aspx) > because so far I've seen two of the games affected by bug 8754 use it > to configure controls. I've not had time to find and test all of them, > but if someone on the list knows about other games that use it I'd > like to be informed. > > Last but not least, thanks for Wylda for testing NFSU and keeping bug > 8754 informed. I actually can't run the game properly, most likely > because of the crappy intel graphics card in my laptop. > > Anyway, feedback on the tasks is welcome. > > --- > lfz >
From b492c54d5ca7179e0905f63cec183d0920555e1d Mon Sep 17 00:00:00 2001 From: Lucas Fialho Zawacki <lfzawa...@gmail.com> Date: Tue, 19 Jul 2011 17:52:12 -0300 Subject: [PATCH 1/4] dinput: EnumDevicesBySemantics enumerating joysticks with priority flags Added an utility function that checks the priority of a device for a given mapping. This can be modified later to return priority 2 mappings, if necessary. --- dlls/dinput/dinput_main.c | 50 ++++++++++++++++++++++++++++++++------------ 1 files changed, 36 insertions(+), 14 deletions(-) diff --git a/dlls/dinput/dinput_main.c b/dlls/dinput/dinput_main.c index b653307..1d59cdc 100644 --- a/dlls/dinput/dinput_main.c +++ b/dlls/dinput/dinput_main.c @@ -317,6 +317,38 @@ void _copy_diactionformatWtoA(LPDIACTIONFORMATA to, LPDIACTIONFORMATW from) } } +/* _diactionformat_priorityA + * + * Given a DIACTIONFORMAT structure and a DI genre, returns the enumeration + * priority. Joysticks should pass the game genre, and mouse or keyboard their + * respective DI*_MASK + */ +static DWORD _diactionformat_priorityA(LPDIACTIONFORMATA lpdiaf, DWORD genre) +{ + int i; + DWORD priorityFlags = 0; + + /* If there's at least one action for the device it's priority 1 */ + for(i=0; i < lpdiaf->dwActionSize; i++) + if ((lpdiaf->rgoAction[i].dwSemantic & genre) == genre) + priorityFlags |= DIEDBS_MAPPEDPRI1; + + return priorityFlags; +} + +static DWORD _diactionformat_priorityW(LPDIACTIONFORMATW lpdiaf, DWORD genre) +{ + int i; + DWORD priorityFlags = 0; + + /* If there's at least one action for the device it's priority 1 */ + for(i=0; i < lpdiaf->dwActionSize; i++) + if ((lpdiaf->rgoAction[i].dwSemantic & genre) == genre) + priorityFlags |= DIEDBS_MAPPEDPRI1; + + return priorityFlags; +} + /****************************************************************************** * IDirectInputA_EnumDevices */ @@ -877,7 +909,7 @@ static HRESULT WINAPI IDirectInput8AImpl_EnumDevicesBySemantics( { TRACE(" - checking device %u ('%s')\n", i, dinput_devices[i]->name); - callbackFlags = 0; + callbackFlags = _diactionformat_priorityA(lpdiActionFormat, lpdiActionFormat->dwGenre); /* Default behavior is to enumerate attached game controllers */ enumSuccess = dinput_devices[i]->enum_deviceA(DI8DEVCLASS_GAMECTRL, DIEDFL_ATTACHEDONLY | dwFlags, &didevi, This->dwVersion, j); if (enumSuccess) @@ -895,16 +927,11 @@ static HRESULT WINAPI IDirectInput8AImpl_EnumDevicesBySemantics( /* Enumerate keyboard and mouse */ for(i=0; i < sizeof(guids)/sizeof(guids[0]); i++) { - callbackFlags = 0; + callbackFlags = _diactionformat_priorityA(lpdiActionFormat, actionMasks[i]); IDirectInput_CreateDevice(iface, guids[i], &lpdid, NULL); IDirectInputDevice_GetDeviceInfo(lpdid, &didevi); - /* If there's at least one action for the device it's priority 1 */ - for(j=0; j < lpdiActionFormat->dwActionSize; j++) - if ((lpdiActionFormat->rgoAction[j].dwSemantic & actionMasks[i]) == actionMasks[i]) - callbackFlags |= DIEDBS_MAPPEDPRI1; - if (lpCallback(&didevi, lpdid, callbackFlags, sizeof(guids)/sizeof(guids[0]) - (i+1), pvRef) == DIENUM_STOP) return DI_OK; } @@ -942,7 +969,7 @@ static HRESULT WINAPI IDirectInput8WImpl_EnumDevicesBySemantics( { TRACE(" - checking device %u ('%s')\n", i, dinput_devices[i]->name); - callbackFlags = 0; + callbackFlags = _diactionformat_priorityW(lpdiActionFormat, lpdiActionFormat->dwGenre); /* Default behavior is to enumerate attached game controllers */ enumSuccess = dinput_devices[i]->enum_deviceW(DI8DEVCLASS_GAMECTRL, DIEDFL_ATTACHEDONLY | dwFlags, &didevi, This->dwVersion, j); if (enumSuccess) @@ -960,16 +987,11 @@ static HRESULT WINAPI IDirectInput8WImpl_EnumDevicesBySemantics( /* Enumerate keyboard and mouse */ for(i=0; i < sizeof(guids)/sizeof(guids[0]); i++) { - callbackFlags = 0; + callbackFlags = _diactionformat_priorityW(lpdiActionFormat, actionMasks[i]); IDirectInput_CreateDevice(iface, guids[i], &lpdid, NULL); IDirectInputDevice_GetDeviceInfo(lpdid, &didevi); - /* If there's at least one action for the device it's priority 1 */ - for(j=0; j < lpdiActionFormat->dwActionSize; j++) - if ((lpdiActionFormat->rgoAction[j].dwSemantic & actionMasks[i]) == actionMasks[i]) - callbackFlags |= DIEDBS_MAPPEDPRI1; - if (lpCallback(&didevi, lpdid, callbackFlags, sizeof(guids)/sizeof(guids[0]) - (i+1), pvRef) == DIENUM_STOP) return DI_OK; } -- 1.7.0.4
From 2c97c4d622a4731721705b81416191b47b738d71 Mon Sep 17 00:00:00 2001 From: Lucas Fialho Zawacki <lfzawa...@gmail.com> Date: Tue, 19 Jul 2011 20:25:48 -0300 Subject: [PATCH 2/4] dinput: BuildActionMap for all joysticks. For the moment only for buttons and axis. Added an utility function that finds the Nth dataformat object of a given type. --- dlls/dinput/device.c | 23 +++++++++++++++++++++++ dlls/dinput/device_private.h | 1 + dlls/dinput/joystick.c | 40 +++++++++++++++++++++++++++++++++++++++- 3 files changed, 63 insertions(+), 1 deletions(-) diff --git a/dlls/dinput/device.c b/dlls/dinput/device.c index 22632ea..2bd5d6c 100644 --- a/dlls/dinput/device.c +++ b/dlls/dinput/device.c @@ -366,6 +366,29 @@ static inline LPDIOBJECTDATAFORMAT dataformat_to_odf(LPCDIDATAFORMAT df, int idx return (LPDIOBJECTDATAFORMAT)((LPBYTE)df->rgodf + idx * df->dwObjSize); } +/* dataformat_to_odf_by_type + * Find the Nth object of the selected type in the DataFormat + */ +LPDIOBJECTDATAFORMAT dataformat_to_odf_by_type(LPCDIDATAFORMAT df, int n, DWORD type) +{ + int i, nfound = 0; + + for (i=0; i < df->dwNumObjs; i++) + { + LPDIOBJECTDATAFORMAT odf = dataformat_to_odf(df, i); + + if (odf->dwType & type) + { + if (n == nfound) + return odf; + + nfound++; + } + } + + return NULL; +} + static HRESULT create_DataFormat(LPCDIDATAFORMAT asked_format, DataFormat *format) { DataTransform *dt; diff --git a/dlls/dinput/device_private.h b/dlls/dinput/device_private.h index 503698a..6c061b2 100644 --- a/dlls/dinput/device_private.h +++ b/dlls/dinput/device_private.h @@ -125,6 +125,7 @@ extern void _dump_DIDATAFORMAT(const DIDATAFORMAT *df) DECLSPEC_HIDDEN; extern const char *_dump_dinput_GUID(const GUID *guid) DECLSPEC_HIDDEN; extern DWORD semantic_to_obj_id(IDirectInputDeviceImpl* This, DWORD dwSemantic) DECLSPEC_HIDDEN; +extern LPDIOBJECTDATAFORMAT dataformat_to_odf_by_type(LPCDIDATAFORMAT df, int n, DWORD type) DECLSPEC_HIDDEN; /* And the stubs */ extern HRESULT WINAPI IDirectInputDevice2AImpl_Acquire(LPDIRECTINPUTDEVICE8A iface) DECLSPEC_HIDDEN; diff --git a/dlls/dinput/joystick.c b/dlls/dinput/joystick.c index 68dcc6a..d821748 100644 --- a/dlls/dinput/joystick.c +++ b/dlls/dinput/joystick.c @@ -432,9 +432,47 @@ HRESULT WINAPI JoystickWGenericImpl_BuildActionMap(LPDIRECTINPUTDEVICE8W iface, LPCWSTR lpszUserName, DWORD dwFlags) { + JoystickGenericImpl *This = impl_from_IDirectInputDevice8W(iface); + int i, j, has_actions = 0; + DWORD object_types[] = { DIDFT_BUTTON, DIDFT_AXIS }; + DWORD type_map[] = { DIDFT_PSHBUTTON, DIDFT_RELAXIS }; + FIXME("(%p)->(%p,%s,%08x): semi-stub !\n", iface, lpdiaf, debugstr_w(lpszUserName), dwFlags); - return DI_NOEFFECT; + for (i=0; i < lpdiaf->dwNumActions; i++) + { + DWORD inst = (0x000000ff & (lpdiaf->rgoAction[i].dwSemantic)) - 1; + DWORD type = 0x000000ff & (lpdiaf->rgoAction[i].dwSemantic >> 8); + DWORD genre = 0xff000000 & lpdiaf->rgoAction[i].dwSemantic; + + /* Only consider actions of the right genre */ + if (lpdiaf->dwGenre != genre) continue; + + for (j=0; j < sizeof(object_types)/sizeof(object_types[0]); j++) + { + if (type & object_types[j]) + { + /* Assure that the object exists */ + LPDIOBJECTDATAFORMAT odf = dataformat_to_odf_by_type(This->base.data_format.wine_df, inst, DIDFT_BUTTON); + + if (odf != NULL) + { + lpdiaf->rgoAction[i].dwObjID = type_map[j] | (0x0000ff00 & (inst << 8)); + lpdiaf->rgoAction[i].guidInstance = This->base.guid; + lpdiaf->rgoAction[i].dwHow = DIAH_DEFAULT; + + has_actions = 1; + + /* No need to try other types if the action was already mapped */ + break; + } + } + } + } + + if (!has_actions) return DI_NOEFFECT; + + return IDirectInputDevice8WImpl_BuildActionMap(iface, lpdiaf, lpszUserName, dwFlags); } HRESULT WINAPI JoystickAGenericImpl_BuildActionMap(LPDIRECTINPUTDEVICE8A iface, -- 1.7.0.4
From a71ddba6092dd7a843c5359b26c4e9254311edfb Mon Sep 17 00:00:00 2001 From: Lucas Fialho Zawacki <lfzawa...@gmail.com> Date: Tue, 19 Jul 2011 20:44:36 -0300 Subject: [PATCH 3/4] dinput: SetActionMap for joysticks. --- dlls/dinput/joystick.c | 60 +++++++++++++++++++++++++++++++++++++++++++++++- 1 files changed, 59 insertions(+), 1 deletions(-) diff --git a/dlls/dinput/joystick.c b/dlls/dinput/joystick.c index d821748..c63def9 100644 --- a/dlls/dinput/joystick.c +++ b/dlls/dinput/joystick.c @@ -500,9 +500,67 @@ HRESULT WINAPI JoystickWGenericImpl_SetActionMap(LPDIRECTINPUTDEVICE8W iface, LPCWSTR lpszUserName, DWORD dwFlags) { + JoystickGenericImpl *This = impl_from_IDirectInputDevice8W(iface); + DIDATAFORMAT data_format; + DIOBJECTDATAFORMAT *obj_df = NULL; + int i, action = 0, num_actions = 0; + unsigned int offset = 0; + FIXME("(%p)->(%p,%s,%08x): semi-stub !\n", iface, lpdiaf, debugstr_w(lpszUserName), dwFlags); - return DI_NOEFFECT; + if (This->base.acquired) return DIERR_ACQUIRED; + + data_format.dwSize = sizeof(data_format); + data_format.dwObjSize = sizeof(DIOBJECTDATAFORMAT); + data_format.dwFlags = DIDF_RELAXIS; + data_format.dwDataSize = lpdiaf->dwDataSize; + + /* count the actions */ + for (i=0; i < lpdiaf->dwNumActions; i++) + if (IsEqualGUID(&This->base.guid, &lpdiaf->rgoAction[i].guidInstance)) + num_actions++; + + if (num_actions == 0) return DI_NOEFFECT; + + This->base.num_actions = num_actions; + + /* Construct the dataformat and actionmap */ + obj_df = HeapAlloc(GetProcessHeap(), 0, sizeof(DIOBJECTDATAFORMAT)*num_actions); + data_format.rgodf = (LPDIOBJECTDATAFORMAT)obj_df; + data_format.dwNumObjs = num_actions; + + This->base.action_map = HeapAlloc(GetProcessHeap(), 0, sizeof(ActionMap)*num_actions); + + for (i = 0; i < lpdiaf->dwNumActions; i++) + { + if (IsEqualGUID(&This->base.guid, &lpdiaf->rgoAction[i].guidInstance)) + { + LPDIDATAFORMAT df = This->base.data_format.wine_df; + DWORD inst = DIDFT_GETINSTANCE(lpdiaf->rgoAction[i].dwObjID); + DWORD type = DIDFT_GETTYPE(lpdiaf->rgoAction[i].dwObjID); + LPDIOBJECTDATAFORMAT obj; + + if (type == DIDFT_PSHBUTTON) type = DIDFT_BUTTON; + if (type == DIDFT_RELAXIS) type = DIDFT_AXIS; + + obj = dataformat_to_odf_by_type(df, inst, type); + + memcpy(&obj_df[action], obj, df->dwObjSize); + + This->base.action_map[action].uAppData = lpdiaf->rgoAction[i].uAppData; + This->base.action_map[action].offset = offset; + obj_df[action].dwOfs = offset; + offset += (type & DIDFT_BUTTON) ? 1 : 4; + + action++; + } + } + + IDirectInputDevice8_SetDataFormat(iface, &data_format); + + HeapFree(GetProcessHeap(), 0, obj_df); + + return IDirectInputDevice8WImpl_SetActionMap(iface, lpdiaf, lpszUserName, dwFlags); } HRESULT WINAPI JoystickAGenericImpl_SetActionMap(LPDIRECTINPUTDEVICE8A iface, -- 1.7.0.4
From 4f18cfb471707c461135ecfde5f975324b37e696 Mon Sep 17 00:00:00 2001 From: Lucas Fialho Zawacki <lfzawa...@gmail.com> Date: Tue, 19 Jul 2011 21:03:16 -0300 Subject: [PATCH 4/4] dinput: SetActionMap setting the axis range according to the action format --- dlls/dinput/device.c | 8 ++++++++ dlls/dinput8/tests/device.c | 15 +++++++++++++++ 2 files changed, 23 insertions(+), 0 deletions(-) diff --git a/dlls/dinput/device.c b/dlls/dinput/device.c index 2bd5d6c..58d4e14 100644 --- a/dlls/dinput/device.c +++ b/dlls/dinput/device.c @@ -1483,9 +1483,17 @@ HRESULT WINAPI IDirectInputDevice8WImpl_SetActionMap(LPDIRECTINPUTDEVICE8W iface DWORD dwFlags) { DIPROPDWORD dp; + DIPROPRANGE dpr; FIXME("(%p)->(%p,%s,%08x): semi-stub !\n", iface, lpdiaf, debugstr_w(lpszUserName), dwFlags); + dpr.diph.dwSize = sizeof(DIPROPRANGE); + dpr.lMin = lpdiaf->lAxisMin; + dpr.lMax = lpdiaf->lAxisMax; + dpr.diph.dwHeaderSize = sizeof(DIPROPHEADER); + dpr.diph.dwHow = DIPH_DEVICE; + IDirectInputDevice8_SetProperty(iface, DIPROP_RANGE, &dpr.diph); + if (lpdiaf->dwBufferSize > 0) { dp.diph.dwSize = sizeof(DIPROPDWORD); diff --git a/dlls/dinput8/tests/device.c b/dlls/dinput8/tests/device.c index cedad80..31e2e3f 100644 --- a/dlls/dinput8/tests/device.c +++ b/dlls/dinput8/tests/device.c @@ -132,6 +132,7 @@ static BOOL CALLBACK enumeration_callback( { HRESULT hr; DIPROPDWORD dp; + DIPROPRANGE dpr; struct enum_data *data = pvRef; if (!data) return DIENUM_CONTINUE; @@ -179,6 +180,20 @@ static BOOL CALLBACK enumeration_callback( ok (SUCCEEDED(hr), "GetProperty failed hr=%08x\n", hr); ok (dp.dwData == data->lpdiaf->dwBufferSize, "SetActionMap must set the buffer, buffersize=%d\n", dp.dwData); + /* Test axis range */ + memset(&dpr, 0, sizeof(dpr)); + dpr.diph.dwSize = sizeof(dpr); + dpr.diph.dwHeaderSize = sizeof(DIPROPHEADER); + dpr.diph.dwHow = DIPH_DEVICE; + + hr = IDirectInputDevice_GetProperty(lpdid, DIPROP_RANGE, &dpr.diph); + /* Only test if device supports the range property */ + if (SUCCEEDED(hr)) + { + ok (dpr.lMin == data->lpdiaf->lAxisMin, "SetActionMap must set the min axis range expected=%d got=%d\n", data->lpdiaf->lAxisMin, dpr.lMin); + ok (dpr.lMax == data->lpdiaf->lAxisMax, "SetActionMap must set the max axis range expected=%d got=%d\n", data->lpdiaf->lAxisMax, dpr.lMax); + } + /* SetActionMap has set the data format so now it should work */ hr = IDirectInputDevice8_Acquire(lpdid); ok (SUCCEEDED(hr), "Acquire failed hr=%08x\n", hr); -- 1.7.0.4