@rbernon here are the the fixes for joy.cpl that I mentioned in !8744 separated from the autocenter stuff.
You can see the individual commits, but basically it makes the press button to play effect feature work again, does a bit of cleanup, and fixes some other bugs I came across in tho code.
From: Tyson Whitehead twhitehead@gmail.com
--- dlls/joy.cpl/dinput.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/dlls/joy.cpl/dinput.c b/dlls/joy.cpl/dinput.c index 68268cbb72c..f090abba3fd 100644 --- a/dlls/joy.cpl/dinput.c +++ b/dlls/joy.cpl/dinput.c @@ -166,7 +166,7 @@ static void set_selected_effect( IDirectInputEffect *effect )
EnterCriticalSection( &state_cs ); if ((previous = effect_selected)) IDirectInputEffect_Release( previous ); - if ((effect_selected = effect)) IDirectInput_AddRef( effect ); + if ((effect_selected = effect)) IDirectInputEffect_AddRef( effect ); LeaveCriticalSection( &state_cs ); }
From: Tyson Whitehead twhitehead@gmail.com
Existing code didn't actually query device state, so it would never detect button presses. Adding the missing query.
Techincally both the device and the effect should be taken together to ensure they match up correctly, so switched to retrieving both within the state critical section.
Assigning the X and Y state positions to the rgdwAxes parameters was also entirely wrong as these identify the axes and not assign them values. Presomably the intent was to play the effect back towards the neutral position as well, so implement that too. --- dlls/joy.cpl/dinput.c | 60 ++++++++++++++++++++++++++----------------- 1 file changed, 37 insertions(+), 23 deletions(-)
diff --git a/dlls/joy.cpl/dinput.c b/dlls/joy.cpl/dinput.c index f090abba3fd..5d172d8c185 100644 --- a/dlls/joy.cpl/dinput.c +++ b/dlls/joy.cpl/dinput.c @@ -268,42 +268,56 @@ static DWORD WINAPI input_thread( void *param )
while (WaitForMultipleObjects( 2, events, FALSE, INFINITE ) != 0) { + IDirectInputDevice8W *device; IDirectInputEffect *effect; DIJOYSTATE2 state = {0}; unsigned int i; + HRESULT hr;
SendMessageW( dialog_hwnd, WM_USER, 0, 0 );
- if ((effect = get_selected_effect())) + EnterCriticalSection( &state_cs ); + device = get_selected_device(); + effect = get_selected_effect(); + LeaveCriticalSection( &state_cs ); + + if ( device && effect ) { - DWORD flags = DIEP_AXES | DIEP_DIRECTION | DIEP_NORESTART; - LONG direction[3] = {0}; - DWORD axes[3] = {0}; - DIEFFECT params = + if ( FAILED(hr = IDirectInputDevice8_GetDeviceState( device, sizeof(state), &state )) ) { - .dwSize = sizeof(DIEFFECT), - .dwFlags = DIEFF_CARTESIAN | DIEFF_OBJECTOFFSETS, - .rglDirection = direction, - .rgdwAxes = axes, - .cAxes = 3, - }; - - IDirectInputEffect_GetParameters( effect, ¶ms, flags ); - params.rgdwAxes[0] = state.lX; - params.rgdwAxes[1] = state.lY; - - for (i = 0; i < ARRAY_SIZE(state.rgbButtons); i++) + WARN("Unable to play effect as unable to get device state, hr = %lx", hr); + } + else { - if (state.rgbButtons[i]) + for (i = 0; i < ARRAY_SIZE(state.rgbButtons); i++) { - IDirectInputEffect_SetParameters( effect, ¶ms, flags ); - IDirectInputEffect_Start( effect, 1, 0 ); - break; + if (state.rgbButtons[i]) + { + LONG direction[2]; + DWORD axes[2] = {DIJOFS_X, DIJOFS_Y}; + DIEFFECT params = + { + .dwSize = sizeof(DIEFFECT), + .dwFlags = DIEFF_CARTESIAN | DIEFF_OBJECTOFFSETS, + .rglDirection = direction, + .rgdwAxes = axes, + .cAxes = 2, + }; + + params.rglDirection[0] = state.lX - 32768; + params.rglDirection[1] = state.lY - 32768; + do hr = IDirectInputEffect_SetParameters( effect, ¶ms, DIEP_DIRECTION | DIEP_NORESTART ); + while (FAILED(hr) && --params.cAxes); + + IDirectInputEffect_Start( effect, 1, 0 ); + break; + } } } - - IDirectInputEffect_Release( effect ); } + + if ( device ) IDirectInputDevice8_Release( device ); + if ( effect ) IDirectInputEffect_Release( effect ); }
return 0;
From: Tyson Whitehead twhitehead@gmail.com
--- dlls/joy.cpl/dinput.c | 2 -- 1 file changed, 2 deletions(-)
diff --git a/dlls/joy.cpl/dinput.c b/dlls/joy.cpl/dinput.c index 5d172d8c185..febf365f2a3 100644 --- a/dlls/joy.cpl/dinput.c +++ b/dlls/joy.cpl/dinput.c @@ -232,7 +232,6 @@ static IDirectInputDevice8W *get_selected_device(void)
static BOOL CALLBACK enum_devices( const DIDEVICEINSTANCEW *instance, void *context ) { - DIDEVCAPS caps = {.dwSize = sizeof(DIDEVCAPS)}; IDirectInput8W *dinput = context; struct device *entry;
@@ -240,7 +239,6 @@ static BOOL CALLBACK enum_devices( const DIDEVICEINSTANCEW *instance, void *cont
IDirectInput8_CreateDevice( dinput, &instance->guidInstance, &entry->device, NULL ); IDirectInputDevice8_SetDataFormat( entry->device, &c_dfDIJoystick2 ); - IDirectInputDevice8_GetCapabilities( entry->device, &caps );
list_add_tail( &devices, &entry->entry );
From: Tyson Whitehead twhitehead@gmail.com
Found it pretty hard to follow acquire and unaquire calls and whether the required data format and cooperative level was set as required. Moved setting the cooperative level to device enumeration where the data format was being set. Moved acquire to the device selection routine where inaquire already lived for the one released. --- dlls/joy.cpl/dinput.c | 25 +++++++++---------------- 1 file changed, 9 insertions(+), 16 deletions(-)
diff --git a/dlls/joy.cpl/dinput.c b/dlls/joy.cpl/dinput.c index febf365f2a3..0046b3fc0cf 100644 --- a/dlls/joy.cpl/dinput.c +++ b/dlls/joy.cpl/dinput.c @@ -107,9 +107,6 @@ static BOOL CALLBACK enum_effects( const DIEFFECTINFOW *info, void *context ) struct effect *entry; HRESULT hr;
- hr = IDirectInputDevice8_Acquire( device ); - if (FAILED(hr)) return DIENUM_CONTINUE; - if (!(entry = calloc( 1, sizeof(*entry) ))) return DIENUM_STOP;
if (IsEqualGUID( &info->guid, &GUID_RampForce )) @@ -206,6 +203,7 @@ static void set_selected_device( IDirectInputDevice8W *device ) if ((previous = device_selected)) { IDirectInputDevice8_SetEventNotification( previous, NULL ); + IDirectInputDevice8_Unacquire( previous ); IDirectInputDevice8_Release( previous ); } if ((device_selected = device)) @@ -234,13 +232,18 @@ static BOOL CALLBACK enum_devices( const DIDEVICEINSTANCEW *instance, void *cont { IDirectInput8W *dinput = context; struct device *entry; + HRESULT hr;
if (!(entry = calloc( 1, sizeof(*entry) ))) return DIENUM_STOP;
- IDirectInput8_CreateDevice( dinput, &instance->guidInstance, &entry->device, NULL ); - IDirectInputDevice8_SetDataFormat( entry->device, &c_dfDIJoystick2 ); + hr = IDirectInput8_CreateDevice( dinput, &instance->guidInstance, &entry->device, NULL ); + if ( SUCCEEDED(hr) ) hr = IDirectInputDevice8_SetDataFormat( entry->device, &c_dfDIJoystick2 ); + if ( SUCCEEDED(hr) ) hr = IDirectInputDevice8_SetCooperativeLevel( entry->device, GetAncestor( dialog_hwnd, GA_ROOT ), + DISCL_BACKGROUND | DISCL_EXCLUSIVE ); + if ( SUCCEEDED(hr) ) list_add_tail( &devices, &entry->entry ); + else WARN( "Skipping device '%s' due to API failure, hr = %#lx\n", debugstr_w(instance->tszInstanceName), hr );
- list_add_tail( &devices, &entry->entry ); + if ( FAILED(hr) && entry->device ) IDirectInputDevice8_Release( entry->device );
return DIENUM_CONTINUE; } @@ -254,7 +257,6 @@ static void clear_devices(void) LIST_FOR_EACH_ENTRY_SAFE( entry, next, &devices, struct device, entry ) { list_remove( &entry->entry ); - IDirectInputDevice8_Unacquire( entry->device ); IDirectInputDevice8_Release( entry->device ); free( entry ); } @@ -663,7 +665,6 @@ static void update_di_effects( HWND hwnd, IDirectInputDevice8W *device )
static void handle_di_effects_change( HWND hwnd ) { - IDirectInputDevice8W *device; struct list *entry; int sel;
@@ -677,14 +678,6 @@ static void handle_di_effects_change( HWND hwnd ) if (!entry) return;
set_selected_effect( LIST_ENTRY( entry, struct effect, entry )->effect ); - - if ((device = get_selected_device())) - { - IDirectInputDevice8_Unacquire( device ); - IDirectInputDevice8_SetCooperativeLevel( device, GetAncestor( hwnd, GA_ROOT ), DISCL_BACKGROUND | DISCL_EXCLUSIVE ); - IDirectInputDevice8_Acquire( device ); - IDirectInputDevice8_Release( device ); - } }
static void create_device_views( HWND hwnd )
From: Tyson Whitehead twhitehead@gmail.com
Effect was continuously being started while button was deteced on, which wasn't great feedback on stick. Either wanted to just start once on press or release or play continuously while held.
Went with continuously playing while button is held. --- dlls/joy.cpl/dinput.c | 64 ++++++++++++++++++++++++++++--------------- dlls/joy.cpl/joy.rc | 2 +- 2 files changed, 43 insertions(+), 23 deletions(-)
diff --git a/dlls/joy.cpl/dinput.c b/dlls/joy.cpl/dinput.c index 0046b3fc0cf..067a9855cd0 100644 --- a/dlls/joy.cpl/dinput.c +++ b/dlls/joy.cpl/dinput.c @@ -77,7 +77,7 @@ static BOOL CALLBACK enum_effects( const DIEFFECTINFOW *info, void *context ) { .dwSize = sizeof(DIEFFECT), .dwFlags = DIEFF_CARTESIAN | DIEFF_OBJECTOFFSETS, - .dwDuration = 2 * DI_SECONDS, + .dwDuration = INFINITE, .dwGain = DI_FFNOMINALMAX, .rglDirection = direction, .rgdwAxes = axes, @@ -111,6 +111,7 @@ static BOOL CALLBACK enum_effects( const DIEFFECTINFOW *info, void *context )
if (IsEqualGUID( &info->guid, &GUID_RampForce )) { + params.dwDuration = 2 * DI_SECONDS, params.cbTypeSpecificParams = sizeof(ramp); params.lpvTypeSpecificParams = &ramp; params.dwFlags |= DIEP_TYPESPECIFICPARAMS; @@ -264,6 +265,7 @@ static void clear_devices(void)
static DWORD WINAPI input_thread( void *param ) { + IDirectInputEffect *playing_effect = NULL; HANDLE events[2] = {param, state_event};
while (WaitForMultipleObjects( 2, events, FALSE, INFINITE ) != 0) @@ -271,7 +273,6 @@ static DWORD WINAPI input_thread( void *param ) IDirectInputDevice8W *device; IDirectInputEffect *effect; DIJOYSTATE2 state = {0}; - unsigned int i; HRESULT hr;
SendMessageW( dialog_hwnd, WM_USER, 0, 0 ); @@ -289,29 +290,41 @@ static DWORD WINAPI input_thread( void *param ) } else { + unsigned int i; + BOOL triggered = FALSE; for (i = 0; i < ARRAY_SIZE(state.rgbButtons); i++) { - if (state.rgbButtons[i]) - { - LONG direction[2]; - DWORD axes[2] = {DIJOFS_X, DIJOFS_Y}; - DIEFFECT params = - { - .dwSize = sizeof(DIEFFECT), - .dwFlags = DIEFF_CARTESIAN | DIEFF_OBJECTOFFSETS, - .rglDirection = direction, - .rgdwAxes = axes, - .cAxes = 2, - }; - - params.rglDirection[0] = state.lX - 32768; - params.rglDirection[1] = state.lY - 32768; - do hr = IDirectInputEffect_SetParameters( effect, ¶ms, DIEP_DIRECTION | DIEP_NORESTART ); - while (FAILED(hr) && --params.cAxes); - - IDirectInputEffect_Start( effect, 1, 0 ); + if (state.rgbButtons[i]) { + TRACE("Button %d is on, effect is triggered...\n", i); + triggered = TRUE; break; - } + } + } + + if ( triggered && !playing_effect ) + { + LONG direction[2]; + DIEFFECT params = + { + .dwSize = sizeof(DIEFFECT), + .dwFlags = DIEFF_CARTESIAN | DIEFF_OBJECTOFFSETS, + .rglDirection = direction, + .cAxes = 2, + }; + + params.rglDirection[0] = state.lX - 32768; + params.rglDirection[1] = state.lY - 32768; + do hr = IDirectInputEffect_SetParameters( effect, ¶ms, DIEP_DIRECTION | DIEP_NORESTART ); + while (FAILED(hr) && --params.cAxes); + + IDirectInputEffect_Start( effect, 1, 0 ); + IDirectInputEffect_AddRef( effect ); + playing_effect = effect; + } + else if ( !triggered && playing_effect ) { + IDirectInputEffect_Stop( playing_effect ); + IDirectInputEffect_Release( playing_effect ); + playing_effect = NULL; } } } @@ -320,6 +333,13 @@ static DWORD WINAPI input_thread( void *param ) if ( effect ) IDirectInputEffect_Release( effect ); }
+ if ( playing_effect ) + { + IDirectInputEffect_Stop( playing_effect ); + IDirectInputEffect_Release( playing_effect ); + playing_effect = NULL; + } + return 0; }
diff --git a/dlls/joy.cpl/joy.rc b/dlls/joy.cpl/joy.rc index 1eb62d86ab3..c98bbe886c4 100644 --- a/dlls/joy.cpl/joy.rc +++ b/dlls/joy.cpl/joy.rc @@ -63,7 +63,7 @@ FONT 8, "Ms Shell Dlg" GROUPBOX "Buttons", IDC_DI_BUTTONS, 15, 100, 291, 86 LTEXT "Force Feedback Effect", IDC_STATIC, 15, 196, 291, 10 LISTBOX IDC_DI_EFFECTS, 15, 206, 291, 54, WS_TABSTOP | WS_VSCROLL | LBS_NOTIFY - LTEXT "Press any button in the controller to activate the chosen effect. The effect direction can be changed with the controller axis.", + LTEXT "Hold any button in the controller to play the chosen effect. The effect direction can be changed with the controller axis.", IDC_STATIC, 15, 260, 291, 25 }
From: Tyson Whitehead twhitehead@gmail.com
--- dlls/joy.cpl/dinput.c | 10 ++++++++++ 1 file changed, 10 insertions(+)
diff --git a/dlls/joy.cpl/dinput.c b/dlls/joy.cpl/dinput.c index 067a9855cd0..e12b6a8ad25 100644 --- a/dlls/joy.cpl/dinput.c +++ b/dlls/joy.cpl/dinput.c @@ -233,6 +233,14 @@ static BOOL CALLBACK enum_devices( const DIDEVICEINSTANCEW *instance, void *cont { IDirectInput8W *dinput = context; struct device *entry; + DIPROPDWORD ac_prop = { + .diph = { + .dwSize = sizeof(DIPROPDWORD), + .dwHeaderSize = sizeof(DIPROPHEADER), + .dwHow = DIPH_DEVICE, + }, + .dwData = DIPROPAUTOCENTER_OFF, + }; HRESULT hr;
if (!(entry = calloc( 1, sizeof(*entry) ))) return DIENUM_STOP; @@ -241,6 +249,8 @@ static BOOL CALLBACK enum_devices( const DIDEVICEINSTANCEW *instance, void *cont if ( SUCCEEDED(hr) ) hr = IDirectInputDevice8_SetDataFormat( entry->device, &c_dfDIJoystick2 ); if ( SUCCEEDED(hr) ) hr = IDirectInputDevice8_SetCooperativeLevel( entry->device, GetAncestor( dialog_hwnd, GA_ROOT ), DISCL_BACKGROUND | DISCL_EXCLUSIVE ); + if ( SUCCEEDED(hr) ) IDirectInputDevice8_SetProperty( entry->device, DIPROP_AUTOCENTER, &ac_prop.diph ); + if ( SUCCEEDED(hr) ) list_add_tail( &devices, &entry->entry ); else WARN( "Skipping device '%s' due to API failure, hr = %#lx\n", debugstr_w(instance->tszInstanceName), hr );
From: Tyson Whitehead twhitehead@gmail.com
This flag is for passing to Get/SetParameters. It is not a valid setting for the DIEFFECT dwFlags member according to the docs.
Type specific parametrs are indicated by cbTypeSpecificParameters being not 0 and lpvTypeSpecificParametrs being non-NULL. --- dlls/joy.cpl/dinput.c | 4 ---- 1 file changed, 4 deletions(-)
diff --git a/dlls/joy.cpl/dinput.c b/dlls/joy.cpl/dinput.c index e12b6a8ad25..a2533711a3b 100644 --- a/dlls/joy.cpl/dinput.c +++ b/dlls/joy.cpl/dinput.c @@ -114,13 +114,11 @@ static BOOL CALLBACK enum_effects( const DIEFFECTINFOW *info, void *context ) params.dwDuration = 2 * DI_SECONDS, params.cbTypeSpecificParams = sizeof(ramp); params.lpvTypeSpecificParams = &ramp; - params.dwFlags |= DIEP_TYPESPECIFICPARAMS; } else if (IsEqualGUID( &info->guid, &GUID_ConstantForce )) { params.cbTypeSpecificParams = sizeof(constant); params.lpvTypeSpecificParams = &constant; - params.dwFlags |= DIEP_TYPESPECIFICPARAMS; } else if (IsEqualGUID( &info->guid, &GUID_Sine ) || IsEqualGUID( &info->guid, &GUID_Square ) || @@ -130,7 +128,6 @@ static BOOL CALLBACK enum_effects( const DIEFFECTINFOW *info, void *context ) { params.cbTypeSpecificParams = sizeof(periodic); params.lpvTypeSpecificParams = &periodic; - params.dwFlags |= DIEP_TYPESPECIFICPARAMS; } else if (IsEqualGUID( &info->guid, &GUID_Spring ) || IsEqualGUID( &info->guid, &GUID_Damper ) || @@ -139,7 +136,6 @@ static BOOL CALLBACK enum_effects( const DIEFFECTINFOW *info, void *context ) { params.cbTypeSpecificParams = sizeof(condition); params.lpvTypeSpecificParams = &condition; - params.dwFlags |= DIEP_TYPESPECIFICPARAMS; }
do hr = IDirectInputDevice2_CreateEffect( device, &info->guid, ¶ms, &effect, NULL );
From: Tyson Whitehead twhitehead@gmail.com
Wore general as will apply type specific parametrs to all effects that are of the given type and not just specific types. --- dlls/joy.cpl/dinput.c | 27 +++++++++------------------ 1 file changed, 9 insertions(+), 18 deletions(-)
diff --git a/dlls/joy.cpl/dinput.c b/dlls/joy.cpl/dinput.c index a2533711a3b..c72bf751c9d 100644 --- a/dlls/joy.cpl/dinput.c +++ b/dlls/joy.cpl/dinput.c @@ -109,33 +109,24 @@ static BOOL CALLBACK enum_effects( const DIEFFECTINFOW *info, void *context )
if (!(entry = calloc( 1, sizeof(*entry) ))) return DIENUM_STOP;
- if (IsEqualGUID( &info->guid, &GUID_RampForce )) - { + switch( DIEFT_GETTYPE(info->dwEffType) ) { + case DIEFT_RAMPFORCE: params.dwDuration = 2 * DI_SECONDS, params.cbTypeSpecificParams = sizeof(ramp); params.lpvTypeSpecificParams = &ramp; - } - else if (IsEqualGUID( &info->guid, &GUID_ConstantForce )) - { + break; + case DIEFT_CONSTANTFORCE: params.cbTypeSpecificParams = sizeof(constant); params.lpvTypeSpecificParams = &constant; - } - else if (IsEqualGUID( &info->guid, &GUID_Sine ) || - IsEqualGUID( &info->guid, &GUID_Square ) || - IsEqualGUID( &info->guid, &GUID_Triangle ) || - IsEqualGUID( &info->guid, &GUID_SawtoothUp ) || - IsEqualGUID( &info->guid, &GUID_SawtoothDown )) - { + break; + case DIEFT_PERIODIC: params.cbTypeSpecificParams = sizeof(periodic); params.lpvTypeSpecificParams = &periodic; - } - else if (IsEqualGUID( &info->guid, &GUID_Spring ) || - IsEqualGUID( &info->guid, &GUID_Damper ) || - IsEqualGUID( &info->guid, &GUID_Inertia ) || - IsEqualGUID( &info->guid, &GUID_Friction )) - { + break; + case DIEFT_CONDITION: params.cbTypeSpecificParams = sizeof(condition); params.lpvTypeSpecificParams = &condition; + break; }
do hr = IDirectInputDevice2_CreateEffect( device, &info->guid, ¶ms, &effect, NULL );