[PATCH 11/11] dinput: Implement HID joystick IDirectInputEffect_(Download|Unload).

Rémi Bernon rbernon at codeweavers.com
Thu Oct 7 03:40:08 CDT 2021


Signed-off-by: Rémi Bernon <rbernon at 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 );
-- 
2.33.0




More information about the wine-devel mailing list