Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/dinput8/tests/hid.c | 587 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 587 insertions(+)
diff --git a/dlls/dinput8/tests/hid.c b/dlls/dinput8/tests/hid.c index 9e21ccdb713..564ae7335e8 100644 --- a/dlls/dinput8/tests/hid.c +++ b/dlls/dinput8/tests/hid.c @@ -8029,6 +8029,591 @@ done: winetest_pop_context(); }
+static void test_device_managed_effect(void) +{ +#include "psh_hid_macros.h" + const unsigned char report_descriptor[] = { + USAGE_PAGE(1, HID_USAGE_PAGE_GENERIC), + USAGE(1, HID_USAGE_GENERIC_JOYSTICK), + COLLECTION(1, Application), + USAGE(1, HID_USAGE_GENERIC_JOYSTICK), + COLLECTION(1, Report), + REPORT_ID(1, 1), + + USAGE(1, HID_USAGE_GENERIC_X), + USAGE(1, HID_USAGE_GENERIC_Y), + USAGE(1, HID_USAGE_GENERIC_Z), + LOGICAL_MINIMUM(1, 0), + LOGICAL_MAXIMUM(1, 0x7f), + PHYSICAL_MINIMUM(1, 0), + PHYSICAL_MAXIMUM(1, 0x7f), + REPORT_SIZE(1, 8), + REPORT_COUNT(1, 3), + INPUT(1, Data|Var|Abs), + + USAGE_PAGE(1, HID_USAGE_PAGE_BUTTON), + USAGE_MINIMUM(1, 1), + USAGE_MAXIMUM(1, 2), + LOGICAL_MINIMUM(1, 0), + LOGICAL_MAXIMUM(1, 1), + PHYSICAL_MINIMUM(1, 0), + PHYSICAL_MAXIMUM(1, 1), + REPORT_SIZE(1, 1), + REPORT_COUNT(1, 2), + INPUT(1, Data|Var|Abs), + REPORT_COUNT(1, 6), + INPUT(1, Cnst|Var|Abs), + END_COLLECTION, + + USAGE_PAGE(1, HID_USAGE_PAGE_PID), + USAGE(1, PID_USAGE_STATE_REPORT), + COLLECTION(1, Report), + REPORT_ID(1, 2), + + USAGE(1, PID_USAGE_DEVICE_PAUSED), + USAGE(1, PID_USAGE_ACTUATORS_ENABLED), + USAGE(1, PID_USAGE_SAFETY_SWITCH), + USAGE(1, PID_USAGE_ACTUATOR_OVERRIDE_SWITCH), + USAGE(1, PID_USAGE_ACTUATOR_POWER), + LOGICAL_MINIMUM(1, 0), + LOGICAL_MAXIMUM(1, 1), + PHYSICAL_MINIMUM(1, 0), + PHYSICAL_MAXIMUM(1, 1), + REPORT_SIZE(1, 1), + REPORT_COUNT(1, 5), + INPUT(1, Data|Var|Abs), + REPORT_COUNT(1, 3), + INPUT(1, Cnst|Var|Abs), + + USAGE(1, PID_USAGE_EFFECT_PLAYING), + LOGICAL_MINIMUM(1, 0), + LOGICAL_MAXIMUM(1, 1), + PHYSICAL_MINIMUM(1, 0), + PHYSICAL_MAXIMUM(1, 1), + REPORT_SIZE(1, 1), + REPORT_COUNT(1, 1), + INPUT(1, Data|Var|Abs), + + USAGE(1, PID_USAGE_EFFECT_BLOCK_INDEX), + LOGICAL_MAXIMUM(1, 0x7f), + LOGICAL_MINIMUM(1, 0x00), + REPORT_SIZE(1, 7), + REPORT_COUNT(1, 1), + INPUT(1, Data|Var|Abs), + END_COLLECTION, + + USAGE_PAGE(1, HID_USAGE_PAGE_PID), + USAGE(1, PID_USAGE_DEVICE_CONTROL_REPORT), + COLLECTION(1, Report), + REPORT_ID(1, 1), + + USAGE(1, PID_USAGE_DEVICE_CONTROL), + COLLECTION(1, Logical), + USAGE(1, PID_USAGE_DC_DEVICE_RESET), + LOGICAL_MINIMUM(1, 1), + LOGICAL_MAXIMUM(1, 2), + PHYSICAL_MINIMUM(1, 1), + PHYSICAL_MAXIMUM(1, 2), + REPORT_SIZE(1, 8), + REPORT_COUNT(1, 1), + OUTPUT(1, Data|Ary|Abs), + END_COLLECTION, + END_COLLECTION, + + USAGE(1, PID_USAGE_EFFECT_OPERATION_REPORT), + COLLECTION(1, Report), + REPORT_ID(1, 2), + + USAGE(1, PID_USAGE_EFFECT_BLOCK_INDEX), + LOGICAL_MINIMUM(1, 0), + LOGICAL_MAXIMUM(1, 0x7f), + PHYSICAL_MINIMUM(1, 0), + PHYSICAL_MAXIMUM(1, 0x7f), + REPORT_SIZE(1, 8), + REPORT_COUNT(1, 1), + OUTPUT(1, Data|Var|Abs), + + USAGE(1, PID_USAGE_EFFECT_OPERATION), + COLLECTION(1, NamedArray), + USAGE(1, PID_USAGE_OP_EFFECT_START), + USAGE(1, PID_USAGE_OP_EFFECT_START_SOLO), + USAGE(1, PID_USAGE_OP_EFFECT_STOP), + LOGICAL_MINIMUM(1, 1), + LOGICAL_MAXIMUM(1, 3), + PHYSICAL_MINIMUM(1, 1), + PHYSICAL_MAXIMUM(1, 3), + REPORT_SIZE(1, 8), + REPORT_COUNT(1, 1), + OUTPUT(1, Data|Ary|Abs), + END_COLLECTION, + + USAGE(1, PID_USAGE_LOOP_COUNT), + LOGICAL_MINIMUM(1, 0), + LOGICAL_MAXIMUM(1, 0x7f), + PHYSICAL_MINIMUM(1, 0), + PHYSICAL_MAXIMUM(1, 0x7f), + REPORT_SIZE(1, 8), + REPORT_COUNT(1, 1), + OUTPUT(1, Data|Var|Abs), + END_COLLECTION, + + USAGE(1, PID_USAGE_SET_EFFECT_REPORT), + COLLECTION(1, Report), + REPORT_ID(1, 3), + + USAGE(1, PID_USAGE_EFFECT_BLOCK_INDEX), + LOGICAL_MINIMUM(1, 0), + LOGICAL_MAXIMUM(1, 0x7f), + PHYSICAL_MINIMUM(1, 0), + PHYSICAL_MAXIMUM(1, 0x7f), + REPORT_SIZE(1, 8), + REPORT_COUNT(1, 1), + OUTPUT(1, Data|Var|Abs), + + USAGE(1, PID_USAGE_EFFECT_TYPE), + COLLECTION(1, NamedArray), + USAGE(1, PID_USAGE_ET_SQUARE), + USAGE(1, PID_USAGE_ET_SINE), + USAGE(1, PID_USAGE_ET_SPRING), + LOGICAL_MINIMUM(1, 1), + LOGICAL_MAXIMUM(1, 3), + PHYSICAL_MINIMUM(1, 1), + PHYSICAL_MAXIMUM(1, 3), + REPORT_SIZE(1, 8), + REPORT_COUNT(1, 1), + OUTPUT(1, Data|Ary|Abs), + END_COLLECTION, + + USAGE(1, PID_USAGE_AXES_ENABLE), + COLLECTION(1, Logical), + USAGE(4, (HID_USAGE_PAGE_GENERIC << 16)|HID_USAGE_GENERIC_X), + USAGE(4, (HID_USAGE_PAGE_GENERIC << 16)|HID_USAGE_GENERIC_Y), + USAGE(4, (HID_USAGE_PAGE_GENERIC << 16)|HID_USAGE_GENERIC_Z), + LOGICAL_MINIMUM(1, 0), + LOGICAL_MAXIMUM(1, 1), + PHYSICAL_MINIMUM(1, 0), + PHYSICAL_MAXIMUM(1, 1), + REPORT_SIZE(1, 1), + REPORT_COUNT(1, 3), + OUTPUT(1, Data|Var|Abs), + END_COLLECTION, + USAGE(1, PID_USAGE_DIRECTION_ENABLE), + REPORT_COUNT(1, 1), + OUTPUT(1, Data|Var|Abs), + REPORT_COUNT(1, 4), + OUTPUT(1, Cnst|Var|Abs), + + USAGE(1, PID_USAGE_DURATION), + USAGE(1, PID_USAGE_START_DELAY), + UNIT(2, 0x1003), /* Eng Lin:Time */ + UNIT_EXPONENT(1, -3), /* 10^-3 */ + LOGICAL_MINIMUM(1, 0), + LOGICAL_MAXIMUM(2, 0x7fff), + PHYSICAL_MINIMUM(1, 0), + PHYSICAL_MAXIMUM(2, 0x7fff), + REPORT_SIZE(1, 16), + REPORT_COUNT(1, 2), + OUTPUT(1, Data|Var|Abs), + UNIT(1, 0), + UNIT_EXPONENT(1, 0), + + USAGE(1, PID_USAGE_TRIGGER_BUTTON), + LOGICAL_MINIMUM(1, 1), + LOGICAL_MAXIMUM(1, 0x08), + PHYSICAL_MINIMUM(1, 1), + PHYSICAL_MAXIMUM(1, 0x08), + REPORT_SIZE(1, 8), + REPORT_COUNT(1, 1), + OUTPUT(1, Data|Var|Abs), + + USAGE(1, PID_USAGE_DIRECTION), + COLLECTION(1, Logical), + USAGE(4, (HID_USAGE_PAGE_ORDINAL << 16)|1), + USAGE(4, (HID_USAGE_PAGE_ORDINAL << 16)|2), + UNIT(1, 0x14), /* Eng Rot:Angular Pos */ + UNIT_EXPONENT(1, -2), /* 10^-2 */ + LOGICAL_MINIMUM(1, 0), + LOGICAL_MAXIMUM(2, 0x00ff), + PHYSICAL_MINIMUM(1, 0), + PHYSICAL_MAXIMUM(4, 0x00008ca0), + UNIT(1, 0), + REPORT_SIZE(1, 8), + REPORT_COUNT(1, 2), + OUTPUT(1, Data|Var|Abs), + UNIT_EXPONENT(1, 0), + UNIT(1, 0), + END_COLLECTION, + END_COLLECTION, + + USAGE(1, PID_USAGE_SET_CONDITION_REPORT), + COLLECTION(1, Logical), + REPORT_ID(1, 7), + + USAGE(1, PID_USAGE_TYPE_SPECIFIC_BLOCK_OFFSET), + COLLECTION(1, Logical), + USAGE(4, (HID_USAGE_PAGE_ORDINAL << 16)|1), + USAGE(4, (HID_USAGE_PAGE_ORDINAL << 16)|2), + LOGICAL_MINIMUM(1, 0), + LOGICAL_MAXIMUM(1, 1), + PHYSICAL_MINIMUM(1, 0), + PHYSICAL_MAXIMUM(1, 1), + REPORT_SIZE(1, 2), + REPORT_COUNT(1, 2), + OUTPUT(1, Data|Var|Abs), + END_COLLECTION, + REPORT_SIZE(1, 4), + REPORT_COUNT(1, 1), + OUTPUT(1, Cnst|Var|Abs), + + USAGE(1, PID_USAGE_CP_OFFSET), + LOGICAL_MINIMUM(1, 0x80), + LOGICAL_MAXIMUM(1, 0x7f), + PHYSICAL_MINIMUM(2, 0xd8f0), + PHYSICAL_MAXIMUM(2, 0x2710), + REPORT_SIZE(1, 8), + REPORT_COUNT(1, 1), + OUTPUT(1, Data|Var|Abs), + + USAGE(1, PID_USAGE_POSITIVE_COEFFICIENT), + USAGE(1, PID_USAGE_NEGATIVE_COEFFICIENT), + LOGICAL_MINIMUM(1, 0x80), + LOGICAL_MAXIMUM(1, 0x7f), + PHYSICAL_MINIMUM(2, 0xd8f0), + PHYSICAL_MAXIMUM(2, 0x2710), + REPORT_SIZE(1, 8), + REPORT_COUNT(1, 2), + OUTPUT(1, Data|Var|Abs), + + USAGE(1, PID_USAGE_POSITIVE_SATURATION), + USAGE(1, PID_USAGE_NEGATIVE_SATURATION), + LOGICAL_MINIMUM(1, 0), + LOGICAL_MAXIMUM(2, 0x00ff), + PHYSICAL_MINIMUM(1, 0), + PHYSICAL_MAXIMUM(2, 0x2710), + REPORT_SIZE(1, 8), + REPORT_COUNT(1, 2), + OUTPUT(1, Data|Var|Abs), + + USAGE(1, PID_USAGE_DEAD_BAND), + LOGICAL_MINIMUM(1, 0), + LOGICAL_MAXIMUM(2, 0x00ff), + PHYSICAL_MINIMUM(1, 0), + PHYSICAL_MAXIMUM(2, 0x2710), + REPORT_SIZE(1, 8), + REPORT_COUNT(1, 1), + OUTPUT(1, Data|Var|Abs), + END_COLLECTION, + + USAGE(1, PID_USAGE_CREATE_NEW_EFFECT_REPORT), + COLLECTION(1, Logical), + REPORT_ID(1, 9), + + USAGE(1, PID_USAGE_EFFECT_TYPE), + COLLECTION(1, NamedArray), + USAGE(1, PID_USAGE_ET_SQUARE), + USAGE(1, PID_USAGE_ET_SINE), + USAGE(1, PID_USAGE_ET_SPRING), + LOGICAL_MINIMUM(1, 1), + LOGICAL_MAXIMUM(1, 3), + PHYSICAL_MINIMUM(1, 1), + PHYSICAL_MAXIMUM(1, 3), + REPORT_SIZE(1, 8), + REPORT_COUNT(1, 1), + FEATURE(1, Data|Ary|Abs), + END_COLLECTION, + END_COLLECTION, + + USAGE(1, PID_USAGE_BLOCK_LOAD_REPORT), + COLLECTION(1, Logical), + REPORT_ID(1, 10), + + USAGE(1, PID_USAGE_EFFECT_BLOCK_INDEX), + LOGICAL_MINIMUM(1, 1), + LOGICAL_MAXIMUM(1, 0x7f), + PHYSICAL_MINIMUM(1, 1), + PHYSICAL_MAXIMUM(1, 0x7f), + REPORT_SIZE(1, 8), + REPORT_COUNT(1, 1), + FEATURE(1, Data|Var|Abs), + + USAGE(1, PID_USAGE_BLOCK_LOAD_STATUS), + COLLECTION(1, NamedArray), + USAGE(1, PID_USAGE_BLOCK_LOAD_SUCCESS), + USAGE(1, PID_USAGE_BLOCK_LOAD_FULL), + USAGE(1, PID_USAGE_BLOCK_LOAD_ERROR), + LOGICAL_MINIMUM(1, 1), + LOGICAL_MAXIMUM(1, 3), + PHYSICAL_MINIMUM(1, 1), + PHYSICAL_MAXIMUM(1, 3), + REPORT_SIZE(1, 8), + REPORT_COUNT(1, 1), + FEATURE(1, Data|Ary|Abs), + END_COLLECTION, + END_COLLECTION, + + USAGE(1, PID_USAGE_BLOCK_FREE_REPORT), + COLLECTION(1, Logical), + REPORT_ID(1, 11), + + USAGE(1, PID_USAGE_EFFECT_BLOCK_INDEX), + LOGICAL_MINIMUM(1, 1), + LOGICAL_MAXIMUM(1, 0x7f), + PHYSICAL_MINIMUM(1, 1), + PHYSICAL_MAXIMUM(1, 0x7f), + REPORT_SIZE(1, 8), + REPORT_COUNT(1, 1), + OUTPUT(1, Data|Var|Abs), + END_COLLECTION, + + USAGE(1, PID_USAGE_POOL_REPORT), + COLLECTION(1, Logical), + REPORT_ID(1, 12), + + USAGE(1, PID_USAGE_DEVICE_MANAGED_POOL), + LOGICAL_MINIMUM(1, 0), + LOGICAL_MAXIMUM(1, 1), + PHYSICAL_MINIMUM(1, 0), + PHYSICAL_MAXIMUM(1, 1), + REPORT_SIZE(1, 1), + REPORT_COUNT(1, 8), + FEATURE(1, Data|Var|Abs), + END_COLLECTION, + END_COLLECTION, + }; +#include "pop_hid_macros.h" + + static const HIDP_CAPS hid_caps = + { + .InputReportByteLength = 5, + }; + struct hid_expect expect_reset[] = + { + { + .code = IOCTL_HID_WRITE_REPORT, + .report_id = 1, + .report_len = 2, + .report_buf = {1, 0x01}, + }, + }; + struct hid_expect expect_create[] = + { + /* create new effect */ + { + .code = IOCTL_HID_SET_FEATURE, + .report_id = 9, + .report_len = 2, + .report_buf = {9,0x03}, + .todo = TRUE, + }, + /* block load */ + { + .code = IOCTL_HID_GET_FEATURE, + .report_id = 10, + .report_len = 3, + .report_buf = {10,0x01,0x01}, + .todo = TRUE, + }, + /* set condition */ + { + .code = IOCTL_HID_WRITE_REPORT, + .report_id = 7, + .report_len = 8, + .report_buf = {0x07,0x00,0xf9,0x19,0xd9,0xff,0xff,0x99}, + }, + /* set condition */ + { + .code = IOCTL_HID_WRITE_REPORT, + .report_id = 7, + .report_len = 8, + .report_buf = {0x07,0x00,0x4c,0x3f,0xcc,0x4c,0x33,0x19}, + }, + /* update effect */ + { + .code = IOCTL_HID_WRITE_REPORT, + .report_id = 3, + .report_len = 11, + .report_buf = {0x03,0x01,0x03,0x08,0x01,0x00,0x06,0x00,0x01,0x55,0x00}, + }, + }; + struct hid_expect expect_destroy[] = + { + /* effect operation */ + { + .code = IOCTL_HID_WRITE_REPORT, + .report_id = 2, + .report_len = 4, + .report_buf = {0x02,0x01,0x03,0x00}, + }, + /* block free */ + { + .code = IOCTL_HID_WRITE_REPORT, + .report_id = 11, + .report_len = 2, + .report_buf = {11,0x01}, + .todo = TRUE, + }, + }; + static const DWORD expect_axes[3] = + { + DIDFT_ABSAXIS | DIDFT_MAKEINSTANCE( 0 ) | DIDFT_FFACTUATOR, + DIDFT_ABSAXIS | DIDFT_MAKEINSTANCE( 2 ) | DIDFT_FFACTUATOR, + DIDFT_ABSAXIS | DIDFT_MAKEINSTANCE( 1 ) | DIDFT_FFACTUATOR, + }; + static const LONG expect_directions[3] = { + +3000, + 0, + 0, + }; + static const DIENVELOPE expect_envelope = + { + .dwSize = sizeof(DIENVELOPE), + .dwAttackLevel = 1000, + .dwAttackTime = 2000, + .dwFadeLevel = 3000, + .dwFadeTime = 4000, + }; + static const DICONDITION expect_condition[3] = + { + { + .lOffset = -500, + .lPositiveCoefficient = 2000, + .lNegativeCoefficient = -3000, + .dwPositiveSaturation = -4000, + .dwNegativeSaturation = -5000, + .lDeadBand = 6000, + }, + { + .lOffset = 6000, + .lPositiveCoefficient = 5000, + .lNegativeCoefficient = -4000, + .dwPositiveSaturation = 3000, + .dwNegativeSaturation = 2000, + .lDeadBand = 1000, + }, + { + .lOffset = -7000, + .lPositiveCoefficient = -8000, + .lNegativeCoefficient = 9000, + .dwPositiveSaturation = 10000, + .dwNegativeSaturation = 11000, + .lDeadBand = -12000, + }, + }; + const DIEFFECT expect_desc = + { + .dwSize = sizeof(DIEFFECT_DX6), + .dwFlags = DIEFF_SPHERICAL | DIEFF_OBJECTIDS, + .dwDuration = 1000, + .dwSamplePeriod = 2000, + .dwGain = 3000, + .dwTriggerButton = DIDFT_PSHBUTTON | DIDFT_MAKEINSTANCE( 0 ) | DIDFT_FFEFFECTTRIGGER, + .dwTriggerRepeatInterval = 5000, + .cAxes = 2, + .rgdwAxes = (void *)expect_axes, + .rglDirection = (void *)expect_directions, + .lpEnvelope = (void *)&expect_envelope, + .cbTypeSpecificParams = 2 * sizeof(DICONDITION), + .lpvTypeSpecificParams = (void *)expect_condition, + .dwStartDelay = 6000, + }; + DIPROPGUIDANDPATH prop_guid_path = + { + .diph = + { + .dwSize = sizeof(DIPROPGUIDANDPATH), + .dwHeaderSize = sizeof(DIPROPHEADER), + .dwHow = DIPH_DEVICE, + }, + }; + DIPROPDWORD prop_dword = + { + .diph = + { + .dwSize = sizeof(DIPROPDWORD), + .dwHeaderSize = sizeof(DIPROPHEADER), + .dwHow = DIPH_DEVICE, + }, + }; + DIDEVICEINSTANCEW devinst = {.dwSize = sizeof(DIDEVICEINSTANCEW)}; + WCHAR cwd[MAX_PATH], tempdir[MAX_PATH]; + IDirectInputDevice8W *device; + IDirectInputEffect *effect; + HANDLE file; + DWORD flags; + HRESULT hr; + ULONG ref; + HWND hwnd; + + GetCurrentDirectoryW( ARRAY_SIZE(cwd), cwd ); + GetTempPathW( ARRAY_SIZE(tempdir), tempdir ); + SetCurrentDirectoryW( tempdir ); + + cleanup_registry_keys(); + if (!dinput_driver_start( report_descriptor, sizeof(report_descriptor), &hid_caps )) goto done; + if (FAILED(hr = create_dinput_device( DIRECTINPUT_VERSION, &devinst, &device ))) goto done; + + hr = IDirectInputDevice8_GetProperty( device, DIPROP_GUIDANDPATH, &prop_guid_path.diph ); + ok( hr == DI_OK, "GetProperty DIPROP_GUIDANDPATH returned %#x\n", hr ); + file = CreateFileW( prop_guid_path.wszPath, FILE_READ_ACCESS | FILE_WRITE_ACCESS, + FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, + FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING, NULL ); + ok( file != INVALID_HANDLE_VALUE, "got error %u\n", GetLastError() ); + + hwnd = CreateWindowW( L"static", L"dinput", WS_OVERLAPPEDWINDOW | WS_VISIBLE, 10, 10, 200, 200, + NULL, NULL, NULL, NULL ); + + hr = IDirectInputDevice8_SetCooperativeLevel( device, hwnd, DISCL_BACKGROUND | DISCL_EXCLUSIVE ); + ok( hr == DI_OK, "SetCooperativeLevel returned: %#x\n", hr ); + hr = IDirectInputDevice8_SetDataFormat( device, &c_dfDIJoystick2 ); + ok( hr == DI_OK, "SetDataFormat returned: %#x\n", hr ); + + hr = IDirectInputDevice8_GetProperty( device, DIPROP_FFLOAD, &prop_dword.diph ); + todo_wine + ok( hr == DIERR_NOTEXCLUSIVEACQUIRED, "GetProperty DIPROP_FFLOAD returned %#x\n", hr ); + + set_hid_expect( file, expect_reset, sizeof(expect_reset) ); + hr = IDirectInputDevice8_Acquire( device ); + ok( hr == DI_OK, "Acquire returned: %#x\n", hr ); + set_hid_expect( file, NULL, 0 ); + + hr = IDirectInputDevice8_CreateEffect( device, &GUID_Spring, NULL, &effect, NULL ); + ok( hr == DI_OK, "CreateEffect returned %#x\n", hr ); + + flags = DIEP_ALLPARAMS; + hr = IDirectInputEffect_SetParameters( effect, &expect_desc, flags | DIEP_NODOWNLOAD ); + ok( hr == DI_DOWNLOADSKIPPED, "SetParameters returned %#x\n", hr ); + + set_hid_expect( file, expect_create, sizeof(expect_create) ); + hr = IDirectInputEffect_Download( effect ); + ok( hr == DI_OK, "Download returned %#x\n", hr ); + set_hid_expect( file, NULL, 0 ); + + set_hid_expect( file, expect_destroy, sizeof(expect_destroy) ); + hr = IDirectInputEffect_Unload( effect ); + ok( hr == DI_OK, "Unload returned %#x\n", hr ); + set_hid_expect( file, NULL, 0 ); + + ref = IDirectInputEffect_Release( effect ); + ok( ref == 0, "Release returned %d\n", ref ); + + set_hid_expect( file, expect_reset, sizeof(expect_reset) ); + hr = IDirectInputDevice8_Unacquire( device ); + ok( hr == DI_OK, "Unacquire returned: %#x\n", hr ); + set_hid_expect( file, NULL, 0 ); + + ref = IDirectInputDevice8_Release( device ); + ok( ref == 0, "Release returned %d\n", ref ); + + DestroyWindow( hwnd ); + CloseHandle( file ); + +done: + pnp_driver_stop(); + cleanup_registry_keys(); + SetCurrentDirectoryW( cwd ); + winetest_pop_context(); +} + START_TEST( hid ) { HANDLE mapping; @@ -8078,6 +8663,8 @@ START_TEST( hid ) test_force_feedback_joystick( 0x500 ); test_force_feedback_joystick( 0x700 ); test_force_feedback_joystick( 0x800 ); + + test_device_managed_effect(); } CoUninitialize();
From: Ivo Ivanov logos128@gmail.com
Signed-off-by: Ivo Ivanov logos128@gmail.com Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/dinput/joystick_hid.c | 73 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 71 insertions(+), 2 deletions(-)
diff --git a/dlls/dinput/joystick_hid.c b/dlls/dinput/joystick_hid.c index 70c761267f7..9968da7d897 100644 --- a/dlls/dinput/joystick_hid.c +++ b/dlls/dinput/joystick_hid.c @@ -132,6 +132,33 @@ struct pid_device_gain struct hid_value_caps *device_gain_caps; };
+struct pid_device_pool +{ + BYTE id; + ULONG collection; + struct hid_value_caps *device_managed_caps; +}; + +struct pid_block_free +{ + BYTE id; + ULONG collection; +}; + +struct pid_block_load +{ + BYTE id; + ULONG collection; + ULONG status_coll; +}; + +struct pid_new_effect +{ + BYTE id; + ULONG collection; + ULONG type_coll; +}; + struct hid_joystick { struct dinput_device base; @@ -161,6 +188,10 @@ struct hid_joystick struct pid_set_constant_force pid_set_constant_force; struct pid_set_ramp_force pid_set_ramp_force; struct pid_device_gain pid_device_gain; + struct pid_device_pool pid_device_pool; + struct pid_block_free pid_block_free; + struct pid_block_load pid_block_load; + struct pid_new_effect pid_new_effect; };
static inline struct hid_joystick *impl_from_IDirectInputDevice8W( IDirectInputDevice8W *iface ) @@ -441,7 +472,7 @@ static void set_axis_type( DIDEVICEOBJECTINSTANCEW *instance, BOOL *seen, DWORD static BOOL enum_objects( struct hid_joystick *impl, const DIPROPHEADER *filter, DWORD flags, enum_object_callback callback, void *data ) { - DWORD collection = 0, object = 0, axis = 0, button = 0, pov = 0, value_ofs = 0, button_ofs = 0, j, len; + DWORD collection = 0, object = 0, axis = 0, button = 0, pov = 0, value_ofs = 0, button_ofs = 0, j, count, len; struct hid_preparsed_data *preparsed = (struct hid_preparsed_data *)impl->preparsed; DIDEVICEOBJECTINSTANCEW instance = {.dwSize = sizeof(DIDEVICEOBJECTINSTANCEW)}; struct hid_value_caps *caps, *caps_end, *nary, *nary_end, *effect_caps; @@ -573,7 +604,8 @@ static BOOL enum_objects( struct hid_joystick *impl, const DIPROPHEADER *filter, } }
- for (caps = HID_OUTPUT_VALUE_CAPS( preparsed ), caps_end = caps + preparsed->output_caps_count; + count = preparsed->output_caps_count + preparsed->feature_caps_count; + for (caps = HID_OUTPUT_VALUE_CAPS( preparsed ), caps_end = caps + count; caps != caps_end; ++caps) { if (!caps->usage_page) continue; @@ -1515,6 +1547,10 @@ static BOOL init_pid_reports( struct hid_joystick *impl, struct hid_value_caps * struct pid_set_periodic *set_periodic = &impl->pid_set_periodic; struct pid_set_envelope *set_envelope = &impl->pid_set_envelope; struct pid_device_gain *device_gain = &impl->pid_device_gain; + struct pid_device_pool *device_pool = &impl->pid_device_pool; + struct pid_block_free *block_free = &impl->pid_block_free; + struct pid_block_load *block_load = &impl->pid_block_load; + struct pid_new_effect *new_effect = &impl->pid_new_effect;
#define SET_COLLECTION( rep ) \ do \ @@ -1546,15 +1582,22 @@ static BOOL init_pid_reports( struct hid_joystick *impl, struct hid_value_caps * case PID_USAGE_SET_CONSTANT_FORCE_REPORT: SET_COLLECTION( set_constant_force ); break; case PID_USAGE_SET_RAMP_FORCE_REPORT: SET_COLLECTION( set_ramp_force ); break; case PID_USAGE_DEVICE_GAIN_REPORT: SET_COLLECTION( device_gain ); break; + case PID_USAGE_POOL_REPORT: SET_COLLECTION( device_pool ); break; + case PID_USAGE_BLOCK_FREE_REPORT: SET_COLLECTION( block_free ); break; + case PID_USAGE_BLOCK_LOAD_REPORT: SET_COLLECTION( block_load ); break; + case PID_USAGE_CREATE_NEW_EFFECT_REPORT: SET_COLLECTION( new_effect ); break;
case PID_USAGE_DEVICE_CONTROL: SET_SUB_COLLECTION( device_control, control_coll ); break; case PID_USAGE_EFFECT_OPERATION: SET_SUB_COLLECTION( effect_control, control_coll ); break; case PID_USAGE_EFFECT_TYPE: if (instance->wCollectionNumber == effect_update->collection) SET_SUB_COLLECTION( effect_update, type_coll ); + else if (instance->wCollectionNumber == new_effect->collection) + SET_SUB_COLLECTION( new_effect, type_coll ); break; case PID_USAGE_AXES_ENABLE: SET_SUB_COLLECTION( effect_update, axes_coll ); break; case PID_USAGE_DIRECTION: SET_SUB_COLLECTION( effect_update, direction_coll ); break; + case PID_USAGE_BLOCK_LOAD_STATUS: SET_SUB_COLLECTION( block_load, status_coll ); break; } }
@@ -1576,6 +1619,10 @@ static BOOL init_pid_caps( struct hid_joystick *impl, struct hid_value_caps *cap struct pid_set_periodic *set_periodic = &impl->pid_set_periodic; struct pid_set_envelope *set_envelope = &impl->pid_set_envelope; struct pid_device_gain *device_gain = &impl->pid_device_gain; + struct pid_device_pool *device_pool = &impl->pid_device_pool; + struct pid_block_free *block_free = &impl->pid_block_free; + struct pid_block_load *block_load = &impl->pid_block_load; + struct pid_new_effect *new_effect = &impl->pid_new_effect;
if (!(instance->dwType & DIDFT_OUTPUT)) return DIENUM_CONTINUE;
@@ -1752,6 +1799,22 @@ static BOOL init_pid_caps( struct hid_joystick *impl, struct hid_value_caps *cap device_gain->device_gain_caps = caps; } } + if (instance->wCollectionNumber == device_pool->collection) + { + SET_REPORT_ID( device_pool ); + if (instance->wUsage == PID_USAGE_DEVICE_MANAGED_POOL) + device_pool->device_managed_caps = caps; + } + if (instance->wCollectionNumber == block_free->collection) + SET_REPORT_ID( block_free ); + if (instance->wCollectionNumber == block_load->collection) + SET_REPORT_ID( block_load ); + if (instance->wCollectionNumber == block_load->status_coll) + SET_REPORT_ID( block_load ); + if (instance->wCollectionNumber == new_effect->collection) + SET_REPORT_ID( new_effect ); + if (instance->wCollectionNumber == new_effect->type_coll) + SET_REPORT_ID( new_effect );
#undef SET_REPORT_ID
@@ -1850,6 +1913,12 @@ HRESULT hid_joystick_create_device( IDirectInputImpl *dinput, const GUID *guid, impl->pid_set_constant_force.collection ); TRACE( "set ramp force id %u, coll %u\n", impl->pid_set_ramp_force.id, impl->pid_set_ramp_force.collection ); TRACE( "device gain id %u, coll %u\n", impl->pid_device_gain.id, impl->pid_device_gain.collection ); + TRACE( "device pool id %u, coll %u\n", impl->pid_device_pool.id, impl->pid_device_pool.collection ); + TRACE( "block free id %u, coll %u\n", impl->pid_block_free.id, impl->pid_block_free.collection ); + TRACE( "block load id %u, coll %u, status_coll %u\n", impl->pid_block_load.id, + impl->pid_block_load.collection, impl->pid_block_load.status_coll ); + TRACE( "create new effect id %u, coll %u, type_coll %u\n", impl->pid_new_effect.id, + impl->pid_new_effect.collection, impl->pid_new_effect.type_coll );
if (impl->pid_device_control.id) {
From: Ivo Ivanov logos128@gmail.com
Required by the PID spec for devices with device managed blocks.
Signed-off-by: Ivo Ivanov logos128@gmail.com Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/dinput/joystick_hid.c | 4 ++++ 1 file changed, 4 insertions(+)
diff --git a/dlls/dinput/joystick_hid.c b/dlls/dinput/joystick_hid.c index 9968da7d897..a43a73fff17 100644 --- a/dlls/dinput/joystick_hid.c +++ b/dlls/dinput/joystick_hid.c @@ -2748,6 +2748,10 @@ static HRESULT WINAPI hid_joystick_effect_Download( IDirectInputEffect *iface ) case PID_USAGE_ET_FRICTION: for (i = 0; i < impl->params.cbTypeSpecificParams / sizeof(DICONDITION); ++i) { + status = HidP_SetUsageValue( HidP_Output, HID_USAGE_PAGE_PID, 0, PID_USAGE_PARAMETER_BLOCK_OFFSET, + i, impl->joystick->preparsed, impl->type_specific_buf, report_len ); + if (status != HIDP_STATUS_SUCCESS) WARN( "HidP_SetUsageValue %04x:%04x returned %#x\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,
From: Ivo Ivanov logos128@gmail.com Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/dinput/joystick_hid.c | 81 +++++++++++++++++++++++++++++++++----- dlls/dinput8/tests/hid.c | 3 -- 2 files changed, 72 insertions(+), 12 deletions(-)
diff --git a/dlls/dinput/joystick_hid.c b/dlls/dinput/joystick_hid.c index a43a73fff17..12ff10d7865 100644 --- a/dlls/dinput/joystick_hid.c +++ b/dlls/dinput/joystick_hid.c @@ -174,6 +174,7 @@ struct hid_joystick
char *input_report_buf; char *output_report_buf; + char *feature_report_buf; USAGE_AND_PAGE *usages_buf; ULONG usages_count;
@@ -401,15 +402,54 @@ static const WCHAR *object_usage_to_string( DIDEVICEOBJECTINSTANCEW *instance ) } }
-static HRESULT find_next_effect_id( struct hid_joystick *impl, ULONG *index ) +static HRESULT find_next_effect_id( struct hid_joystick *impl, DWORD *index, USAGE type ) { - ULONG i; + struct pid_device_pool *device_pool = &impl->pid_device_pool; + struct pid_new_effect *new_effect = &impl->pid_new_effect; + struct pid_block_load *block_load = &impl->pid_block_load; + ULONG i, count, report_len = impl->caps.FeatureReportByteLength; + NTSTATUS status; + USAGE usage;
- 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; + if (!device_pool->device_managed_caps) + { + 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; + } + else + { + status = HidP_InitializeReportForID( HidP_Feature, new_effect->id, impl->preparsed, + impl->feature_report_buf, report_len ); + if (status != HIDP_STATUS_SUCCESS) return status; + + count = 1; + status = HidP_SetUsages( HidP_Feature, HID_USAGE_PAGE_PID, new_effect->type_coll, + &type, &count, impl->preparsed, impl->feature_report_buf, report_len ); + if (status != HIDP_STATUS_SUCCESS) return status; + + if (!HidD_SetFeature( impl->device, impl->feature_report_buf, report_len )) return DIERR_INPUTLOST; + + status = HidP_InitializeReportForID( HidP_Feature, block_load->id, impl->preparsed, + impl->feature_report_buf, report_len ); + if (status != HIDP_STATUS_SUCCESS) return status; + + if (!HidD_GetFeature( impl->device, impl->feature_report_buf, report_len )) return DIERR_INPUTLOST; + + count = 1; + status = HidP_GetUsages( HidP_Feature, HID_USAGE_PAGE_PID, block_load->status_coll, + &usage, &count, impl->preparsed, impl->feature_report_buf, report_len ); + if (status != HIDP_STATUS_SUCCESS) return status; + + if (count != 1 || usage == PID_USAGE_BLOCK_LOAD_ERROR) return DIERR_INPUTLOST; + if (usage == PID_USAGE_BLOCK_LOAD_FULL) return DIERR_DEVICEFULL; + + status = HidP_GetUsageValue( HidP_Feature, HID_USAGE_PAGE_PID, 0, PID_USAGE_EFFECT_BLOCK_INDEX, + index, impl->preparsed, impl->feature_report_buf, report_len ); + if (status != HIDP_STATUS_SUCCESS) return status; + }
return DI_OK; } @@ -732,6 +772,7 @@ static void hid_joystick_release( IDirectInputDevice8W *iface ) if (!ref) { free( impl->usages_buf ); + free( impl->feature_report_buf ); free( impl->output_report_buf ); free( impl->input_report_buf ); HidD_FreePreparsedData( impl->preparsed ); @@ -1893,6 +1934,9 @@ HRESULT hid_joystick_create_device( IDirectInputImpl *dinput, const GUID *guid, size = impl->caps.OutputReportByteLength; if (!(buffer = malloc( size ))) goto failed; impl->output_report_buf = buffer; + size = impl->caps.FeatureReportByteLength; + if (!(buffer = malloc( size ))) goto failed; + impl->feature_report_buf = buffer; impl->usages_count = HidP_MaxUsageListLength( HidP_Input, 0, impl->preparsed ); size = impl->usages_count * sizeof(USAGE_AND_PAGE); if (!(usages = malloc( size ))) goto failed; @@ -2703,7 +2747,7 @@ static HRESULT WINAPI hid_joystick_effect_Download( IDirectInputEffect *iface ) hr = DIERR_NOTEXCLUSIVEACQUIRED; else if ((impl->flags & complete_mask) != complete_mask) hr = DIERR_INCOMPLETEEFFECT; - else if (!impl->index && SUCCEEDED(hr = find_next_effect_id( impl->joystick, &impl->index ))) + else if (!impl->index && SUCCEEDED(hr = find_next_effect_id( impl->joystick, &impl->index, impl->type ))) { if (!impl->type_specific_buf[0]) status = HIDP_STATUS_SUCCESS; else status = HidP_SetUsageValue( HidP_Output, HID_USAGE_PAGE_PID, 0, PID_USAGE_EFFECT_BLOCK_INDEX, @@ -2857,7 +2901,11 @@ static HRESULT WINAPI hid_joystick_effect_Unload( IDirectInputEffect *iface ) { struct hid_joystick_effect *impl = impl_from_IDirectInputEffect( iface ); struct hid_joystick *joystick = impl->joystick; + struct pid_device_pool *device_pool = &joystick->pid_device_pool; + struct pid_block_free *block_free = &joystick->pid_block_free; + ULONG report_len = joystick->caps.OutputReportByteLength; HRESULT hr = DI_OK; + NTSTATUS status;
TRACE( "iface %p\n", iface );
@@ -2866,7 +2914,22 @@ static HRESULT WINAPI hid_joystick_effect_Unload( IDirectInputEffect *iface ) hr = DI_NOEFFECT; else if (SUCCEEDED(hr = IDirectInputEffect_Stop( iface ))) { - impl->joystick->effect_inuse[impl->index - 1] = FALSE; + if (!device_pool->device_managed_caps) + joystick->effect_inuse[impl->index - 1] = FALSE; + else if (block_free->id) + { + status = HidP_InitializeReportForID( HidP_Output, block_free->id, joystick->preparsed, + joystick->output_report_buf, report_len ); + + if (status != HIDP_STATUS_SUCCESS) hr = status; + else status = HidP_SetUsageValue( HidP_Output, HID_USAGE_PAGE_PID, 0, PID_USAGE_EFFECT_BLOCK_INDEX, + impl->index, joystick->preparsed, joystick->output_report_buf, report_len ); + + if (status != HIDP_STATUS_SUCCESS) hr = status; + else if (WriteFile( joystick->device, joystick->output_report_buf, report_len, NULL, NULL )) hr = DI_OK; + else hr = DIERR_INPUTLOST; + } + impl->index = 0; } LeaveCriticalSection( &joystick->base.crit ); diff --git a/dlls/dinput8/tests/hid.c b/dlls/dinput8/tests/hid.c index 564ae7335e8..596d318acd9 100644 --- a/dlls/dinput8/tests/hid.c +++ b/dlls/dinput8/tests/hid.c @@ -8403,7 +8403,6 @@ static void test_device_managed_effect(void) .report_id = 9, .report_len = 2, .report_buf = {9,0x03}, - .todo = TRUE, }, /* block load */ { @@ -8411,7 +8410,6 @@ static void test_device_managed_effect(void) .report_id = 10, .report_len = 3, .report_buf = {10,0x01,0x01}, - .todo = TRUE, }, /* set condition */ { @@ -8450,7 +8448,6 @@ static void test_device_managed_effect(void) .report_id = 11, .report_len = 2, .report_buf = {11,0x01}, - .todo = TRUE, }, }; static const DWORD expect_axes[3] =
On 11/19/21 12:37, Rémi Bernon wrote:
Signed-off-by: Rémi Bernon rbernon@codeweavers.com
I think this series should apply against the previous one without conflicts, but if there is any please ignore this for now the first series is probably worth more.