From: Makarenko Oleg <oleg(a)makarenk.ooo> Move scaling and clamping FFB report values to separate functions. Do not clamp effect duration or saturation values. --- dlls/dinput/joystick_hid.c | 118 +++++++++++++++++++---------- dlls/dinput/tests/force_feedback.c | 44 +---------- 2 files changed, 78 insertions(+), 84 deletions(-) diff --git a/dlls/dinput/joystick_hid.c b/dlls/dinput/joystick_hid.c index 63466c2a27c..f7f87596c82 100644 --- a/dlls/dinput/joystick_hid.c +++ b/dlls/dinput/joystick_hid.c @@ -752,22 +752,10 @@ static void set_report_value( struct hid_joystick *impl, char *report_buf, { ULONG report_len = impl->caps.OutputReportByteLength; PHIDP_PREPARSED_DATA preparsed = impl->preparsed; - LONG log_min, log_max, phy_min, phy_max; NTSTATUS status; if (!caps) return; - log_min = caps->logical_min; - log_max = caps->logical_max; - phy_min = caps->physical_min; - phy_max = caps->physical_max; - - if (phy_max || phy_min) - { - if (value > phy_max || value < phy_min) value = -1; - else value = log_min + (value - phy_min) * (log_max - log_min) / (phy_max - phy_min); - } - status = HidP_SetUsageValue( HidP_Output, caps->usage_page, caps->link_collection, caps->usage_min, value, preparsed, report_buf, report_len ); if (status != HIDP_STATUS_SUCCESS) WARN( "HidP_SetUsageValue %04x:%04x returned %#lx\n", @@ -840,6 +828,37 @@ static HRESULT hid_joystick_get_property( IDirectInputDevice8W *iface, DWORD pro return DIERR_UNSUPPORTED; } +static LONG scale_to_logical_value( LONG value, struct hid_value_caps *caps ) +{ + LONG log_min, log_max, phy_min, phy_max; + + if (!caps) return value; + + log_min = caps->logical_min; + log_max = caps->logical_max; + phy_min = caps->physical_min; + phy_max = caps->physical_max; + + if (phy_max || phy_min) + { + if (value > phy_max || value < phy_min) value = -1; + else value = log_min + (value - phy_min) * (log_max - log_min) / (phy_max - phy_min); + } + + return value; +} + +static LONG clamp_to_physical_value( LONG value, struct hid_value_caps *caps ) +{ + LONG phy_min, phy_max; + + if (!caps) return value; + + phy_min = caps->physical_min; + phy_max = caps->physical_max; + return max( min( value, phy_max ), phy_min ); +} + static HRESULT hid_joystick_send_device_gain( IDirectInputDevice8W *iface, LONG device_gain ) { struct hid_joystick *impl = impl_from_IDirectInputDevice8W( iface ); @@ -855,6 +874,8 @@ static HRESULT hid_joystick_send_device_gain( IDirectInputDevice8W *iface, LONG status = HidP_InitializeReportForID( HidP_Output, report->id, impl->preparsed, report_buf, report_len ); if (status != HIDP_STATUS_SUCCESS) return status; + device_gain = clamp_to_physical_value( device_gain, report->device_gain_caps ); + device_gain = scale_to_logical_value( device_gain, report->device_gain_caps ); set_report_value( impl, report_buf, report->device_gain_caps, device_gain ); if (!WriteFile( impl->device, report_buf, report_len, NULL, NULL )) return DIERR_INPUTLOST; @@ -2813,9 +2834,17 @@ static HRESULT WINAPI hid_joystick_effect_GetEffectStatus( IDirectInputEffect *i static void set_parameter_value( struct hid_joystick_effect *impl, char *report_buf, struct hid_value_caps *caps, LONG value ) { + value = scale_to_logical_value( value, caps ); return set_report_value( impl->joystick, report_buf, caps, value ); } +static void set_parameter_value_clamp( struct hid_joystick_effect *impl, char *report_buf, + struct hid_value_caps *caps, LONG value ) +{ + value = clamp_to_physical_value( value, caps ); + return set_parameter_value( impl, report_buf, caps, value ); +} + static void set_parameter_value_angle( struct hid_joystick_effect *impl, char *report_buf, struct hid_value_caps *caps, LONG value ) { @@ -2825,19 +2854,23 @@ static void set_parameter_value_angle( struct hid_joystick_effect *impl, char *r if (caps->units != 0x14) WARN( "unknown angle unit caps %#lx\n", caps->units ); else if (exp < -2) while (exp++ < -2) value *= 10; else if (exp > -2) while (exp-- > -2) value /= 10; - set_parameter_value( impl, report_buf, caps, value ); + set_parameter_value_clamp( impl, report_buf, caps, value ); } static void set_parameter_value_us( struct hid_joystick_effect *impl, char *report_buf, struct hid_value_caps *caps, LONG value ) { LONG exp; + if (!caps) return; + if (value == INFINITE) return set_report_value( impl->joystick, report_buf, caps, caps->physical_min - 1 ); + exp = caps->units_exp; - if (value == INFINITE) value = caps->physical_min - 1; - else if (caps->units != 0x1003) WARN( "unknown time unit caps %#lx\n", caps->units ); + if (caps->units != 0x1003) WARN( "unknown time unit caps %#lx\n", caps->units ); else if (exp < -6) while (exp++ < -6) value *= 10; else if (exp > -6) while (exp-- > -6) value /= 10; + + /* testing shows that duration values are not clamped, any out-of-range values becomes -1 */ set_parameter_value( impl, report_buf, caps, value ); } @@ -2907,14 +2940,14 @@ static HRESULT WINAPI hid_joystick_effect_Download( IDirectInputEffect *iface ) case PID_USAGE_ET_SAWTOOTH_DOWN: if (!(impl->modified & DIEP_TYPESPECIFICPARAMS)) break; - set_parameter_value( impl, impl->type_specific_buf, set_periodic->magnitude_caps, - impl->periodic.dwMagnitude ); + set_parameter_value_clamp( impl, impl->type_specific_buf, set_periodic->magnitude_caps, + impl->periodic.dwMagnitude ); set_parameter_value_us( impl, impl->type_specific_buf, set_periodic->period_caps, impl->periodic.dwPeriod ); - set_parameter_value( impl, impl->type_specific_buf, set_periodic->phase_caps, - impl->periodic.dwPhase ); - set_parameter_value( impl, impl->type_specific_buf, set_periodic->offset_caps, - impl->periodic.lOffset ); + set_parameter_value_clamp( impl, impl->type_specific_buf, set_periodic->phase_caps, + impl->periodic.dwPhase ); + set_parameter_value_clamp( impl, impl->type_specific_buf, set_periodic->offset_caps, + impl->periodic.lOffset ); if (!WriteFile( device, impl->type_specific_buf, report_len, NULL, NULL )) hr = DIERR_INPUTLOST; else impl->modified &= ~DIEP_TYPESPECIFICPARAMS; @@ -2931,18 +2964,21 @@ static HRESULT WINAPI hid_joystick_effect_Download( IDirectInputEffect *iface ) i, impl->joystick->preparsed, impl->type_specific_buf, report_len ); if (status != HIDP_STATUS_SUCCESS) WARN( "HidP_SetUsageValue %04x:%04x returned %#lx\n", HID_USAGE_PAGE_PID, PID_USAGE_PARAMETER_BLOCK_OFFSET, status ); - set_parameter_value( impl, impl->type_specific_buf, set_condition->center_point_offset_caps, - impl->condition[i].lOffset ); - set_parameter_value( impl, impl->type_specific_buf, set_condition->positive_coefficient_caps, - impl->condition[i].lPositiveCoefficient ); - set_parameter_value( impl, impl->type_specific_buf, set_condition->negative_coefficient_caps, - impl->condition[i].lNegativeCoefficient ); + set_parameter_value_clamp( impl, impl->type_specific_buf, set_condition->center_point_offset_caps, + impl->condition[i].lOffset ); + set_parameter_value_clamp( impl, impl->type_specific_buf, set_condition->positive_coefficient_caps, + impl->condition[i].lPositiveCoefficient ); + set_parameter_value_clamp( impl, impl->type_specific_buf, set_condition->negative_coefficient_caps, + impl->condition[i].lNegativeCoefficient ); + + /* testing shows that saturation values are not clamped, any out-of-range values becomes -1 */ set_parameter_value( impl, impl->type_specific_buf, set_condition->positive_saturation_caps, impl->condition[i].dwPositiveSaturation ); set_parameter_value( impl, impl->type_specific_buf, set_condition->negative_saturation_caps, impl->condition[i].dwNegativeSaturation ); - set_parameter_value( impl, impl->type_specific_buf, set_condition->dead_band_caps, - impl->condition[i].lDeadBand ); + + set_parameter_value_clamp( impl, impl->type_specific_buf, set_condition->dead_band_caps, + impl->condition[i].lDeadBand ); if (!WriteFile( device, impl->type_specific_buf, report_len, NULL, NULL )) hr = DIERR_INPUTLOST; else impl->modified &= ~DIEP_TYPESPECIFICPARAMS; @@ -2951,8 +2987,8 @@ static HRESULT WINAPI hid_joystick_effect_Download( IDirectInputEffect *iface ) case PID_USAGE_ET_CONSTANT_FORCE: if (!(impl->modified & DIEP_TYPESPECIFICPARAMS)) break; - set_parameter_value( impl, impl->type_specific_buf, set_constant_force->magnitude_caps, - impl->constant_force.lMagnitude ); + set_parameter_value_clamp( impl, impl->type_specific_buf, set_constant_force->magnitude_caps, + impl->constant_force.lMagnitude ); if (!WriteFile( device, impl->type_specific_buf, report_len, NULL, NULL )) hr = DIERR_INPUTLOST; else impl->modified &= ~DIEP_TYPESPECIFICPARAMS; @@ -2960,10 +2996,10 @@ static HRESULT WINAPI hid_joystick_effect_Download( IDirectInputEffect *iface ) case PID_USAGE_ET_RAMP: if (!(impl->modified & DIEP_TYPESPECIFICPARAMS)) break; - set_parameter_value( impl, impl->type_specific_buf, set_ramp_force->start_caps, - impl->ramp_force.lStart ); - set_parameter_value( impl, impl->type_specific_buf, set_ramp_force->end_caps, - impl->ramp_force.lEnd ); + set_parameter_value_clamp( impl, impl->type_specific_buf, set_ramp_force->start_caps, + impl->ramp_force.lStart ); + set_parameter_value_clamp( impl, impl->type_specific_buf, set_ramp_force->end_caps, + impl->ramp_force.lEnd ); if (!WriteFile( device, impl->type_specific_buf, report_len, NULL, NULL )) hr = DIERR_INPUTLOST; else impl->modified &= ~DIEP_TYPESPECIFICPARAMS; @@ -2985,12 +3021,12 @@ static HRESULT WINAPI hid_joystick_effect_Download( IDirectInputEffect *iface ) if (!(impl->flags & DIEP_ENVELOPE)) break; if (!(impl->modified & DIEP_ENVELOPE)) break; - set_parameter_value( impl, impl->set_envelope_buf, set_envelope->attack_level_caps, - impl->envelope.dwAttackLevel ); + set_parameter_value_clamp( impl, impl->set_envelope_buf, set_envelope->attack_level_caps, + impl->envelope.dwAttackLevel ); set_parameter_value_us( impl, impl->set_envelope_buf, set_envelope->attack_time_caps, impl->envelope.dwAttackTime ); - set_parameter_value( impl, impl->set_envelope_buf, set_envelope->fade_level_caps, - impl->envelope.dwFadeLevel ); + set_parameter_value_clamp( impl, impl->set_envelope_buf, set_envelope->fade_level_caps, + impl->envelope.dwFadeLevel ); set_parameter_value_us( impl, impl->set_envelope_buf, set_envelope->fade_time_caps, impl->envelope.dwFadeTime ); @@ -3004,8 +3040,8 @@ static HRESULT WINAPI hid_joystick_effect_Download( IDirectInputEffect *iface ) { set_parameter_value_us( impl, impl->effect_update_buf, effect_update->duration_caps, impl->params.dwDuration ); - set_parameter_value( impl, impl->effect_update_buf, effect_update->gain_caps, - impl->params.dwGain ); + set_parameter_value_clamp( impl, impl->effect_update_buf, effect_update->gain_caps, + impl->params.dwGain ); set_parameter_value_us( impl, impl->effect_update_buf, effect_update->sample_period_caps, impl->params.dwSamplePeriod ); set_parameter_value_us( impl, impl->effect_update_buf, effect_update->start_delay_caps, diff --git a/dlls/dinput/tests/force_feedback.c b/dlls/dinput/tests/force_feedback.c index 35340d75a24..b2ef329cd08 100644 --- a/dlls/dinput/tests/force_feedback.c +++ b/dlls/dinput/tests/force_feedback.c @@ -1954,57 +1954,15 @@ static void test_constantforce_effect( IDirectInputDevice8W *device, HANDLE file .report_buf = {0x03,0x01,0x04,0x08,0x01,0x00,version >= 0x700 ? 0x06 : 0x00,0x00,0x01,0x3f,0x00}, }, }; - struct hid_expect expect_create_1_todo[] = - { - /* set constantforce, magnitude 10000 */ - { - .code = IOCTL_HID_WRITE_REPORT, - .report_id = 9, - .report_len = 3, - .report_buf = {0x09,0xff,0xff}, - .todo = TRUE, .wine_only = TRUE, - }, - /* set constantforce, magnitude 10000 */ - { - .code = IOCTL_HID_WRITE_REPORT, - .report_id = 9, - .report_len = 3, - .report_buf = {0x09,0x10,0x27}, - .todo = TRUE, - }, - /* update envelope */ - { - .code = IOCTL_HID_WRITE_REPORT, - .report_id = 6, - .report_len = 7, - .report_buf = {0x06,0x19,0x4c,0x02,0x00,0x04,0x00}, - }, - /* update effect */ - { - .code = IOCTL_HID_WRITE_REPORT, - .report_id = 3, - .report_len = 11, - .report_buf = {0x03,0x01,0x04,0x08,0x01,0x00,version >= 0x700 ? 0x06 : 0x00,0x00,0x01,0x3f,0x00}, - }, - }; struct hid_expect expect_create_2[] = { - /* set constantforce, magnitude -10000 */ - { - .code = IOCTL_HID_WRITE_REPORT, - .report_id = 9, - .report_len = 3, - .report_buf = {0x09,0xff,0xff}, - .todo = TRUE, .wine_only = TRUE, - }, /* set constantforce, magnitude -10000 */ { .code = IOCTL_HID_WRITE_REPORT, .report_id = 9, .report_len = 3, .report_buf = {0x09,0xf0,0xd8}, - .todo = TRUE, }, /* update envelope */ { @@ -2170,7 +2128,7 @@ static void test_constantforce_effect( IDirectInputDevice8W *device, HANDLE file desc.cbTypeSpecificParams = sizeof(DICONSTANTFORCE); desc.lpvTypeSpecificParams = (void *)&input_constant_force[2]; - set_hid_expect( file, expect_create_1_todo, sizeof(expect_create_1_todo) ); + set_hid_expect( file, expect_create_1, sizeof(expect_create_1) ); hr = IDirectInputDevice8_CreateEffect( device, &GUID_ConstantForce, &desc, &effect, NULL ); ok( hr == DI_OK, "CreateEffect returned %#lx\n", hr ); set_hid_expect( file, NULL, 0 ); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/7217