From: Tomasz Pakuła tomasz.pakula.oficjalny@gmail.com
--- dlls/dinput/tests/force_feedback.c | 747 ++++++++++++++++++++++++++++- 1 file changed, 746 insertions(+), 1 deletion(-)
diff --git a/dlls/dinput/tests/force_feedback.c b/dlls/dinput/tests/force_feedback.c index 14bf9575e8f..89caa86af9d 100644 --- a/dlls/dinput/tests/force_feedback.c +++ b/dlls/dinput/tests/force_feedback.c @@ -3659,6 +3659,750 @@ done: return device != NULL; }
+static void test_condition_effect_six_axes( IDirectInputDevice8W *device, HANDLE file ) +{ + struct hid_expect expect_create[] = + { + /* set condition */ + { + .code = IOCTL_HID_WRITE_REPORT, + .report_id = 7, + .report_len = 9, + .report_buf = {0x07,0x00,0x00,0x80,0x80,0x80,0x00,0x00,0x00}, + }, + /* set condition */ + { + .code = IOCTL_HID_WRITE_REPORT, + .report_id = 7, + .report_len = 9, + .report_buf = {0x07,0x00,0x00,0x7f,0x7f,0x7f,0xff,0xff,0xff}, + }, + /* set condition */ + { + .code = IOCTL_HID_WRITE_REPORT, + .report_id = 7, + .report_len = 9, + .report_buf = {0x07,0x00,0x00,0x80,0x7f,0x80,0xff,0xff,0x00}, + }, + /* set condition */ + { + .code = IOCTL_HID_WRITE_REPORT, + .report_id = 7, + .report_len = 9, + .report_buf = {0x07,0x00,0x00,0x7f,0xff,0x80,0x00,0xff,0xff}, + }, + /* set condition */ + { + .code = IOCTL_HID_WRITE_REPORT, + .report_id = 7, + .report_len = 9, + .report_buf = {0x07,0x00,0x00,0xff,0x7f,0xff,0xff,0x00,0x00}, + }, + /* set condition */ + { + .code = IOCTL_HID_WRITE_REPORT, + .report_id = 7, + .report_len = 9, + .report_buf = {0x07,0x00,0x00,0x7f,0x7f,0xff,0xff,0x00,0xff}, + }, + /* create effect */ + { + .code = IOCTL_HID_WRITE_REPORT, + .report_id = 3, + .report_len = 15, + .report_buf = {0x03,0x01,0x03,0x40,0x01,0x00,0x06,0x00,0x01,0x7f,0x2a,0xea,0xf5,0x1f,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}, + }, + }; + static const DWORD expect_axes[6] = + { + DIDFT_ABSAXIS | DIDFT_MAKEINSTANCE( 4 ) | DIDFT_FFACTUATOR, + DIDFT_ABSAXIS | DIDFT_MAKEINSTANCE( 2 ) | DIDFT_FFACTUATOR, + DIDFT_ABSAXIS | DIDFT_MAKEINSTANCE( 5 ) | DIDFT_FFACTUATOR, + DIDFT_ABSAXIS | DIDFT_MAKEINSTANCE( 0 ) | DIDFT_FFACTUATOR, + DIDFT_ABSAXIS | DIDFT_MAKEINSTANCE( 3 ) | DIDFT_FFACTUATOR, + DIDFT_ABSAXIS | DIDFT_MAKEINSTANCE( 1 ) | DIDFT_FFACTUATOR, + }; + static const LONG expect_directions[6] = + { + 9000, + 6000, + 33000, + 34500, + 4500, + 0, + }; + static const DIENVELOPE expect_envelope = + { + .dwSize = sizeof(DIENVELOPE), + .dwAttackLevel = 1000, + .dwAttackTime = 2000, + .dwFadeLevel = 3000, + .dwFadeTime = 4000, + }; + static const DICONDITION expect_condition[6] = + { + { + .lOffset = -10000, + .lPositiveCoefficient = -10000, + .lNegativeCoefficient = -10000, + .dwPositiveSaturation = 0, + .dwNegativeSaturation = 0, + .lDeadBand = 0, + }, + { + .lOffset = 10000, + .lPositiveCoefficient = 10000, + .lNegativeCoefficient = 10000, + .dwPositiveSaturation = 10000, + .dwNegativeSaturation = 10000, + .lDeadBand = 10000, + }, + { + .lOffset = -10000, + .lPositiveCoefficient = +10000, + .lNegativeCoefficient = -10000, + .dwPositiveSaturation = +10000, + .dwNegativeSaturation = -10000, + .lDeadBand = 0, + }, + { + .lOffset = +10000, + .lPositiveCoefficient = 0, + .lNegativeCoefficient = -10000, + .dwPositiveSaturation = 0, + .dwNegativeSaturation = -10000, + .lDeadBand = 20000, + }, + { + .lOffset = 0, + .lPositiveCoefficient = +10000, + .lNegativeCoefficient = 0, + .dwPositiveSaturation = +10000, + .dwNegativeSaturation = 0, + .lDeadBand = 0, + }, + { + .lOffset = 10000, + .lPositiveCoefficient = +10000, + .lNegativeCoefficient = 0, + .dwPositiveSaturation = +10000, + .dwNegativeSaturation = 0, + .lDeadBand = 20000, + }, + }; + 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 = 6, + .rgdwAxes = (void *)expect_axes, + .rglDirection = (void *)expect_directions, + .lpEnvelope = (void *)&expect_envelope, + .cbTypeSpecificParams = 6 * sizeof(DICONDITION), + .lpvTypeSpecificParams = (void *)expect_condition, + .dwStartDelay = 6000, + }; + struct check_created_effect_params check_params = {0}; + DIENVELOPE envelope = {.dwSize = sizeof(DIENVELOPE)}; + DICONDITION condition[6] = {{0}}; + IDirectInputEffect *effect; + LONG directions[6] = {0}; + DWORD axes[6] = {0}; + DIEFFECT desc = + { + .dwSize = sizeof(DIEFFECT_DX6), + .dwFlags = DIEFF_SPHERICAL | DIEFF_OBJECTIDS, + .cAxes = 6, + .rgdwAxes = axes, + .rglDirection = directions, + .lpEnvelope = &envelope, + .cbTypeSpecificParams = 6 * sizeof(DICONDITION), + .lpvTypeSpecificParams = condition, + }; + HRESULT hr; + ULONG ref; + GUID guid; + + set_hid_expect( file, expect_create, sizeof(expect_create) ); + hr = IDirectInputDevice8_CreateEffect( device, &GUID_Spring, &expect_desc, &effect, NULL ); + ok( hr == DI_OK, "CreateEffect returned %#lx\n", hr ); + set_hid_expect( file, NULL, 0 ); + + check_params.expect_effect = effect; + hr = IDirectInputDevice8_EnumCreatedEffectObjects( device, check_created_effect_objects, &check_params, 0 ); + ok( hr == DI_OK, "EnumCreatedEffectObjects returned %#lx\n", hr ); + ok( check_params.count == 1, "got count %lu, expected 1\n", check_params.count ); + + hr = IDirectInputEffect_GetEffectGuid( effect, &guid ); + ok( hr == DI_OK, "GetEffectGuid returned %#lx\n", hr ); + ok( IsEqualGUID( &guid, &GUID_Spring ), "got guid %s, expected %s\n", debugstr_guid( &guid ), + debugstr_guid( &GUID_Spring ) ); + + hr = IDirectInputEffect_GetParameters( effect, &desc, DIEP_ALLPARAMS ); + ok( hr == DI_OK, "GetParameters returned %#lx\n", hr ); + check_member( desc, expect_desc, "%lu", cAxes ); + check_member( desc, expect_desc, "%#lx", rgdwAxes[0] ); + check_member( desc, expect_desc, "%#lx", rgdwAxes[1] ); + check_member( desc, expect_desc, "%#lx", rgdwAxes[2] ); + check_member( desc, expect_desc, "%#lx", rgdwAxes[3] ); + check_member( desc, expect_desc, "%#lx", rgdwAxes[4] ); + check_member( desc, expect_desc, "%#lx", rgdwAxes[5] ); + check_member( desc, expect_desc, "%ld", rglDirection[0] ); + check_member( desc, expect_desc, "%ld", rglDirection[1] ); + check_member( desc, expect_desc, "%ld", rglDirection[2] ); + check_member( desc, expect_desc, "%ld", rglDirection[3] ); + check_member( desc, expect_desc, "%ld", rglDirection[4] ); + check_member( desc, expect_desc, "%ld", rglDirection[5] ); + check_member( desc, expect_desc, "%lu", cbTypeSpecificParams ); + + set_hid_expect( file, expect_destroy, sizeof(expect_destroy) ); + ref = IDirectInputEffect_Release( effect ); + ok( ref == 0, "Release returned %ld\n", ref ); + set_hid_expect( file, NULL, 0 ); +} + +static BOOL test_force_feedback_six_axes(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), + USAGE(1, HID_USAGE_GENERIC_RX), + USAGE(1, HID_USAGE_GENERIC_RY), + USAGE(1, HID_USAGE_GENERIC_RZ), + LOGICAL_MINIMUM(1, 0), + LOGICAL_MAXIMUM(1, 0x7f), + PHYSICAL_MINIMUM(1, 0), + PHYSICAL_MAXIMUM(1, 0x7f), + REPORT_SIZE(1, 8), + REPORT_COUNT(1, 6), + 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), + USAGE(1, PID_USAGE_DC_STOP_ALL_EFFECTS), + LOGICAL_MINIMUM(1, 1), + LOGICAL_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), + USAGE(1, PID_USAGE_ET_CONSTANT_FORCE), + LOGICAL_MINIMUM(1, 1), + LOGICAL_MAXIMUM(1, 4), + PHYSICAL_MINIMUM(1, 1), + PHYSICAL_MAXIMUM(1, 4), + 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), + USAGE(4, (HID_USAGE_PAGE_GENERIC << 16)|HID_USAGE_GENERIC_RX), + USAGE(4, (HID_USAGE_PAGE_GENERIC << 16)|HID_USAGE_GENERIC_RY), + USAGE(4, (HID_USAGE_PAGE_GENERIC << 16)|HID_USAGE_GENERIC_RZ), + LOGICAL_MINIMUM(1, 0), + LOGICAL_MAXIMUM(1, 1), + PHYSICAL_MINIMUM(1, 0), + PHYSICAL_MAXIMUM(1, 1), + REPORT_SIZE(1, 1), + REPORT_COUNT(1, 6), + 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, 1), + 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), + USAGE(4, (HID_USAGE_PAGE_ORDINAL << 16)|3), + USAGE(4, (HID_USAGE_PAGE_ORDINAL << 16)|4), + USAGE(4, (HID_USAGE_PAGE_ORDINAL << 16)|5), + USAGE(4, (HID_USAGE_PAGE_ORDINAL << 16)|6), + 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, 6), + OUTPUT(1, Data|Var|Abs), + UNIT_EXPONENT(1, 0), + UNIT(1, 0), + END_COLLECTION, + END_COLLECTION, + + USAGE(1, PID_USAGE_SET_PERIODIC_REPORT), + COLLECTION(1, Logical), + REPORT_ID(1, 5), + + USAGE(1, PID_USAGE_MAGNITUDE), + 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_SET_ENVELOPE_REPORT), + COLLECTION(1, Logical), + REPORT_ID(1, 6), + + USAGE(1, PID_USAGE_ATTACK_LEVEL), + USAGE(1, PID_USAGE_FADE_LEVEL), + 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_ATTACK_TIME), + USAGE(1, PID_USAGE_FADE_TIME), + 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), + PHYSICAL_MAXIMUM(1, 0), + UNIT_EXPONENT(1, 0), + UNIT(1, 0), + 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), + USAGE(4, (HID_USAGE_PAGE_ORDINAL << 16)|3), + USAGE(4, (HID_USAGE_PAGE_ORDINAL << 16)|4), + USAGE(4, (HID_USAGE_PAGE_ORDINAL << 16)|5), + USAGE(4, (HID_USAGE_PAGE_ORDINAL << 16)|6), + LOGICAL_MINIMUM(1, 0), + LOGICAL_MAXIMUM(1, 1), + PHYSICAL_MINIMUM(1, 0), + PHYSICAL_MAXIMUM(1, 1), + REPORT_SIZE(1, 2), + REPORT_COUNT(1, 6), + OUTPUT(1, Data|Var|Abs), + END_COLLECTION, + REPORT_SIZE(1, 2), + REPORT_COUNT(1, 2), + OUTPUT(1, Cnst|Var|Abs), + + USAGE(1, PID_USAGE_CP_OFFSET), + 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, 3), + OUTPUT(1, Data|Var|Abs), + + USAGE(1, PID_USAGE_POSITIVE_SATURATION), + USAGE(1, PID_USAGE_NEGATIVE_SATURATION), + 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, 3), + OUTPUT(1, Data|Var|Abs), + END_COLLECTION, + + + USAGE(1, PID_USAGE_DEVICE_GAIN_REPORT), + COLLECTION(1, Logical), + REPORT_ID(1, 8), + + USAGE(1, PID_USAGE_DEVICE_GAIN), + 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_SET_CONSTANT_FORCE_REPORT), + COLLECTION(1, Logical), + REPORT_ID(1, 9), + + USAGE(1, PID_USAGE_MAGNITUDE), + LOGICAL_MINIMUM(2, 0xd8f0), + LOGICAL_MAXIMUM(2, 0x2710), + PHYSICAL_MINIMUM(2, 0xd8f0), + PHYSICAL_MAXIMUM(2, 0x2710), + REPORT_SIZE(1, 16), + REPORT_COUNT(1, 1), + OUTPUT(1, Data|Var|Abs), + END_COLLECTION, + + END_COLLECTION, + }; + C_ASSERT(sizeof(report_descriptor) < MAX_HID_DESCRIPTOR_LEN); +#include "pop_hid_macros.h" + + struct hid_device_desc desc = + { + .use_report_id = TRUE, + .caps = + { + .InputReportByteLength = 8, + .OutputReportByteLength = 15, + }, + .attributes = default_attributes, + }; + const DIDEVCAPS expect_caps = + { + .dwSize = sizeof(DIDEVCAPS), + .dwFlags = DIDC_FORCEFEEDBACK | DIDC_ATTACHED | DIDC_EMULATED | DIDC_STARTDELAY | + DIDC_FFFADE | DIDC_FFATTACK | DIDC_DEADBAND | DIDC_SATURATION, + .dwDevType = DIDEVTYPE_HID | (DI8DEVTYPE1STPERSON_LIMITED << 8) | DI8DEVTYPE_1STPERSON, + .dwAxes = 6, + .dwButtons = 2, + .dwFFSamplePeriod = 1000000, + .dwFFMinTimeResolution = 1000000, + .dwHardwareRevision = 1, + .dwFFDriverVersion = 1, + }; + const DIDEVICEINSTANCEW expect_devinst = + { + .dwSize = sizeof(DIDEVICEINSTANCEW), + .guidInstance = expect_guid_product, + .guidProduct = expect_guid_product, + .dwDevType = DIDEVTYPE_HID | (DI8DEVTYPE1STPERSON_LIMITED << 8) | DI8DEVTYPE_1STPERSON, + .tszInstanceName = L"Wine Test", + .tszProductName = L"Wine Test", + .guidFFDriver = IID_IDirectInputPIDDriver, + .wUsagePage = HID_USAGE_PAGE_GENERIC, + .wUsage = HID_USAGE_GENERIC_JOYSTICK, + }; + DIPROPDWORD prop_dword = + { + .diph = + { + .dwSize = sizeof(DIPROPDWORD), + .dwHeaderSize = sizeof(DIPROPHEADER), + .dwHow = DIPH_DEVICE, + }, + }; + DIPROPGUIDANDPATH prop_guid_path = + { + .diph = + { + .dwSize = sizeof(DIPROPGUIDANDPATH), + .dwHeaderSize = sizeof(DIPROPHEADER), + .dwHow = DIPH_DEVICE, + }, + }; + struct hid_expect expect_acquire[] = + { + /* device control */ + { + .code = IOCTL_HID_WRITE_REPORT, + .report_id = 1, + .report_len = 2, + .report_buf = {1, 0x01}, + }, + /* device gain */ + { + .code = IOCTL_HID_WRITE_REPORT, + .report_id = 8, + .report_len = 2, + .report_buf = {8, 0xff}, + }, + }; + struct hid_expect expect_reset[] = + { + /* device control */ + { + .code = IOCTL_HID_WRITE_REPORT, + .report_id = 1, + .report_len = 2, + .report_buf = {1, 0x01}, + }, + }; + DIDEVICEINSTANCEW devinst = {.dwSize = sizeof(DIDEVICEINSTANCEW)}; + IDirectInputDevice8W *device = NULL; + DIDEVCAPS caps = {0}; + DWORD version = 0x800; + HANDLE file; + HRESULT hr; + HWND hwnd; + ULONG ref; + + winetest_push_context( "%#lx", version ); + cleanup_registry_keys(); + + desc.report_descriptor_len = sizeof(report_descriptor); + memcpy( desc.report_descriptor_buf, report_descriptor, sizeof(report_descriptor) ); + fill_context( desc.context, ARRAY_SIZE(desc.context) ); + + if (!hid_device_start( &desc, 1 )) goto done; + if (FAILED(hr = dinput_test_create_device( version, &devinst, &device ))) goto done; + + check_dinput_devices( version, &devinst ); + + hr = IDirectInputDevice8_GetDeviceInfo( device, &devinst ); + ok( hr == DI_OK, "GetDeviceInfo returned %#lx\n", hr ); + check_member( devinst, expect_devinst, "%lu", dwSize ); + check_member_guid( devinst, expect_devinst, guidProduct ); + todo_wine check_member( devinst, expect_devinst, "%#lx", dwDevType ); + check_member_wstr( devinst, expect_devinst, tszInstanceName ); + check_member_wstr( devinst, expect_devinst, tszProductName ); + check_member_guid( devinst, expect_devinst, guidFFDriver ); + check_member( devinst, expect_devinst, "%04x", wUsagePage ); + check_member( devinst, expect_devinst, "%04x", wUsage ); + + caps.dwSize = sizeof(DIDEVCAPS); + hr = IDirectInputDevice8_GetCapabilities( device, &caps ); + ok( hr == DI_OK, "GetCapabilities returned %#lx\n", hr ); + check_member( caps, expect_caps, "%lu", dwSize ); + check_member( caps, expect_caps, "%#lx", dwFlags ); + todo_wine check_member( caps, expect_caps, "%#lx", dwDevType ); + check_member( caps, expect_caps, "%lu", dwAxes ); + check_member( caps, expect_caps, "%lu", dwButtons ); + check_member( caps, expect_caps, "%lu", dwPOVs ); + check_member( caps, expect_caps, "%lu", dwFFSamplePeriod ); + check_member( caps, expect_caps, "%lu", dwFFMinTimeResolution ); + check_member( caps, expect_caps, "%lu", dwFirmwareRevision ); + check_member( caps, expect_caps, "%lu", dwHardwareRevision ); + check_member( caps, expect_caps, "%lu", dwFFDriverVersion ); + + prop_dword.dwData = 0xdeadbeef; + hr = IDirectInputDevice8_GetProperty( device, DIPROP_FFGAIN, &prop_dword.diph ); + ok( hr == DI_OK, "GetProperty DIPROP_FFGAIN returned %#lx\n", hr ); + ok( prop_dword.dwData == 10000, "got %lu expected %u\n", prop_dword.dwData, 10000 ); + + hr = IDirectInputDevice8_GetProperty( device, DIPROP_FFLOAD, &prop_dword.diph ); + ok( hr == DIERR_NOTEXCLUSIVEACQUIRED, "GetProperty DIPROP_FFLOAD returned %#lx\n", hr ); + + hr = IDirectInputDevice8_SetDataFormat( device, &c_dfDIJoystick2 ); + ok( hr == DI_OK, "SetDataFormat returned: %#lx\n", hr ); + + hr = IDirectInputDevice8_GetProperty( device, DIPROP_GUIDANDPATH, &prop_guid_path.diph ); + ok( hr == DI_OK, "GetProperty DIPROP_GUIDANDPATH returned %#lx\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 %lu\n", GetLastError() ); + + hwnd = create_foreground_window( FALSE ); + + hr = IDirectInputDevice8_SetCooperativeLevel( device, hwnd, DISCL_BACKGROUND | DISCL_EXCLUSIVE ); + ok( hr == DI_OK, "SetCooperativeLevel returned: %#lx\n", hr ); + set_hid_expect( file, expect_acquire, sizeof(expect_acquire) ); + hr = IDirectInputDevice8_Acquire( device ); + ok( hr == DI_OK, "Acquire returned: %#lx\n", hr ); + wait_hid_expect( file, 100 ); + + test_condition_effect_six_axes( device, file ); + + set_hid_expect( file, expect_reset, sizeof(struct hid_expect) ); + hr = IDirectInputDevice8_Unacquire( device ); + ok( hr == DI_OK, "Unacquire returned: %#lx\n", hr ); + set_hid_expect( file, NULL, 0 ); + + ref = IDirectInputDevice8_Release( device ); + ok( ref == 0, "Release returned %ld\n", ref ); + + DestroyWindow( hwnd ); + CloseHandle( file ); + +done: + hid_device_stop( &desc, 1 ); + cleanup_registry_keys(); + winetest_pop_context(); + return device != NULL; +} + static void test_device_managed_effect(void) { #include "psh_hid_macros.h" @@ -7225,7 +7969,8 @@ START_TEST( force_feedback ) test_force_feedback_joystick( 0x500 ); test_force_feedback_joystick( 0x700 ); test_device_managed_effect(); - test_windows_gaming_input(); + test_force_feedback_six_axes(); + test_windows_gaming_input(); /* keep it last, seems to mess with other tests */ }
done:
From: Tomasz Pakuła tomasz.pakula.oficjalny@gmail.com
--- dlls/dinput/joystick_hid.c | 18 +++++++++--------- dlls/winebus.sys/hid.c | 2 +- dlls/winebus.sys/unix_private.h | 7 ++++--- include/wine/hid.h | 2 ++ 4 files changed, 16 insertions(+), 13 deletions(-)
diff --git a/dlls/dinput/joystick_hid.c b/dlls/dinput/joystick_hid.c index f7f87596c82..27d08fd2af2 100644 --- a/dlls/dinput/joystick_hid.c +++ b/dlls/dinput/joystick_hid.c @@ -69,8 +69,8 @@ struct pid_effect_update UINT axis_count; UINT direction_coll; UINT direction_count; - struct hid_value_caps *axis_caps[6]; - struct hid_value_caps *direction_caps[6]; + struct hid_value_caps *axis_caps[MAX_PID_AXES]; + struct hid_value_caps *direction_caps[MAX_PID_AXES]; struct hid_value_caps *duration_caps; struct hid_value_caps *gain_caps; struct hid_value_caps *sample_period_caps; @@ -221,11 +221,11 @@ struct hid_joystick_effect struct list entry; struct hid_joystick *joystick;
- DWORD axes[6]; - LONG directions[6]; + DWORD axes[MAX_PID_AXES]; + LONG directions[MAX_PID_AXES]; DICONSTANTFORCE constant_force; DIRAMPFORCE ramp_force; - DICONDITION condition[6]; + DICONDITION condition[MAX_PID_AXES]; DIENVELOPE envelope; DIPERIODIC periodic; DIEFFECT params; @@ -1852,7 +1852,7 @@ static BOOL init_pid_caps( struct dinput_device *device, UINT index, struct hid_ if (instance->wCollectionNumber == effect_update->axes_coll) { SET_REPORT_ID( effect_update ); - if (effect_update->axis_count >= 6) FIXME( "more than 6 PID axes detected\n" ); + if (effect_update->axis_count >= MAX_PID_AXES) FIXME( "more than %d PID axes detected\n", MAX_PID_AXES ); else effect_update->axis_caps[effect_update->axis_count] = caps; effect_update->axis_count++; } @@ -1861,7 +1861,7 @@ static BOOL init_pid_caps( struct dinput_device *device, UINT index, struct hid_ SET_REPORT_ID( effect_update ); caps->physical_min = 0; caps->physical_max = 35900; - if (effect_update->direction_count >= 6) FIXME( "more than 6 PID directions detected\n" ); + if (effect_update->direction_count >= MAX_PID_AXES) FIXME( "more than %d PID directions detected\n", MAX_PID_AXES ); else effect_update->direction_caps[effect_update->direction_count] = caps; effect_update->direction_count++; } @@ -2374,7 +2374,7 @@ static void convert_directions_from_spherical( const DIEFFECT *in, DIEFFECT *out static void convert_directions( const DIEFFECT *in, DIEFFECT *out ) { DWORD direction_flags = DIEFF_CARTESIAN | DIEFF_POLAR | DIEFF_SPHERICAL; - LONG directions[6] = {0}; + LONG directions[MAX_PID_AXES] = {0}; DIEFFECT spherical = {.rglDirection = directions};
switch (in->dwFlags & direction_flags) @@ -2894,7 +2894,7 @@ static HRESULT WINAPI hid_joystick_effect_Download( IDirectInputEffect *iface ) ULONG report_len = impl->joystick->caps.OutputReportByteLength; HANDLE device = impl->joystick->device; struct hid_value_caps *caps; - LONG directions[4] = {0}; + LONG directions[MAX_PID_AXES] = {0}; DWORD i, tmp, count; DIEFFECT spherical; NTSTATUS status; diff --git a/dlls/winebus.sys/hid.c b/dlls/winebus.sys/hid.c index 78efe6116ce..0e101c2f70a 100644 --- a/dlls/winebus.sys/hid.c +++ b/dlls/winebus.sys/hid.c @@ -503,7 +503,7 @@ struct pid_effect_update BYTE gain_percent; BYTE trigger_button; BYTE enable_bits; - UINT16 direction[2]; + UINT16 direction[MAX_PID_AXES]; };
struct pid_set_periodic diff --git a/dlls/winebus.sys/unix_private.h b/dlls/winebus.sys/unix_private.h index f2f4e71f014..bc76fad33dc 100644 --- a/dlls/winebus.sys/unix_private.h +++ b/dlls/winebus.sys/unix_private.h @@ -29,6 +29,7 @@ #include "unixlib.h"
#include "wine/list.h" +#include "wine/hid.h"
struct effect_periodic { @@ -75,9 +76,9 @@ struct effect_params UINT16 sample_period; UINT16 start_delay; BYTE trigger_button; - BOOL axis_enabled[2]; + BOOL axis_enabled[MAX_PID_AXES]; BOOL direction_enabled; - UINT16 direction[2]; + UINT16 direction[MAX_PID_AXES]; BYTE gain_percent; BYTE condition_count; /* only for periodic, constant or ramp forces */ @@ -85,7 +86,7 @@ struct effect_params union { struct effect_periodic periodic; - struct effect_condition condition[2]; + struct effect_condition condition[MAX_PID_AXES]; struct effect_constant_force constant_force; struct effect_ramp_force ramp_force; }; diff --git a/include/wine/hid.h b/include/wine/hid.h index 8200ca9beec..cb31bd6e5a5 100644 --- a/include/wine/hid.h +++ b/include/wine/hid.h @@ -239,6 +239,8 @@ struct hid_preparsed_data #define PID_USAGE_CREATE_NEW_EFFECT_REPORT ((USAGE) 0xab) #define PID_USAGE_RAM_POOL_AVAILABLE ((USAGE) 0xac)
+#define MAX_PID_AXES 6 + #define IOCTL_HID_GET_WINE_RAWINPUT_HANDLE HID_BUFFER_CTL_CODE(300)
#endif /* __WINE_PARSE_H */
From: Tomasz Pakuła tomasz.pakula.oficjalny@gmail.com
Allow the PID descriptor building function to create up to MAX_PID_AXES FFB-enabled axes. This will allow better effect handling and support for devices with more than two FFB axis.
Devices with only one axis, can be actually presented as such. --- dlls/winebus.sys/bus_sdl.c | 2 +- dlls/winebus.sys/bus_udev.c | 2 +- dlls/winebus.sys/hid.c | 139 ++++++++++++++++++++++---------- dlls/winebus.sys/unix_private.h | 3 +- 4 files changed, 101 insertions(+), 45 deletions(-)
diff --git a/dlls/winebus.sys/bus_sdl.c b/dlls/winebus.sys/bus_sdl.c index e050742ca66..29a0d2cf8d2 100644 --- a/dlls/winebus.sys/bus_sdl.c +++ b/dlls/winebus.sys/bus_sdl.c @@ -238,7 +238,7 @@ static BOOL descriptor_add_haptic(struct sdl_device *impl, BOOL force) if (force || (impl->effect_support & SDL_HAPTIC_CONSTANT)) usages[count++] = PID_USAGE_ET_CONSTANT_FORCE; if (force || (impl->effect_support & SDL_HAPTIC_RAMP)) usages[count++] = PID_USAGE_ET_RAMP;
- if (!hid_device_add_physical(&impl->unix_device, usages, count)) + if (!hid_device_add_physical(&impl->unix_device, usages, count, 2)) return FALSE; }
diff --git a/dlls/winebus.sys/bus_udev.c b/dlls/winebus.sys/bus_udev.c index 561f0cdc0e4..05cf34d2071 100644 --- a/dlls/winebus.sys/bus_udev.c +++ b/dlls/winebus.sys/bus_udev.c @@ -648,7 +648,7 @@ static NTSTATUS build_report_descriptor(struct unix_device *iface, struct udev_d if (test_bit(ffbits, FF_CONSTANT)) usages[count++] = PID_USAGE_ET_CONSTANT_FORCE; if (test_bit(ffbits, FF_RAMP)) usages[count++] = PID_USAGE_ET_RAMP;
- if (!hid_device_add_physical(iface, usages, count)) + if (!hid_device_add_physical(iface, usages, count, 2)) return STATUS_NO_MEMORY; }
diff --git a/dlls/winebus.sys/hid.c b/dlls/winebus.sys/hid.c index 0e101c2f70a..7bb6617501d 100644 --- a/dlls/winebus.sys/hid.c +++ b/dlls/winebus.sys/hid.c @@ -556,6 +556,80 @@ struct pid_effect_state }; #include "poppack.h"
+static BOOL hid_descriptor_add_axes_enable(struct unix_device *iface, USHORT axes_count) +{ + struct hid_report_descriptor *desc = &iface->hid_report_descriptor; + const BYTE header[] = + { + USAGE(1, PID_USAGE_AXES_ENABLE), + COLLECTION(1, Logical), + }; + const BYTE footer[] = + { + LOGICAL_MINIMUM(1, 0), + LOGICAL_MAXIMUM(1, 1), + REPORT_SIZE(1, 1), + REPORT_COUNT(1, axes_count), + 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, (7 - axes_count) % 8), /* byte pad */ + OUTPUT(1, Cnst|Var|Abs), + }; + UINT i; + + if (!hid_report_descriptor_append(desc, header, sizeof(header))) + return FALSE; + + for (i = 0; i < axes_count; i++) + { + USAGE_AND_PAGE usage = iface->hid_device_state.abs_axis_usages[i]; + const BYTE template[] = { USAGE(4, ((UINT)usage.UsagePage << 16) | usage.Usage) }; + if (!hid_report_descriptor_append(desc, template, sizeof(template))) + return FALSE; + } + + return hid_report_descriptor_append(desc, footer, sizeof(footer)); +} + +static BOOL hid_descriptor_add_directions(struct unix_device *iface, USHORT axes_count) +{ + struct hid_report_descriptor *desc = &iface->hid_report_descriptor; + const BYTE header[] = + { + USAGE(1, PID_USAGE_DIRECTION), + COLLECTION(1, Logical), + }; + const BYTE footer[] = + { + UNIT(1, 0x14), /* Eng Rot:Angular Pos */ + UNIT_EXPONENT(1, -2), + LOGICAL_MINIMUM(1, 0), + LOGICAL_MAXIMUM(4, 35900), + REPORT_SIZE(1, 16), + REPORT_COUNT(1, axes_count), + OUTPUT(1, Data|Var|Abs), + END_COLLECTION, + UNIT_EXPONENT(1, 0), + UNIT(1, 0), /* None */ + }; + UINT i; + + if (!hid_report_descriptor_append(desc, header, sizeof(header))) + return FALSE; + + for (i = 0; i < axes_count; ++i) + { + const BYTE template[] = { USAGE(4, (HID_USAGE_PAGE_ORDINAL << 16) | (i + 1)) }; + if (!hid_report_descriptor_append(desc, template, sizeof(template))) + return FALSE; + } + + return hid_report_descriptor_append(desc, footer, sizeof(footer)); +} + static BOOL hid_descriptor_add_set_periodic(struct unix_device *iface) { struct hid_report_descriptor *desc = &iface->hid_report_descriptor; @@ -672,7 +746,7 @@ static BOOL hid_descriptor_add_set_envelope(struct unix_device *iface) return hid_report_descriptor_append(desc, template, sizeof(template)); }
-static BOOL hid_descriptor_add_set_condition(struct unix_device *iface) +static BOOL hid_descriptor_add_set_condition(struct unix_device *iface, USHORT axes_count) { struct hid_report_descriptor *desc = &iface->hid_report_descriptor; const BYTE report_id = ++desc->next_report_id[HidP_Output]; @@ -692,7 +766,7 @@ static BOOL hid_descriptor_add_set_condition(struct unix_device *iface)
USAGE(1, PID_USAGE_PARAMETER_BLOCK_OFFSET), LOGICAL_MINIMUM(1, 0x00), - LOGICAL_MAXIMUM(1, 0x01), + LOGICAL_MAXIMUM(1, axes_count - 1), REPORT_SIZE(1, 8), REPORT_COUNT(1, 1), OUTPUT(1, Data|Var|Abs), @@ -800,7 +874,7 @@ static BOOL hid_descriptor_add_set_ramp_force(struct unix_device *iface) return hid_report_descriptor_append(desc, template, sizeof(template)); }
-BOOL hid_device_add_physical(struct unix_device *iface, USAGE *usages, USHORT count) +BOOL hid_device_add_physical(struct unix_device *iface, USAGE *usages, USHORT count, USHORT axes_count) { struct hid_report_descriptor *desc = &iface->hid_report_descriptor; const BYTE device_control_report = ++desc->next_report_id[HidP_Output]; @@ -901,7 +975,7 @@ BOOL hid_device_add_physical(struct unix_device *iface, USAGE *usages, USHORT co USAGE(1, PID_USAGE_EFFECT_TYPE), COLLECTION(1, Logical), }; - const BYTE effect_update_footer[] = + const BYTE effect_update_template[] = { LOGICAL_MINIMUM(1, 1), LOGICAL_MAXIMUM(1, count), @@ -936,37 +1010,9 @@ BOOL hid_device_add_physical(struct unix_device *iface, USAGE *usages, USHORT co REPORT_SIZE(1, 8), REPORT_COUNT(1, 1), OUTPUT(1, Data|Var|Abs|Null), - - USAGE(1, PID_USAGE_AXES_ENABLE), - COLLECTION(1, Logical), - USAGE(4, (state->abs_axis_usages[0].UsagePage<<16)|state->abs_axis_usages[0].Usage), - USAGE(4, (state->abs_axis_usages[1].UsagePage<<16)|state->abs_axis_usages[1].Usage), - LOGICAL_MINIMUM(1, 0), - LOGICAL_MAXIMUM(1, 1), - REPORT_SIZE(1, 1), - REPORT_COUNT(1, 2), - 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, 5), - OUTPUT(1, Cnst|Var|Abs), /* 5-bit pad */ - - 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), - LOGICAL_MINIMUM(1, 0), - LOGICAL_MAXIMUM(4, 35900), - REPORT_SIZE(1, 16), - REPORT_COUNT(1, 2), - OUTPUT(1, Data|Var|Abs), - END_COLLECTION, - UNIT_EXPONENT(1, 0), - UNIT(1, 0), /* None */ + }; + const BYTE effect_update_footer[] = + { END_COLLECTION, };
@@ -1003,6 +1049,8 @@ BOOL hid_device_add_physical(struct unix_device *iface, USAGE *usages, USHORT co BOOL ramp_force = FALSE; ULONG i;
+ if (axes_count > MAX_PID_AXES) axes_count = MAX_PID_AXES; + if (!hid_report_descriptor_append(desc, device_control_header, sizeof(device_control_header))) return FALSE; for (i = 1; i < ARRAY_SIZE(pid_device_control_usages); ++i) @@ -1033,6 +1081,12 @@ BOOL hid_device_add_physical(struct unix_device *iface, USAGE *usages, USHORT co if (!hid_report_descriptor_append_usage(desc, usages[i])) return FALSE; } + if (!hid_report_descriptor_append(desc, effect_update_template, sizeof(effect_update_template))) + return FALSE; + if (!hid_descriptor_add_axes_enable(iface, axes_count)) + return FALSE; + if (!hid_descriptor_add_directions(iface, axes_count)) + return FALSE; if (!hid_report_descriptor_append(desc, effect_update_footer, sizeof(effect_update_footer))) return FALSE;
@@ -1059,7 +1113,7 @@ BOOL hid_device_add_physical(struct unix_device *iface, USAGE *usages, USHORT co return FALSE; if (envelope && !hid_descriptor_add_set_envelope(iface)) return FALSE; - if (condition && !hid_descriptor_add_set_condition(iface)) + if (condition && !hid_descriptor_add_set_condition(iface, axes_count)) return FALSE; if (constant_force && !hid_descriptor_add_set_constant_force(iface)) return FALSE; @@ -1076,6 +1130,7 @@ BOOL hid_device_add_physical(struct unix_device *iface, USAGE *usages, USHORT co iface->hid_physical.device_gain_report = device_gain_report; iface->hid_physical.effect_control_report = effect_control_report; iface->hid_physical.effect_update_report = effect_update_report; + iface->hid_physical.axes_count = axes_count;
effect_state->id = effect_state_report; effect_state->report_len = sizeof(struct pid_effect_state) + 1; @@ -1187,8 +1242,9 @@ static void hid_device_set_output_report(struct unix_device *iface, HID_XFER_PAC struct pid_effect_update *report = (struct pid_effect_update *)(packet->reportBuffer + 1); struct effect_params *params = iface->hid_physical.effect_params + report->index; USAGE effect_type; + ULONG i;
- io->Information = sizeof(*report) + 1; + io->Information = offsetof(struct pid_effect_update, direction[physical->axes_count]) + 1; if (packet->reportBufferLen < io->Information) io->Status = STATUS_BUFFER_TOO_SMALL; else if (report->type_index >= ARRAY_SIZE(iface->hid_physical.effect_types)) @@ -1204,11 +1260,10 @@ static void hid_device_set_output_report(struct unix_device *iface, HID_XFER_PAC params->start_delay = report->start_delay; params->gain_percent = report->gain_percent; params->trigger_button = report->trigger_button == 0xff ? 0 : report->trigger_button; - params->axis_enabled[0] = (report->enable_bits & 1) != 0; - params->axis_enabled[1] = (report->enable_bits & 2) != 0; - params->direction_enabled = (report->enable_bits & 4) != 0; - params->direction[0] = report->direction[0]; - params->direction[1] = report->direction[1]; + + for (i = 0; i < physical->axes_count; ++i) params->direction[i] = report->direction[i]; + for (i = 0; i < physical->axes_count; ++i) params->axis_enabled[i] = !!(report->enable_bits & (1 << i)); + params->direction_enabled = (report->enable_bits & (1 << physical->axes_count)) != 0;
io->Status = iface->hid_vtbl->physical_effect_update(iface, report->index, params); } diff --git a/dlls/winebus.sys/unix_private.h b/dlls/winebus.sys/unix_private.h index bc76fad33dc..89a63c50430 100644 --- a/dlls/winebus.sys/unix_private.h +++ b/dlls/winebus.sys/unix_private.h @@ -184,6 +184,7 @@ struct hid_physical BYTE set_ramp_force_report;
struct hid_effect_state effect_state; + USHORT axes_count; };
struct hid_device_state @@ -253,7 +254,7 @@ extern BOOL hid_device_add_axes(struct unix_device *iface, BYTE count, USAGE usa const USAGE *usages, BOOL rel, LONG min, LONG max);
extern BOOL hid_device_add_haptics(struct unix_device *iface); -extern BOOL hid_device_add_physical(struct unix_device *iface, USAGE *usages, USHORT count); +extern BOOL hid_device_add_physical(struct unix_device *iface, USAGE *usages, USHORT count, USHORT axes_count);
extern BOOL hid_device_set_abs_axis(struct unix_device *iface, ULONG index, LONG value); extern BOOL hid_device_set_rel_axis(struct unix_device *iface, ULONG index, LONG value);
From: Tomasz Pakuła tomasz.pakula.oficjalny@gmail.com
Wine will now create PID axes in the virtual descriptor based on the haptic axes found in the SDL joystick.
With the recent SDL hint SDL_JOYSTICK_HAPTIC_AXES, it's possible to limit the exposed FFB axes to just one, even if device presents more. This, in turn fixes force feedback in Richard Burns Rally which includes all ffb axes on effect creation, but then updates this effec with only the steering axis. Changing cAxes and rgdwAxes after effect creation is forbidden and renders force feedback broken.
This issue is not wine-specific as the same thing happens on Windows for all steering wheels which define more than one FFB axis. On windows, this can be worked around with registry hacks which remove DIDFT_FFACTUATOR and DIDOI_FFACTUATOR from second axis dwType and dwFlags.
SDL_HapticNumAxes() was available since SDL 2.0 --- dlls/winebus.sys/bus_sdl.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/dlls/winebus.sys/bus_sdl.c b/dlls/winebus.sys/bus_sdl.c index 29a0d2cf8d2..eee95e213c0 100644 --- a/dlls/winebus.sys/bus_sdl.c +++ b/dlls/winebus.sys/bus_sdl.c @@ -96,6 +96,7 @@ MAKE_FUNCPTR(SDL_HapticClose); MAKE_FUNCPTR(SDL_HapticDestroyEffect); MAKE_FUNCPTR(SDL_HapticGetEffectStatus); MAKE_FUNCPTR(SDL_HapticNewEffect); +MAKE_FUNCPTR(SDL_HapticNumAxes); MAKE_FUNCPTR(SDL_HapticOpenFromJoystick); MAKE_FUNCPTR(SDL_HapticPause); MAKE_FUNCPTR(SDL_HapticQuery); @@ -226,6 +227,8 @@ static BOOL descriptor_add_haptic(struct sdl_device *impl, BOOL force)
if ((impl->effect_support & EFFECT_SUPPORT_PHYSICAL)) { + int axes_count; + /* SDL_HAPTIC_SQUARE doesn't exist */ if (force || (impl->effect_support & SDL_HAPTIC_SINE)) usages[count++] = PID_USAGE_ET_SINE; if (force || (impl->effect_support & SDL_HAPTIC_TRIANGLE)) usages[count++] = PID_USAGE_ET_TRIANGLE; @@ -238,7 +241,8 @@ static BOOL descriptor_add_haptic(struct sdl_device *impl, BOOL force) if (force || (impl->effect_support & SDL_HAPTIC_CONSTANT)) usages[count++] = PID_USAGE_ET_CONSTANT_FORCE; if (force || (impl->effect_support & SDL_HAPTIC_RAMP)) usages[count++] = PID_USAGE_ET_RAMP;
- if (!hid_device_add_physical(&impl->unix_device, usages, count, 2)) + if ((axes_count = pSDL_HapticNumAxes(impl->sdl_haptic)) < 0) axes_count = 2; + if (!hid_device_add_physical(&impl->unix_device, usages, count, axes_count)) return FALSE; }
@@ -1122,6 +1126,7 @@ NTSTATUS sdl_bus_init(void *args) LOAD_FUNCPTR(SDL_HapticDestroyEffect); LOAD_FUNCPTR(SDL_HapticGetEffectStatus); LOAD_FUNCPTR(SDL_HapticNewEffect); + LOAD_FUNCPTR(SDL_HapticNumAxes); LOAD_FUNCPTR(SDL_HapticOpenFromJoystick); LOAD_FUNCPTR(SDL_HapticPause); LOAD_FUNCPTR(SDL_HapticQuery);
This merge request was approved by Rémi Bernon.