Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/dinput/joystick_hid.c | 157 ++++++++++++++++++++++++++++++++++++- dlls/dinput8/tests/hid.c | 33 -------- 2 files changed, 153 insertions(+), 37 deletions(-)
diff --git a/dlls/dinput/joystick_hid.c b/dlls/dinput/joystick_hid.c index c11b3500e52..b727ab16976 100644 --- a/dlls/dinput/joystick_hid.c +++ b/dlls/dinput/joystick_hid.c @@ -131,6 +131,7 @@ struct hid_joystick BYTE device_state_report_id; BYTE device_state[DEVICE_STATE_MAX_SIZE];
+ BYTE effect_inuse[255]; struct list effect_list; struct pid_control_report pid_device_control; struct pid_control_report pid_effect_control; @@ -260,6 +261,19 @@ static const WCHAR *effect_guid_to_string( const GUID *guid ) return NULL; }
+static HRESULT find_next_effect_id( struct hid_joystick *impl, ULONG *index ) +{ + ULONG i; + + for (i = 0; i < ARRAY_SIZE(impl->effect_inuse); ++i) + if (!impl->effect_inuse[i]) break; + if (i == ARRAY_SIZE(impl->effect_inuse)) return DIERR_DEVICEFULL; + impl->effect_inuse[i] = TRUE; + *index = i + 1; + + return DI_OK; +} + typedef BOOL (*enum_object_callback)( struct hid_joystick *impl, struct hid_value_caps *caps, DIDEVICEOBJECTINSTANCEW *instance, void *data );
@@ -1179,6 +1193,12 @@ static HRESULT WINAPI hid_joystick_GetForceFeedbackState( IDirectInputDevice8W * return DIERR_UNSUPPORTED; }
+static BOOL CALLBACK unload_effect_object( IDirectInputEffect *effect, void *context ) +{ + IDirectInputEffect_Unload( effect ); + return DIENUM_CONTINUE; +} + static HRESULT WINAPI hid_joystick_SendForceFeedbackCommand( IDirectInputDevice8W *iface, DWORD command ) { struct hid_joystick *impl = impl_from_IDirectInputDevice8W( iface ); @@ -1210,6 +1230,8 @@ static HRESULT WINAPI hid_joystick_SendForceFeedbackCommand( IDirectInputDevice8 hr = DIERR_NOTEXCLUSIVEACQUIRED; else { + if (command == DISFFC_RESET) IDirectInputDevice8_EnumCreatedEffectObjects( iface, unload_effect_object, NULL, 0 ); + count = 1; status = HidP_InitializeReportForID( HidP_Output, report->id, impl->preparsed, report_buf, report_len ); if (status != HIDP_STATUS_SUCCESS) hr = status; @@ -1875,7 +1897,11 @@ static BOOL init_pid_caps( struct hid_joystick *impl, struct hid_value_caps *cap if (instance->wUsage == PID_USAGE_DURATION) effect_update->duration_caps = caps; if (instance->wUsage == PID_USAGE_GAIN) + { + caps->physical_min = 0; + caps->physical_max = 10000; effect_update->gain_caps = caps; + } if (instance->wUsage == PID_USAGE_SAMPLE_PERIOD) effect_update->sample_period_caps = caps; if (instance->wUsage == PID_USAGE_START_DELAY) @@ -1895,6 +1921,8 @@ static BOOL init_pid_caps( struct hid_joystick *impl, struct hid_value_caps *cap if (instance->wCollectionNumber == effect_update->direction_coll) { SET_REPORT_ID( effect_update ); + caps->physical_min = 0; + caps->physical_max = 36000 - 36000 / (caps->logical_max - caps->logical_min); if (effect_update->direction_count >= 6) FIXME( "more than 6 PID directions detected\n" ); else effect_update->direction_caps[effect_update->direction_count] = caps; effect_update->direction_count++; @@ -2089,6 +2117,7 @@ static ULONG WINAPI hid_joystick_effect_Release( IDirectInputEffect *iface ) TRACE( "iface %p, ref %u.\n", iface, ref ); if (!ref) { + IDirectInputEffect_Unload( iface ); EnterCriticalSection( &impl->joystick->base.crit ); list_remove( &impl->entry ); LeaveCriticalSection( &impl->joystick->base.crit ); @@ -2568,16 +2597,136 @@ static HRESULT WINAPI hid_joystick_effect_GetEffectStatus( IDirectInputEffect *i return DIERR_UNSUPPORTED; }
+static void set_parameter_value( struct hid_joystick_effect *impl, char *report_buf, + struct hid_value_caps *caps, LONG value ) +{ + ULONG report_len = impl->joystick->caps.OutputReportByteLength; + PHIDP_PREPARSED_DATA preparsed = impl->joystick->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 (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 %#x\n", + caps->usage_page, caps->usage_min, status ); +} + +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; + exp = caps->units_exp; + if (caps->units != 0x1003) WARN( "unknown time unit caps %x\n", caps->units ); + else if (exp < -6) while (exp++ < -6) value *= 10; + else if (exp > -6) while (exp-- > -6) value /= 10; + set_parameter_value( impl, report_buf, caps, value ); +} + static HRESULT WINAPI hid_joystick_effect_Download( IDirectInputEffect *iface ) { - FIXME( "iface %p stub!\n", iface ); - return DIERR_UNSUPPORTED; + static const DWORD complete_mask = DIEP_AXES | DIEP_DIRECTION | DIEP_TYPESPECIFICPARAMS; + struct hid_joystick_effect *impl = impl_from_IDirectInputEffect( iface ); + struct pid_effect_update *effect_update = &impl->joystick->pid_effect_update; + ULONG report_len = impl->joystick->caps.OutputReportByteLength; + HANDLE device = impl->joystick->device; + struct hid_value_caps *caps; + DWORD i, tmp, count; + NTSTATUS status; + USAGE usage; + HRESULT hr; + + TRACE( "iface %p\n", iface ); + + EnterCriticalSection( &impl->joystick->base.crit ); + if (impl->modified) hr = DI_OK; + else hr = DI_NOEFFECT; + + if (!impl->joystick->base.acquired || !(impl->joystick->base.dwCoopLevel & DISCL_EXCLUSIVE)) + hr = DIERR_NOTEXCLUSIVEACQUIRED; + else if ((impl->flags & complete_mask) != complete_mask) + hr = DIERR_INCOMPLETEEFFECT; + else if (!impl->index && !FAILED(hr = find_next_effect_id( impl->joystick, &impl->index ))) + { + status = HidP_SetUsageValue( HidP_Output, HID_USAGE_PAGE_PID, 0, PID_USAGE_EFFECT_BLOCK_INDEX, + impl->index, impl->joystick->preparsed, impl->effect_update_buf, report_len ); + if (status != HIDP_STATUS_SUCCESS) hr = status; + else hr = DI_OK; + } + + if (hr == DI_OK) + { + 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_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, + impl->params.dwStartDelay ); + set_parameter_value_us( impl, impl->effect_update_buf, effect_update->trigger_repeat_interval_caps, + impl->params.dwTriggerRepeatInterval ); + + if (impl->flags & DIEP_DIRECTION) + { + count = 1; + usage = PID_USAGE_DIRECTION_ENABLE; + status = HidP_SetUsages( HidP_Output, HID_USAGE_PAGE_PID, 0, &usage, &count, + impl->joystick->preparsed, impl->effect_update_buf, report_len ); + if (status != HIDP_STATUS_SUCCESS) WARN( "HidP_SetUsages returned %#x\n", status ); + + if (!effect_update->direction_count) WARN( "no PID effect direction caps found\n" ); + else for (i = 0; i < impl->params.cAxes - 1; ++i) + { + tmp = impl->directions[i] + (i == 0 ? 9000 : 0); + caps = effect_update->direction_caps[effect_update->direction_count - i - 1]; + set_parameter_value( impl, impl->effect_update_buf, caps, tmp % 36000 ); + } + } + + status = HidP_SetUsageValue( HidP_Output, HID_USAGE_PAGE_PID, 0, PID_USAGE_TRIGGER_BUTTON, + impl->params.dwTriggerButton, impl->joystick->preparsed, + impl->effect_update_buf, report_len ); + if (status != HIDP_STATUS_SUCCESS) WARN( "HidP_SetUsageValue returned %#x\n", status ); + + if (WriteFile( device, impl->effect_update_buf, report_len, NULL, NULL )) hr = DI_OK; + else hr = DIERR_INPUTLOST; + + impl->modified = FALSE; + } + LeaveCriticalSection( &impl->joystick->base.crit ); + + return hr; }
static HRESULT WINAPI hid_joystick_effect_Unload( IDirectInputEffect *iface ) { - FIXME( "iface %p stub!\n", iface ); - return DIERR_UNSUPPORTED; + struct hid_joystick_effect *impl = impl_from_IDirectInputEffect( iface ); + struct hid_joystick *joystick = impl->joystick; + HRESULT hr = DI_OK; + + TRACE( "iface %p\n", iface ); + + EnterCriticalSection( &joystick->base.crit ); + if (!impl->index) + hr = DI_NOEFFECT; + else if (!FAILED(hr = IDirectInputEffect_Stop( iface ))) + { + impl->joystick->effect_inuse[impl->index - 1] = FALSE; + impl->index = 0; + } + LeaveCriticalSection( &joystick->base.crit ); + + return hr; }
static HRESULT WINAPI hid_joystick_effect_Escape( IDirectInputEffect *iface, DIEFFESCAPE *escape ) diff --git a/dlls/dinput8/tests/hid.c b/dlls/dinput8/tests/hid.c index 70c55136602..9030d9cf157 100644 --- a/dlls/dinput8/tests/hid.c +++ b/dlls/dinput8/tests/hid.c @@ -5108,7 +5108,6 @@ static void test_periodic_effect( IDirectInputDevice8W *device, HANDLE file ) /* update effect */ { .code = IOCTL_HID_WRITE_REPORT, - .todo = TRUE, .report_id = 3, .report_len = 9, .report_buf = {0x03,0x01,0x01,0x08,0x01,0x00,0x06,0x00,0x01}, @@ -5116,7 +5115,6 @@ static void test_periodic_effect( IDirectInputDevice8W *device, HANDLE file ) /* start command when DIEP_START is set */ { .code = IOCTL_HID_WRITE_REPORT, - .todo = TRUE, .report_id = 2, .report_len = 4, .report_buf = {0x02,0x01,0x01,0x01}, @@ -5125,7 +5123,6 @@ static void test_periodic_effect( IDirectInputDevice8W *device, HANDLE file ) struct hid_expect expect_start = { .code = IOCTL_HID_WRITE_REPORT, - .todo = TRUE, .report_id = 2, .report_len = 4, .report_buf = {0x02, 0x01, 0x01, 0x01}, @@ -5133,7 +5130,6 @@ static void test_periodic_effect( IDirectInputDevice8W *device, HANDLE file ) struct hid_expect expect_start_solo = { .code = IOCTL_HID_WRITE_REPORT, - .todo = TRUE, .report_id = 2, .report_len = 4, .report_buf = {0x02, 0x01, 0x02, 0x01}, @@ -5141,7 +5137,6 @@ static void test_periodic_effect( IDirectInputDevice8W *device, HANDLE file ) struct hid_expect expect_start_0 = { .code = IOCTL_HID_WRITE_REPORT, - .todo = TRUE, .report_id = 2, .report_len = 4, .report_buf = {0x02, 0x01, 0x01, 0x00}, @@ -5149,7 +5144,6 @@ static void test_periodic_effect( IDirectInputDevice8W *device, HANDLE file ) struct hid_expect expect_start_4 = { .code = IOCTL_HID_WRITE_REPORT, - .todo = TRUE, .report_id = 2, .report_len = 4, .report_buf = {0x02, 0x01, 0x01, 0x04}, @@ -5157,7 +5151,6 @@ static void test_periodic_effect( IDirectInputDevice8W *device, HANDLE file ) struct hid_expect expect_stop = { .code = IOCTL_HID_WRITE_REPORT, - .todo = TRUE, .report_id = 2, .report_len = 4, .report_buf = {0x02, 0x01, 0x03, 0x00}, @@ -5166,7 +5159,6 @@ static void test_periodic_effect( IDirectInputDevice8W *device, HANDLE file ) { { .code = IOCTL_HID_WRITE_REPORT, - .todo = TRUE, .report_id = 2, .report_len = 4, .report_buf = {0x02,0x01,0x03,0x00}, @@ -5420,7 +5412,6 @@ static void test_periodic_effect( IDirectInputDevice8W *device, HANDLE file ) ok( hr == DI_OK, "Unacquire returned: %#x\n", hr ); set_hid_expect( file, NULL, 0 ); hr = IDirectInputEffect_Download( effect ); - todo_wine ok( hr == DIERR_NOTEXCLUSIVEACQUIRED, "Download returned %#x\n", hr ); set_hid_expect( file, &expect_dc_reset, sizeof(expect_dc_reset) ); hr = IDirectInputDevice8_Acquire( device ); @@ -5428,10 +5419,8 @@ static void test_periodic_effect( IDirectInputDevice8W *device, HANDLE file ) set_hid_expect( file, NULL, 0 );
hr = IDirectInputEffect_Download( effect ); - todo_wine ok( hr == DIERR_INCOMPLETEEFFECT, "Download returned %#x\n", hr ); hr = IDirectInputEffect_Unload( effect ); - todo_wine ok( hr == DI_NOEFFECT, "Unload returned %#x\n", hr );
hr = IDirectInputEffect_SetParameters( effect, NULL, DIEP_NODOWNLOAD ); @@ -5472,10 +5461,8 @@ static void test_periodic_effect( IDirectInputDevice8W *device, HANDLE file ) check_member( desc, expect_desc_init, "%u", dwStartDelay );
hr = IDirectInputEffect_Download( effect ); - todo_wine ok( hr == DIERR_INCOMPLETEEFFECT, "Download returned %#x\n", hr ); hr = IDirectInputEffect_Unload( effect ); - todo_wine ok( hr == DI_NOEFFECT, "Unload returned %#x\n", hr );
hr = IDirectInputEffect_SetParameters( effect, &expect_desc, @@ -5498,10 +5485,8 @@ static void test_periodic_effect( IDirectInputDevice8W *device, HANDLE file ) check_member( desc, expect_desc, "%u", dwStartDelay );
hr = IDirectInputEffect_Download( effect ); - todo_wine ok( hr == DIERR_INCOMPLETEEFFECT, "Download returned %#x\n", hr ); hr = IDirectInputEffect_Unload( effect ); - todo_wine ok( hr == DI_NOEFFECT, "Unload returned %#x\n", hr );
desc.lpEnvelope = NULL; @@ -5525,10 +5510,8 @@ static void test_periodic_effect( IDirectInputDevice8W *device, HANDLE file ) check_member( envelope, expect_envelope, "%u", dwFadeTime );
hr = IDirectInputEffect_Download( effect ); - todo_wine ok( hr == DIERR_INCOMPLETEEFFECT, "Download returned %#x\n", hr ); hr = IDirectInputEffect_Unload( effect ); - todo_wine ok( hr == DI_NOEFFECT, "Unload returned %#x\n", hr );
desc.dwFlags = 0; @@ -5580,10 +5563,8 @@ static void test_periodic_effect( IDirectInputDevice8W *device, HANDLE file ) ok( desc.rgdwAxes[2] == 4, "got %#x expected %#x\n", desc.rgdwAxes[2], 4 );
hr = IDirectInputEffect_Download( effect ); - todo_wine ok( hr == DIERR_INCOMPLETEEFFECT, "Download returned %#x\n", hr ); hr = IDirectInputEffect_Unload( effect ); - todo_wine ok( hr == DI_NOEFFECT, "Unload returned %#x\n", hr );
desc.dwFlags = DIEFF_CARTESIAN; @@ -5632,10 +5613,8 @@ static void test_periodic_effect( IDirectInputDevice8W *device, HANDLE file ) ok( hr == DIERR_INVALIDPARAM, "GetParameters returned %#x\n", hr );
hr = IDirectInputEffect_Download( effect ); - todo_wine ok( hr == DIERR_INCOMPLETEEFFECT, "Download returned %#x\n", hr ); hr = IDirectInputEffect_Unload( effect ); - todo_wine ok( hr == DI_NOEFFECT, "Unload returned %#x\n", hr );
desc.cbTypeSpecificParams = 0; @@ -5662,12 +5641,10 @@ static void test_periodic_effect( IDirectInputDevice8W *device, HANDLE file )
set_hid_expect( file, expect_download, 3 * sizeof(struct hid_expect) ); hr = IDirectInputEffect_Download( effect ); - todo_wine ok( hr == DI_OK, "Download returned %#x\n", hr ); set_hid_expect( file, NULL, 0 );
hr = IDirectInputEffect_Download( effect ); - todo_wine ok( hr == DI_NOEFFECT, "Download returned %#x\n", hr );
hr = IDirectInputEffect_Start( effect, 1, 0xdeadbeef ); @@ -5675,55 +5652,46 @@ static void test_periodic_effect( IDirectInputDevice8W *device, HANDLE file )
set_hid_expect( file, &expect_start_solo, sizeof(expect_start_solo) ); hr = IDirectInputEffect_Start( effect, 1, DIES_SOLO ); - todo_wine ok( hr == DI_OK, "Start returned %#x\n", hr ); set_hid_expect( file, NULL, 0 );
set_hid_expect( file, &expect_stop, sizeof(expect_stop) ); hr = IDirectInputEffect_Stop( effect ); - todo_wine ok( hr == DI_OK, "Stop returned %#x\n", hr ); set_hid_expect( file, NULL, 0 );
set_hid_expect( file, &expect_start, sizeof(expect_start) ); hr = IDirectInputEffect_Start( effect, 1, 0 ); - todo_wine ok( hr == DI_OK, "Start returned %#x\n", hr ); set_hid_expect( file, NULL, 0 );
set_hid_expect( file, &expect_start_4, sizeof(expect_start_4) ); hr = IDirectInputEffect_Start( effect, 4, 0 ); - todo_wine ok( hr == DI_OK, "Start returned %#x\n", hr ); set_hid_expect( file, NULL, 0 );
set_hid_expect( file, &expect_start_0, sizeof(expect_start_4) ); hr = IDirectInputEffect_Start( effect, 0, 0 ); - todo_wine ok( hr == DI_OK, "Start returned %#x\n", hr ); set_hid_expect( file, NULL, 0 );
set_hid_expect( file, expect_unload, sizeof(struct hid_expect) ); hr = IDirectInputEffect_Unload( effect ); - todo_wine ok( hr == DI_OK, "Unload returned %#x\n", hr ); set_hid_expect( file, NULL, 0 );
set_hid_expect( file, expect_download, 4 * sizeof(struct hid_expect) ); hr = IDirectInputEffect_SetParameters( effect, &expect_desc, DIEP_START ); - todo_wine ok( hr == DI_OK, "SetParameters returned %#x\n", hr ); set_hid_expect( file, NULL, 0 );
set_hid_expect( file, expect_unload, sizeof(struct hid_expect) ); hr = IDirectInputEffect_Unload( effect ); - todo_wine ok( hr == DI_OK, "Unload returned %#x\n", hr ); set_hid_expect( file, NULL, 0 );
set_hid_expect( file, expect_download, 3 * sizeof(struct hid_expect) ); hr = IDirectInputEffect_Download( effect ); - todo_wine ok( hr == DI_OK, "Download returned %#x\n", hr ); set_hid_expect( file, NULL, 0 );
@@ -5743,7 +5711,6 @@ static void test_periodic_effect( IDirectInputDevice8W *device, HANDLE file ) set_hid_expect( file, NULL, 0 );
hr = IDirectInputEffect_Unload( effect ); - todo_wine ok( hr == DI_NOEFFECT, "Unload returned %#x\n", hr );
ref = IDirectInputEffect_Release( effect );