Signed-off-by: Rémi Bernon rbernon@codeweavers.com ---
With this series we should have all non-custom force feedback effect types supported in the dinput HID joystick through PID reports, as well as down in the SDL and UDEV lnxev devices.
Any raw device, like UDEV hidraw or IOHID may be supported too, as long as they use the standard PID reports, although it's quite untested. They may expect slightly different usage of the reports.
What remains after that is a effect status input report, in order to implement force feedback status functions, and then dropping the other backends to convert dinput(8) to PE.
dlls/winebus.sys/bus_sdl.c | 4 ++ dlls/winebus.sys/bus_udev.c | 4 ++ dlls/winebus.sys/hid.c | 107 ++++++++++++++++++++++++++++++++ dlls/winebus.sys/unix_private.h | 13 ++++ 4 files changed, 128 insertions(+)
diff --git a/dlls/winebus.sys/bus_sdl.c b/dlls/winebus.sys/bus_sdl.c index 4eab51b5cb2..1bec3661757 100644 --- a/dlls/winebus.sys/bus_sdl.c +++ b/dlls/winebus.sys/bus_sdl.c @@ -549,6 +549,10 @@ static NTSTATUS sdl_device_physical_effect_update(struct unix_device *iface, BYT effect.periodic.direction.type = SDL_HAPTIC_SPHERICAL; effect.periodic.direction.dir[0] = params->direction[0] * 36000 / 256; effect.periodic.direction.dir[1] = params->direction[1] * 36000 / 256; + effect.periodic.period = params->periodic.period; + effect.periodic.magnitude = params->periodic.magnitude * 128; + effect.periodic.offset = params->periodic.offset; + effect.periodic.phase = params->periodic.phase; break;
case PID_USAGE_ET_SPRING: diff --git a/dlls/winebus.sys/bus_udev.c b/dlls/winebus.sys/bus_udev.c index f780aac2785..e60891c2244 100644 --- a/dlls/winebus.sys/bus_udev.c +++ b/dlls/winebus.sys/bus_udev.c @@ -1001,6 +1001,10 @@ static NTSTATUS lnxev_device_physical_effect_update(struct unix_device *iface, B case PID_USAGE_ET_SAWTOOTH_UP: case PID_USAGE_ET_SAWTOOTH_DOWN: FIXME("periodic effect semi-stub!"); + effect.u.periodic.period = params->periodic.period; + effect.u.periodic.magnitude = params->periodic.magnitude * 128; + effect.u.periodic.offset = params->periodic.offset; + effect.u.periodic.phase = params->periodic.phase; break;
case PID_USAGE_ET_SPRING: diff --git a/dlls/winebus.sys/hid.c b/dlls/winebus.sys/hid.c index 72d358e6ad7..10e9316eb6c 100644 --- a/dlls/winebus.sys/hid.c +++ b/dlls/winebus.sys/hid.c @@ -440,8 +440,85 @@ struct pid_effect_update BYTE enable_bits; BYTE direction[2]; }; + +struct pid_set_periodic +{ + BYTE index; + BYTE magnitude; + BYTE offset; + BYTE phase; + UINT16 period; +}; #include "poppack.h"
+static BOOL hid_descriptor_add_set_periodic(struct unix_device *iface) +{ + struct hid_report_descriptor *desc = &iface->hid_report_descriptor; + const BYTE report_id = ++desc->next_report_id[HidP_Output]; + const BYTE template[] = + { + /* Periodic Report Definition */ + USAGE(1, PID_USAGE_SET_PERIODIC_REPORT), + COLLECTION(1, Logical), + REPORT_ID(1, report_id), + + USAGE(1, PID_USAGE_EFFECT_BLOCK_INDEX), + LOGICAL_MAXIMUM(1, 0x7f), + LOGICAL_MINIMUM(1, 0x00), + REPORT_SIZE(1, 8), + REPORT_COUNT(1, 1), + OUTPUT(1, Data|Var|Abs), + + USAGE(1, PID_USAGE_MAGNITUDE), + LOGICAL_MINIMUM(1, 0), + LOGICAL_MAXIMUM(2, 0x00ff), + PHYSICAL_MINIMUM(1, 0), + PHYSICAL_MAXIMUM(2, 10000), + REPORT_SIZE(1, 8), + REPORT_COUNT(1, 1), + OUTPUT(1, Data|Var|Abs), + + USAGE(1, PID_USAGE_OFFSET), + LOGICAL_MINIMUM(1, 0x80), + LOGICAL_MAXIMUM(1, 0x7f), + PHYSICAL_MINIMUM(2, -10000), + PHYSICAL_MAXIMUM(2, 10000), + REPORT_SIZE(1, 8), + REPORT_COUNT(1, 1), + OUTPUT(1, Data|Var|Abs), + + USAGE(1, PID_USAGE_PHASE), + UNIT(1, 0x14), /* Eng Rot:Angular Pos */ + UNIT_EXPONENT(1, -2), + LOGICAL_MINIMUM(1, 0), + LOGICAL_MAXIMUM(2, 0xff), + PHYSICAL_MINIMUM(1, 0), + PHYSICAL_MAXIMUM(4, 36000), + REPORT_SIZE(1, 8), + REPORT_COUNT(1, 1), + OUTPUT(1, Data|Var|Abs), + + USAGE(1, PID_USAGE_PERIOD), + 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, 1), + OUTPUT(1, Data|Var|Abs), + + PHYSICAL_MAXIMUM(1, 0), + UNIT_EXPONENT(1, 0), + UNIT(1, 0), /* None */ + END_COLLECTION, + }; + + iface->hid_physical.set_periodic_report = report_id; + return hid_report_descriptor_append(desc, template, sizeof(template)); +} + BOOL hid_device_add_physical(struct unix_device *iface, USAGE *usages, USHORT count) { struct hid_report_descriptor *desc = &iface->hid_report_descriptor; @@ -592,6 +669,7 @@ BOOL hid_device_add_physical(struct unix_device *iface, USAGE *usages, USHORT co UNIT(1, 0), /* None */ END_COLLECTION, }; + BOOL periodic = FALSE; ULONG i;
if (!hid_report_descriptor_append(desc, device_control_header, sizeof(device_control_header))) @@ -624,6 +702,19 @@ BOOL hid_device_add_physical(struct unix_device *iface, USAGE *usages, USHORT co if (!hid_report_descriptor_append(desc, effect_update_footer, sizeof(effect_update_footer))) return FALSE;
+ for (i = 0; i < count; ++i) + { + if (usages[i] == PID_USAGE_ET_SINE || + usages[i] == PID_USAGE_ET_SQUARE || + usages[i] == PID_USAGE_ET_TRIANGLE || + usages[i] == PID_USAGE_ET_SAWTOOTH_UP || + usages[i] == PID_USAGE_ET_SAWTOOTH_DOWN) + periodic = TRUE; + } + + if (periodic && !hid_descriptor_add_set_periodic(iface)) + return FALSE; + /* HID nary collection indexes start at 1 */ memcpy(iface->hid_physical.effect_types + 1, usages, count * sizeof(*usages));
@@ -751,6 +842,22 @@ static void hid_device_set_output_report(struct unix_device *iface, HID_XFER_PAC io->Status = iface->hid_vtbl->physical_effect_update(iface, report->index, params); } } + else if (packet->reportId == physical->set_periodic_report) + { + struct pid_set_periodic *report = (struct pid_set_periodic *)(packet->reportBuffer + 1); + struct effect_params *params = iface->hid_physical.effect_params + report->index; + + io->Information = sizeof(*report) + 1; + if (packet->reportBufferLen < io->Information) + io->Status = STATUS_BUFFER_TOO_SMALL; + else + { + params->periodic.magnitude = report->magnitude; + params->periodic.offset = report->offset; + params->periodic.phase = report->phase; + params->periodic.period = report->period; + } + } else { io->Information = 0; diff --git a/dlls/winebus.sys/unix_private.h b/dlls/winebus.sys/unix_private.h index 125ee600eb6..5962de7fb10 100644 --- a/dlls/winebus.sys/unix_private.h +++ b/dlls/winebus.sys/unix_private.h @@ -29,6 +29,14 @@
#include "wine/list.h"
+struct effect_periodic +{ + BYTE magnitude; + BYTE offset; + BYTE phase; + UINT16 period; +}; + struct effect_params { USAGE effect_type; @@ -41,6 +49,10 @@ struct effect_params BOOL axis_enabled[2]; BOOL direction_enabled; BYTE direction[2]; + union + { + struct effect_periodic periodic; + }; };
struct raw_device_vtbl @@ -112,6 +124,7 @@ struct hid_physical BYTE device_control_report; BYTE effect_control_report; BYTE effect_update_report; + BYTE set_periodic_report; };
struct hid_device_state
Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/dinput/joystick_hid.c | 101 +++++++++++++++++++++++++++++++++++++ dlls/dinput8/tests/hid.c | 1 - 2 files changed, 101 insertions(+), 1 deletion(-)
diff --git a/dlls/dinput/joystick_hid.c b/dlls/dinput/joystick_hid.c index 75bf66b7a51..7ea8ea3cd0e 100644 --- a/dlls/dinput/joystick_hid.c +++ b/dlls/dinput/joystick_hid.c @@ -104,6 +104,16 @@ struct pid_effect_update struct hid_value_caps *trigger_repeat_interval_caps; };
+struct pid_set_periodic +{ + BYTE id; + ULONG collection; + struct hid_value_caps *magnitude_caps; + struct hid_value_caps *period_caps; + struct hid_value_caps *phase_caps; + struct hid_value_caps *offset_caps; +}; + #define DEVICE_STATE_MAX_SIZE 1024
struct hid_joystick @@ -136,6 +146,7 @@ struct hid_joystick struct pid_control_report pid_device_control; struct pid_control_report pid_effect_control; struct pid_effect_update pid_effect_update; + struct pid_set_periodic pid_set_periodic; };
static inline struct hid_joystick *impl_from_IDirectInputDevice8W( IDirectInputDevice8W *iface ) @@ -164,6 +175,7 @@ struct hid_joystick_effect
char *effect_control_buf; char *effect_update_buf; + char *type_specific_buf[1]; };
static inline struct hid_joystick_effect *impl_from_IDirectInputEffect( IDirectInputEffect *iface ) @@ -1100,6 +1112,7 @@ static HRESULT WINAPI hid_joystick_GetEffectInfo( IDirectInputDevice8W *iface, D { struct hid_joystick *impl = impl_from_IDirectInputDevice8W( iface ); struct pid_effect_update *effect_update = &impl->pid_effect_update; + struct pid_set_periodic *set_periodic = &impl->pid_set_periodic; PHIDP_PREPARSED_DATA preparsed = impl->preparsed; HIDP_BUTTON_CAPS button; ULONG type, collection; @@ -1176,6 +1189,14 @@ static HRESULT WINAPI hid_joystick_GetEffectInfo( IDirectInputDevice8W *iface, D } }
+ if ((type == DIEFT_PERIODIC) && (collection = set_periodic->collection)) + { + if (set_periodic->magnitude_caps) info->dwDynamicParams |= DIEP_TYPESPECIFICPARAMS; + if (set_periodic->offset_caps) info->dwDynamicParams |= DIEP_TYPESPECIFICPARAMS; + if (set_periodic->period_caps) info->dwDynamicParams |= DIEP_TYPESPECIFICPARAMS; + if (set_periodic->phase_caps) info->dwDynamicParams |= DIEP_TYPESPECIFICPARAMS; + } + info->guid = *guid; info->dwEffType = type; info->dwStaticParams = info->dwDynamicParams; @@ -1824,6 +1845,7 @@ static BOOL init_pid_reports( struct hid_joystick *impl, struct hid_value_caps * struct pid_control_report *device_control = &impl->pid_device_control; struct pid_control_report *effect_control = &impl->pid_effect_control; struct pid_effect_update *effect_update = &impl->pid_effect_update; + struct pid_set_periodic *set_periodic = &impl->pid_set_periodic;
#define SET_COLLECTION( rep ) \ do \ @@ -1849,6 +1871,7 @@ static BOOL init_pid_reports( struct hid_joystick *impl, struct hid_value_caps * case PID_USAGE_DEVICE_CONTROL_REPORT: SET_COLLECTION( device_control ); break; case PID_USAGE_EFFECT_OPERATION_REPORT: SET_COLLECTION( effect_control ); break; case PID_USAGE_SET_EFFECT_REPORT: SET_COLLECTION( effect_update ); break; + case PID_USAGE_SET_PERIODIC_REPORT: SET_COLLECTION( set_periodic ); 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; @@ -1873,6 +1896,7 @@ static BOOL init_pid_caps( struct hid_joystick *impl, struct hid_value_caps *cap struct pid_control_report *device_control = &impl->pid_device_control; struct pid_control_report *effect_control = &impl->pid_effect_control; struct pid_effect_update *effect_update = &impl->pid_effect_update; + struct pid_set_periodic *set_periodic = &impl->pid_set_periodic;
if (!(instance->dwType & DIDFT_OUTPUT)) return DIENUM_CONTINUE;
@@ -1927,6 +1951,30 @@ static BOOL init_pid_caps( struct hid_joystick *impl, struct hid_value_caps *cap else effect_update->direction_caps[effect_update->direction_count] = caps; effect_update->direction_count++; } + if (instance->wCollectionNumber == set_periodic->collection) + { + SET_REPORT_ID( set_periodic ); + if (instance->wUsage == PID_USAGE_MAGNITUDE) + { + caps->physical_min = 0; + caps->physical_max = 10000; + set_periodic->magnitude_caps = caps; + } + if (instance->wUsage == PID_USAGE_PERIOD) + set_periodic->period_caps = caps; + if (instance->wUsage == PID_USAGE_PHASE) + { + caps->physical_min = 0; + caps->physical_max = 36000 - 36000 / (caps->logical_max - caps->logical_min); + set_periodic->phase_caps = caps; + } + if (instance->wUsage == PID_USAGE_OFFSET) + { + caps->physical_min = -10000; + caps->physical_max = 10000; + set_periodic->offset_caps = caps; + } + }
#undef SET_REPORT_ID
@@ -2033,6 +2081,7 @@ static HRESULT hid_joystick_create_device( IDirectInputImpl *dinput, const GUID TRACE( "effect control id %u, coll %u\n", impl->pid_effect_control.id, impl->pid_effect_control.collection ); TRACE( "effect update id %u, coll %u, type_coll %u\n", impl->pid_effect_update.id, impl->pid_effect_update.collection, impl->pid_effect_update.type_coll ); + TRACE( "set periodic id %u, coll %u\n", impl->pid_set_periodic.id, impl->pid_set_periodic.collection );
if (impl->pid_device_control.id) { @@ -2122,6 +2171,7 @@ static ULONG WINAPI hid_joystick_effect_Release( IDirectInputEffect *iface ) list_remove( &impl->entry ); LeaveCriticalSection( &impl->joystick->base.crit ); hid_joystick_private_decref( impl->joystick ); + HeapFree( GetProcessHeap(), 0, impl->type_specific_buf[0] ); HeapFree( GetProcessHeap(), 0, impl->effect_update_buf ); HeapFree( GetProcessHeap(), 0, impl->effect_control_buf ); HeapFree( GetProcessHeap(), 0, impl ); @@ -2148,6 +2198,29 @@ static HRESULT WINAPI hid_joystick_effect_Initialize( IDirectInputEffect *iface, joystick->preparsed, impl->effect_update_buf, report_len ); if (status != HIDP_STATUS_SUCCESS) return DIERR_DEVICENOTREG;
+ switch (type) + { + case PID_USAGE_ET_SQUARE: + case PID_USAGE_ET_SINE: + case PID_USAGE_ET_TRIANGLE: + case PID_USAGE_ET_SAWTOOTH_UP: + case PID_USAGE_ET_SAWTOOTH_DOWN: + status = HidP_InitializeReportForID( HidP_Output, joystick->pid_set_periodic.id, + joystick->preparsed, impl->type_specific_buf[0], report_len ); + if (status != HIDP_STATUS_SUCCESS) return DIERR_DEVICENOTREG; + break; + case PID_USAGE_ET_SPRING: + case PID_USAGE_ET_DAMPER: + case PID_USAGE_ET_INERTIA: + case PID_USAGE_ET_FRICTION: + case PID_USAGE_ET_CONSTANT_FORCE: + case PID_USAGE_ET_RAMP: + case PID_USAGE_ET_CUSTOM_FORCE_DATA: + FIXME( "effect type %#x not implemented!\n", type ); + impl->type_specific_buf[0][0] = 0; + break; + } + count = 1; status = HidP_SetUsages( HidP_Output, HID_USAGE_PAGE_PID, joystick->pid_effect_update.type_coll, &type, &count, joystick->preparsed, impl->effect_update_buf, report_len ); @@ -2637,6 +2710,7 @@ static HRESULT WINAPI hid_joystick_effect_Download( IDirectInputEffect *iface ) 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; + struct pid_set_periodic *set_periodic = &impl->joystick->pid_set_periodic; ULONG report_len = impl->joystick->caps.OutputReportByteLength; HANDLE device = impl->joystick->device; struct hid_value_caps *caps; @@ -2657,6 +2731,11 @@ static HRESULT WINAPI hid_joystick_effect_Download( IDirectInputEffect *iface ) hr = DIERR_INCOMPLETEEFFECT; else if (!impl->index && !FAILED(hr = find_next_effect_id( impl->joystick, &impl->index ))) { + if (!impl->type_specific_buf[0][0]) status = HIDP_STATUS_SUCCESS; + else status = HidP_SetUsageValue( HidP_Output, HID_USAGE_PAGE_PID, 0, PID_USAGE_EFFECT_BLOCK_INDEX, + impl->index, impl->joystick->preparsed, impl->type_specific_buf[0], report_len ); + if (status != HIDP_STATUS_SUCCESS) WARN( "HidP_SetUsageValue returned %#x\n", status ); + 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; @@ -2665,6 +2744,27 @@ static HRESULT WINAPI hid_joystick_effect_Download( IDirectInputEffect *iface )
if (hr == DI_OK) { + switch (impl->type) + { + case PID_USAGE_ET_SQUARE: + case PID_USAGE_ET_SINE: + case PID_USAGE_ET_TRIANGLE: + case PID_USAGE_ET_SAWTOOTH_UP: + case PID_USAGE_ET_SAWTOOTH_DOWN: + set_parameter_value( impl, impl->type_specific_buf[0], set_periodic->magnitude_caps, + impl->periodic.dwMagnitude ); + set_parameter_value_us( impl, impl->type_specific_buf[0], set_periodic->period_caps, + impl->periodic.dwPeriod ); + set_parameter_value( impl, impl->type_specific_buf[0], set_periodic->phase_caps, + impl->periodic.dwPhase ); + set_parameter_value( impl, impl->type_specific_buf[0], set_periodic->offset_caps, + impl->periodic.lOffset ); + + if (WriteFile( device, impl->type_specific_buf[0], report_len, NULL, NULL )) hr = DI_OK; + else hr = DIERR_INPUTLOST; + break; + } + 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, @@ -2773,6 +2873,7 @@ static HRESULT hid_joystick_effect_create( struct hid_joystick *joystick, IDirec report_len = joystick->caps.OutputReportByteLength; if (!(impl->effect_control_buf = HeapAlloc( GetProcessHeap(), 0, report_len ))) goto failed; if (!(impl->effect_update_buf = HeapAlloc( GetProcessHeap(), 0, report_len ))) goto failed; + if (!(impl->type_specific_buf[0] = HeapAlloc( GetProcessHeap(), 0, report_len ))) goto failed;
impl->envelope.dwSize = sizeof(DIENVELOPE); impl->params.dwSize = sizeof(DIEFFECT); diff --git a/dlls/dinput8/tests/hid.c b/dlls/dinput8/tests/hid.c index 9030d9cf157..662953266ef 100644 --- a/dlls/dinput8/tests/hid.c +++ b/dlls/dinput8/tests/hid.c @@ -5092,7 +5092,6 @@ static void test_periodic_effect( IDirectInputDevice8W *device, HANDLE file ) /* set periodic */ { .code = IOCTL_HID_WRITE_REPORT, - .todo = TRUE, .report_id = 5, .report_len = 2, .report_buf = {0x05,0x19},
Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/winebus.sys/bus_sdl.c | 5 ++- dlls/winebus.sys/bus_udev.c | 5 ++- dlls/winebus.sys/hid.c | 79 ++++++++++++++++++++++++++++++++- dlls/winebus.sys/unix_private.h | 11 +++++ 4 files changed, 97 insertions(+), 3 deletions(-)
diff --git a/dlls/winebus.sys/bus_sdl.c b/dlls/winebus.sys/bus_sdl.c index 1bec3661757..78de600fda6 100644 --- a/dlls/winebus.sys/bus_sdl.c +++ b/dlls/winebus.sys/bus_sdl.c @@ -541,7 +541,6 @@ static NTSTATUS sdl_device_physical_effect_update(struct unix_device *iface, BYT case PID_USAGE_ET_TRIANGLE: case PID_USAGE_ET_SAWTOOTH_UP: case PID_USAGE_ET_SAWTOOTH_DOWN: - FIXME("periodic effect semi-stub!"); effect.periodic.length = params->duration; effect.periodic.delay = params->start_delay; effect.periodic.button = params->trigger_button; @@ -553,6 +552,10 @@ static NTSTATUS sdl_device_physical_effect_update(struct unix_device *iface, BYT effect.periodic.magnitude = params->periodic.magnitude * 128; effect.periodic.offset = params->periodic.offset; effect.periodic.phase = params->periodic.phase; + effect.periodic.attack_length = params->envelope.attack_time; + effect.periodic.attack_level = params->envelope.attack_level; + effect.periodic.fade_length = params->envelope.fade_time; + effect.periodic.fade_level = params->envelope.fade_level; break;
case PID_USAGE_ET_SPRING: diff --git a/dlls/winebus.sys/bus_udev.c b/dlls/winebus.sys/bus_udev.c index e60891c2244..8a47b232148 100644 --- a/dlls/winebus.sys/bus_udev.c +++ b/dlls/winebus.sys/bus_udev.c @@ -1000,11 +1000,14 @@ static NTSTATUS lnxev_device_physical_effect_update(struct unix_device *iface, B case PID_USAGE_ET_TRIANGLE: case PID_USAGE_ET_SAWTOOTH_UP: case PID_USAGE_ET_SAWTOOTH_DOWN: - FIXME("periodic effect semi-stub!"); effect.u.periodic.period = params->periodic.period; effect.u.periodic.magnitude = params->periodic.magnitude * 128; effect.u.periodic.offset = params->periodic.offset; effect.u.periodic.phase = params->periodic.phase; + effect.u.periodic.envelope.attack_length = params->envelope.attack_time; + effect.u.periodic.envelope.attack_level = params->envelope.attack_level; + effect.u.periodic.envelope.fade_length = params->envelope.fade_time; + effect.u.periodic.envelope.fade_level = params->envelope.fade_level; break;
case PID_USAGE_ET_SPRING: diff --git a/dlls/winebus.sys/hid.c b/dlls/winebus.sys/hid.c index 10e9316eb6c..c25558a5ac4 100644 --- a/dlls/winebus.sys/hid.c +++ b/dlls/winebus.sys/hid.c @@ -449,6 +449,15 @@ struct pid_set_periodic BYTE phase; UINT16 period; }; + +struct pid_set_envelope +{ + BYTE index; + BYTE attack_level; + BYTE fade_level; + UINT16 attack_time; + UINT16 fade_time; +}; #include "poppack.h"
static BOOL hid_descriptor_add_set_periodic(struct unix_device *iface) @@ -519,6 +528,55 @@ static BOOL hid_descriptor_add_set_periodic(struct unix_device *iface) return hid_report_descriptor_append(desc, template, sizeof(template)); }
+static BOOL hid_descriptor_add_set_envelope(struct unix_device *iface) +{ + struct hid_report_descriptor *desc = &iface->hid_report_descriptor; + const BYTE report_id = ++desc->next_report_id[HidP_Output]; + const BYTE template[] = + { + /* Envelope Report Definition */ + USAGE(1, PID_USAGE_SET_ENVELOPE_REPORT), + COLLECTION(1, Logical), + REPORT_ID(1, report_id), + + USAGE(1, PID_USAGE_EFFECT_BLOCK_INDEX), + LOGICAL_MAXIMUM(1, 0x7f), + LOGICAL_MINIMUM(1, 0x00), + REPORT_SIZE(1, 8), + REPORT_COUNT(1, 1), + OUTPUT(1, Data|Var|Abs), + + USAGE(1, PID_USAGE_ATTACK_LEVEL), + USAGE(1, PID_USAGE_FADE_LEVEL), + LOGICAL_MINIMUM(1, 0x00), + LOGICAL_MAXIMUM(2, 0x00ff), + PHYSICAL_MINIMUM(1, 0), + PHYSICAL_MAXIMUM(2, 10000), + 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), + LOGICAL_MINIMUM(1, 0x00), + 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, + }; + + iface->hid_physical.set_envelope_report = report_id; + return hid_report_descriptor_append(desc, template, sizeof(template)); +} + BOOL hid_device_add_physical(struct unix_device *iface, USAGE *usages, USHORT count) { struct hid_report_descriptor *desc = &iface->hid_report_descriptor; @@ -670,6 +728,7 @@ BOOL hid_device_add_physical(struct unix_device *iface, USAGE *usages, USHORT co END_COLLECTION, }; BOOL periodic = FALSE; + BOOL envelope = FALSE; ULONG i;
if (!hid_report_descriptor_append(desc, device_control_header, sizeof(device_control_header))) @@ -709,11 +768,13 @@ BOOL hid_device_add_physical(struct unix_device *iface, USAGE *usages, USHORT co usages[i] == PID_USAGE_ET_TRIANGLE || usages[i] == PID_USAGE_ET_SAWTOOTH_UP || usages[i] == PID_USAGE_ET_SAWTOOTH_DOWN) - periodic = TRUE; + periodic = envelope = TRUE; }
if (periodic && !hid_descriptor_add_set_periodic(iface)) return FALSE; + if (envelope && !hid_descriptor_add_set_envelope(iface)) + return FALSE;
/* HID nary collection indexes start at 1 */ memcpy(iface->hid_physical.effect_types + 1, usages, count * sizeof(*usages)); @@ -858,6 +919,22 @@ static void hid_device_set_output_report(struct unix_device *iface, HID_XFER_PAC params->periodic.period = report->period; } } + else if (packet->reportId == physical->set_envelope_report) + { + struct pid_set_envelope *report = (struct pid_set_envelope *)(packet->reportBuffer + 1); + struct effect_params *params = iface->hid_physical.effect_params + report->index; + + io->Information = sizeof(*report) + 1; + if (packet->reportBufferLen < io->Information) + io->Status = STATUS_BUFFER_TOO_SMALL; + else + { + params->envelope.attack_level = report->attack_level; + params->envelope.fade_level = report->fade_level; + params->envelope.attack_time = report->attack_time; + params->envelope.fade_time = report->fade_time; + } + } else { io->Information = 0; diff --git a/dlls/winebus.sys/unix_private.h b/dlls/winebus.sys/unix_private.h index 5962de7fb10..71e6716c7fa 100644 --- a/dlls/winebus.sys/unix_private.h +++ b/dlls/winebus.sys/unix_private.h @@ -37,6 +37,14 @@ struct effect_periodic UINT16 period; };
+struct effect_envelope +{ + BYTE attack_level; + BYTE fade_level; + UINT16 attack_time; + UINT16 fade_time; +}; + struct effect_params { USAGE effect_type; @@ -49,6 +57,8 @@ struct effect_params BOOL axis_enabled[2]; BOOL direction_enabled; BYTE direction[2]; + /* only for periodic, constant or ramp forces */ + struct effect_envelope envelope; union { struct effect_periodic periodic; @@ -125,6 +135,7 @@ struct hid_physical BYTE effect_control_report; BYTE effect_update_report; BYTE set_periodic_report; + BYTE set_envelope_report; };
struct hid_device_state
Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/dinput/joystick_hid.c | 73 +++++++++++++++++++++++++++++++++++++- dlls/dinput8/tests/hid.c | 2 -- 2 files changed, 72 insertions(+), 3 deletions(-)
diff --git a/dlls/dinput/joystick_hid.c b/dlls/dinput/joystick_hid.c index 7ea8ea3cd0e..735b3800b31 100644 --- a/dlls/dinput/joystick_hid.c +++ b/dlls/dinput/joystick_hid.c @@ -114,6 +114,16 @@ struct pid_set_periodic struct hid_value_caps *offset_caps; };
+struct pid_set_envelope +{ + BYTE id; + ULONG collection; + struct hid_value_caps *attack_level_caps; + struct hid_value_caps *attack_time_caps; + struct hid_value_caps *fade_level_caps; + struct hid_value_caps *fade_time_caps; +}; + #define DEVICE_STATE_MAX_SIZE 1024
struct hid_joystick @@ -147,6 +157,7 @@ struct hid_joystick struct pid_control_report pid_effect_control; struct pid_effect_update pid_effect_update; struct pid_set_periodic pid_set_periodic; + struct pid_set_envelope pid_set_envelope; };
static inline struct hid_joystick *impl_from_IDirectInputDevice8W( IDirectInputDevice8W *iface ) @@ -175,7 +186,7 @@ struct hid_joystick_effect
char *effect_control_buf; char *effect_update_buf; - char *type_specific_buf[1]; + char *type_specific_buf[2]; };
static inline struct hid_joystick_effect *impl_from_IDirectInputEffect( IDirectInputEffect *iface ) @@ -1113,6 +1124,7 @@ static HRESULT WINAPI hid_joystick_GetEffectInfo( IDirectInputDevice8W *iface, D struct hid_joystick *impl = impl_from_IDirectInputDevice8W( iface ); struct pid_effect_update *effect_update = &impl->pid_effect_update; struct pid_set_periodic *set_periodic = &impl->pid_set_periodic; + struct pid_set_envelope *set_envelope = &impl->pid_set_envelope; PHIDP_PREPARSED_DATA preparsed = impl->preparsed; HIDP_BUTTON_CAPS button; ULONG type, collection; @@ -1197,6 +1209,16 @@ static HRESULT WINAPI hid_joystick_GetEffectInfo( IDirectInputDevice8W *iface, D if (set_periodic->phase_caps) info->dwDynamicParams |= DIEP_TYPESPECIFICPARAMS; }
+ if ((type == DIEFT_PERIODIC || type == DIEFT_RAMPFORCE || type == DIEFT_CONSTANTFORCE) && + (collection = set_envelope->collection)) + { + info->dwDynamicParams |= DIEP_ENVELOPE; + if (set_envelope->attack_level_caps) type |= DIEFT_FFATTACK; + if (set_envelope->attack_time_caps) type |= DIEFT_FFATTACK; + if (set_envelope->fade_level_caps) type |= DIEFT_FFFADE; + if (set_envelope->fade_time_caps) type |= DIEFT_FFFADE; + } + info->guid = *guid; info->dwEffType = type; info->dwStaticParams = info->dwDynamicParams; @@ -1846,6 +1868,7 @@ static BOOL init_pid_reports( struct hid_joystick *impl, struct hid_value_caps * struct pid_control_report *effect_control = &impl->pid_effect_control; struct pid_effect_update *effect_update = &impl->pid_effect_update; struct pid_set_periodic *set_periodic = &impl->pid_set_periodic; + struct pid_set_envelope *set_envelope = &impl->pid_set_envelope;
#define SET_COLLECTION( rep ) \ do \ @@ -1872,6 +1895,7 @@ static BOOL init_pid_reports( struct hid_joystick *impl, struct hid_value_caps * case PID_USAGE_EFFECT_OPERATION_REPORT: SET_COLLECTION( effect_control ); break; case PID_USAGE_SET_EFFECT_REPORT: SET_COLLECTION( effect_update ); break; case PID_USAGE_SET_PERIODIC_REPORT: SET_COLLECTION( set_periodic ); break; + case PID_USAGE_SET_ENVELOPE_REPORT: SET_COLLECTION( set_envelope ); 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; @@ -1897,6 +1921,7 @@ static BOOL init_pid_caps( struct hid_joystick *impl, struct hid_value_caps *cap struct pid_control_report *effect_control = &impl->pid_effect_control; struct pid_effect_update *effect_update = &impl->pid_effect_update; struct pid_set_periodic *set_periodic = &impl->pid_set_periodic; + struct pid_set_envelope *set_envelope = &impl->pid_set_envelope;
if (!(instance->dwType & DIDFT_OUTPUT)) return DIENUM_CONTINUE;
@@ -1975,6 +2000,26 @@ static BOOL init_pid_caps( struct hid_joystick *impl, struct hid_value_caps *cap set_periodic->offset_caps = caps; } } + if (instance->wCollectionNumber == set_envelope->collection) + { + SET_REPORT_ID( set_envelope ); + if (instance->wUsage == PID_USAGE_ATTACK_LEVEL) + { + caps->physical_min = 0; + caps->physical_max = 10000; + set_envelope->attack_level_caps = caps; + } + if (instance->wUsage == PID_USAGE_ATTACK_TIME) + set_envelope->attack_time_caps = caps; + if (instance->wUsage == PID_USAGE_FADE_LEVEL) + { + caps->physical_min = 0; + caps->physical_max = 10000; + set_envelope->fade_level_caps = caps; + } + if (instance->wUsage == PID_USAGE_FADE_TIME) + set_envelope->fade_time_caps = caps; + }
#undef SET_REPORT_ID
@@ -2082,12 +2127,19 @@ static HRESULT hid_joystick_create_device( IDirectInputImpl *dinput, const GUID TRACE( "effect update id %u, coll %u, type_coll %u\n", impl->pid_effect_update.id, impl->pid_effect_update.collection, impl->pid_effect_update.type_coll ); TRACE( "set periodic id %u, coll %u\n", impl->pid_set_periodic.id, impl->pid_set_periodic.collection ); + TRACE( "set envelope id %u, coll %u\n", impl->pid_set_envelope.id, impl->pid_set_envelope.collection );
if (impl->pid_device_control.id) { impl->dev_caps.dwFlags |= DIDC_FORCEFEEDBACK; if (impl->pid_effect_update.start_delay_caps) impl->dev_caps.dwFlags |= DIDC_STARTDELAY; + if (impl->pid_set_envelope.attack_level_caps || + impl->pid_set_envelope.attack_time_caps) + impl->dev_caps.dwFlags |= DIDC_FFATTACK; + if (impl->pid_set_envelope.fade_level_caps || + impl->pid_set_envelope.fade_time_caps) + impl->dev_caps.dwFlags |= DIDC_FFFADE; impl->dev_caps.dwFFSamplePeriod = 1000000; impl->dev_caps.dwFFMinTimeResolution = 1000000; impl->dev_caps.dwHardwareRevision = 1; @@ -2171,6 +2223,7 @@ static ULONG WINAPI hid_joystick_effect_Release( IDirectInputEffect *iface ) list_remove( &impl->entry ); LeaveCriticalSection( &impl->joystick->base.crit ); hid_joystick_private_decref( impl->joystick ); + HeapFree( GetProcessHeap(), 0, impl->type_specific_buf[1] ); HeapFree( GetProcessHeap(), 0, impl->type_specific_buf[0] ); HeapFree( GetProcessHeap(), 0, impl->effect_update_buf ); HeapFree( GetProcessHeap(), 0, impl->effect_control_buf ); @@ -2208,6 +2261,9 @@ static HRESULT WINAPI hid_joystick_effect_Initialize( IDirectInputEffect *iface, status = HidP_InitializeReportForID( HidP_Output, joystick->pid_set_periodic.id, joystick->preparsed, impl->type_specific_buf[0], report_len ); if (status != HIDP_STATUS_SUCCESS) return DIERR_DEVICENOTREG; + status = HidP_InitializeReportForID( HidP_Output, joystick->pid_set_envelope.id, joystick->preparsed, + impl->type_specific_buf[1], report_len ); + if (status != HIDP_STATUS_SUCCESS) return DIERR_DEVICENOTREG; break; case PID_USAGE_ET_SPRING: case PID_USAGE_ET_DAMPER: @@ -2218,6 +2274,7 @@ static HRESULT WINAPI hid_joystick_effect_Initialize( IDirectInputEffect *iface, case PID_USAGE_ET_CUSTOM_FORCE_DATA: FIXME( "effect type %#x not implemented!\n", type ); impl->type_specific_buf[0][0] = 0; + impl->type_specific_buf[1][0] = 0; break; }
@@ -2711,6 +2768,7 @@ static HRESULT WINAPI hid_joystick_effect_Download( IDirectInputEffect *iface ) struct hid_joystick_effect *impl = impl_from_IDirectInputEffect( iface ); struct pid_effect_update *effect_update = &impl->joystick->pid_effect_update; struct pid_set_periodic *set_periodic = &impl->joystick->pid_set_periodic; + struct pid_set_envelope *set_envelope = &impl->joystick->pid_set_envelope; ULONG report_len = impl->joystick->caps.OutputReportByteLength; HANDLE device = impl->joystick->device; struct hid_value_caps *caps; @@ -2762,6 +2820,18 @@ static HRESULT WINAPI hid_joystick_effect_Download( IDirectInputEffect *iface )
if (WriteFile( device, impl->type_specific_buf[0], report_len, NULL, NULL )) hr = DI_OK; else hr = DIERR_INPUTLOST; + + set_parameter_value( impl, impl->type_specific_buf[1], set_envelope->attack_level_caps, + impl->envelope.dwAttackLevel ); + set_parameter_value_us( impl, impl->type_specific_buf[1], set_envelope->attack_time_caps, + impl->envelope.dwAttackTime ); + set_parameter_value( impl, impl->type_specific_buf[1], set_envelope->fade_level_caps, + impl->envelope.dwFadeLevel ); + set_parameter_value_us( impl, impl->type_specific_buf[1], set_envelope->fade_time_caps, + impl->envelope.dwFadeTime ); + + if (WriteFile( device, impl->type_specific_buf[1], report_len, NULL, NULL )) hr = DI_OK; + else hr = DIERR_INPUTLOST; break; }
@@ -2874,6 +2944,7 @@ static HRESULT hid_joystick_effect_create( struct hid_joystick *joystick, IDirec if (!(impl->effect_control_buf = HeapAlloc( GetProcessHeap(), 0, report_len ))) goto failed; if (!(impl->effect_update_buf = HeapAlloc( GetProcessHeap(), 0, report_len ))) goto failed; if (!(impl->type_specific_buf[0] = HeapAlloc( GetProcessHeap(), 0, report_len ))) goto failed; + if (!(impl->type_specific_buf[1] = HeapAlloc( GetProcessHeap(), 0, report_len ))) goto failed;
impl->envelope.dwSize = sizeof(DIENVELOPE); impl->params.dwSize = sizeof(DIEFFECT); diff --git a/dlls/dinput8/tests/hid.c b/dlls/dinput8/tests/hid.c index 662953266ef..9c7a6ff7c1d 100644 --- a/dlls/dinput8/tests/hid.c +++ b/dlls/dinput8/tests/hid.c @@ -5099,7 +5099,6 @@ static void test_periodic_effect( IDirectInputDevice8W *device, HANDLE file ) /* set envelope */ { .code = IOCTL_HID_WRITE_REPORT, - .todo = TRUE, .report_id = 6, .report_len = 7, .report_buf = {0x06,0x19,0x4c,0x02,0x00,0x04,0x00}, @@ -6555,7 +6554,6 @@ static void test_force_feedback_joystick( void ) hr = IDirectInputDevice8_GetCapabilities( device, &caps ); ok( hr == DI_OK, "GetCapabilities returned %#x\n", hr ); check_member( caps, expect_caps, "%d", dwSize ); - todo_wine check_member( caps, expect_caps, "%#x", dwFlags ); check_member( caps, expect_caps, "%#x", dwDevType ); check_member( caps, expect_caps, "%d", dwAxes );
Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/dinput8/tests/hid.c | 578 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 534 insertions(+), 44 deletions(-)
diff --git a/dlls/dinput8/tests/hid.c b/dlls/dinput8/tests/hid.c index 9c7a6ff7c1d..35f7ccab4e6 100644 --- a/dlls/dinput8/tests/hid.c +++ b/dlls/dinput8/tests/hid.c @@ -5107,8 +5107,8 @@ static void test_periodic_effect( IDirectInputDevice8W *device, HANDLE file ) { .code = IOCTL_HID_WRITE_REPORT, .report_id = 3, - .report_len = 9, - .report_buf = {0x03,0x01,0x01,0x08,0x01,0x00,0x06,0x00,0x01}, + .report_len = 11, + .report_buf = {0x03,0x01,0x01,0x08,0x01,0x00,0x06,0x00,0x01,0x55,0xd5}, }, /* start command when DIEP_START is set */ { @@ -5771,6 +5771,244 @@ static void test_periodic_effect( IDirectInputDevice8W *device, HANDLE file ) ok( ref == 0, "Release returned %d\n", ref ); }
+static void test_condition_effect( IDirectInputDevice8W *device, HANDLE file ) +{ + struct hid_expect expect_create[] = + { + /* set condition */ + { + .code = IOCTL_HID_WRITE_REPORT, + .todo = TRUE, + .report_id = 7, + .report_len = 8, + .report_buf = {0x07,0x00,0xf9,0x19,0xd9,0xff,0xff,0x99}, + }, + /* set condition */ + { + .code = IOCTL_HID_WRITE_REPORT, + .todo = TRUE, + .report_id = 7, + .report_len = 8, + .report_buf = {0x07,0x00,0x4c,0x3f,0xcc,0x4c,0x33,0x19}, + }, + /* update effect */ + { + .code = IOCTL_HID_WRITE_REPORT, + .todo = TRUE, + .report_id = 3, + .report_len = 11, + .report_buf = {0x03,0x01,0x03,0x08,0x01,0x00,0x06,0x00,0x01,0x55,0x00}, + }, + }; + struct hid_expect expect_create_1[] = + { + /* 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,0x00,0x00}, + }, + }; + struct hid_expect expect_create_2[] = + { + /* 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 = + { + .code = IOCTL_HID_WRITE_REPORT, + .report_id = 2, + .report_len = 4, + .report_buf = {0x02, 0x01, 0x03, 0x00}, + }; + static const DWORD expect_axes[3] = { + DIDFT_ABSAXIS | DIDFT_MAKEINSTANCE( 2 ) | DIDFT_FFACTUATOR, + DIDFT_ABSAXIS | DIDFT_MAKEINSTANCE( 1 ) | DIDFT_FFACTUATOR, + DIDFT_ABSAXIS | DIDFT_MAKEINSTANCE( 0 ) | 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, + }, + }; + static const DIEFFECT expect_desc = + { + .dwSize = sizeof(DIEFFECT), + .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, + }; + struct check_created_effect_params check_params = {0}; + DIENVELOPE envelope = + {.dwSize = sizeof(DIENVELOPE)}; + DICONDITION condition[2] = {0}; + IDirectInputEffect *effect; + LONG directions[4] = {0}; + DWORD axes[4] = {0}; + DIEFFECT desc = + { + .dwSize = sizeof(DIEFFECT), + .dwFlags = DIEFF_SPHERICAL | DIEFF_OBJECTIDS, + .cAxes = 4, + .rgdwAxes = axes, + .rglDirection = directions, + .lpEnvelope = &envelope, + .cbTypeSpecificParams = 2 * 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 ); + todo_wine + ok( hr == DI_OK, "CreateEffect returned %#x\n", hr ); + set_hid_expect( file, NULL, 0 ); + if (hr != DI_OK) return; + + check_params.expect_effect = effect; + hr = IDirectInputDevice8_EnumCreatedEffectObjects( device, check_created_effect_objects, &check_params, 0 ); + ok( hr == DI_OK, "EnumCreatedEffectObjects returned %#x\n", hr ); + ok( check_params.count == 1, "got count %u, expected 1\n", check_params.count ); + + hr = IDirectInputEffect_GetEffectGuid( effect, &guid ); + ok( hr == DI_OK, "GetEffectGuid returned %#x\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 %#x\n", hr ); + check_member( desc, expect_desc, "%u", dwDuration ); + check_member( desc, expect_desc, "%u", dwSamplePeriod ); + check_member( desc, expect_desc, "%u", dwGain ); + check_member( desc, expect_desc, "%#x", dwTriggerButton ); + check_member( desc, expect_desc, "%u", dwTriggerRepeatInterval ); + check_member( desc, expect_desc, "%u", cAxes ); + check_member( desc, expect_desc, "%#x", rgdwAxes[0] ); + check_member( desc, expect_desc, "%#x", rgdwAxes[1] ); + check_member( desc, expect_desc, "%d", rglDirection[0] ); + check_member( desc, expect_desc, "%d", rglDirection[1] ); + check_member( desc, expect_desc, "%u", cbTypeSpecificParams ); + check_member( desc, expect_desc, "%u", dwStartDelay ); + check_member( envelope, expect_envelope, "%u", dwAttackLevel ); + check_member( envelope, expect_envelope, "%u", dwAttackTime ); + check_member( envelope, expect_envelope, "%u", dwFadeLevel ); + check_member( envelope, expect_envelope, "%u", dwFadeTime ); + check_member( condition[0], expect_condition[0], "%d", lOffset ); + check_member( condition[0], expect_condition[0], "%d", lPositiveCoefficient ); + check_member( condition[0], expect_condition[0], "%d", lNegativeCoefficient ); + check_member( condition[0], expect_condition[0], "%u", dwPositiveSaturation ); + check_member( condition[0], expect_condition[0], "%u", dwNegativeSaturation ); + check_member( condition[0], expect_condition[0], "%d", lDeadBand ); + check_member( condition[1], expect_condition[1], "%d", lOffset ); + check_member( condition[1], expect_condition[1], "%d", lPositiveCoefficient ); + check_member( condition[1], expect_condition[1], "%d", lNegativeCoefficient ); + check_member( condition[1], expect_condition[1], "%u", dwPositiveSaturation ); + check_member( condition[1], expect_condition[1], "%u", dwNegativeSaturation ); + check_member( condition[1], expect_condition[1], "%d", lDeadBand ); + + set_hid_expect( file, &expect_destroy, sizeof(expect_destroy) ); + ref = IDirectInputEffect_Release( effect ); + ok( ref == 0, "Release returned %d\n", ref ); + set_hid_expect( file, NULL, 0 ); + + desc = expect_desc; + desc.cAxes = 1; + hr = IDirectInputDevice8_CreateEffect( device, &GUID_Spring, &desc, &effect, NULL ); + ok( hr == DIERR_INVALIDPARAM, "CreateEffect returned %#x\n", hr ); + desc.cbTypeSpecificParams = 1 * sizeof(DICONDITION); + desc.lpvTypeSpecificParams = (void *)&expect_condition[1]; + set_hid_expect( file, expect_create_1, sizeof(expect_create_1) ); + hr = IDirectInputDevice8_CreateEffect( device, &GUID_Spring, &desc, &effect, NULL ); + ok( hr == DI_OK, "CreateEffect returned %#x\n", hr ); + set_hid_expect( file, NULL, 0 ); + + set_hid_expect( file, &expect_destroy, sizeof(expect_destroy) ); + ref = IDirectInputEffect_Release( effect ); + ok( ref == 0, "Release returned %d\n", ref ); + set_hid_expect( file, NULL, 0 ); + + desc = expect_desc; + desc.cAxes = 3; + desc.cbTypeSpecificParams = 1 * sizeof(DICONDITION); + desc.lpvTypeSpecificParams = (void *)&expect_condition[1]; + set_hid_expect( file, expect_create_2, sizeof(expect_create_2) ); + hr = IDirectInputDevice8_CreateEffect( device, &GUID_Spring, &desc, &effect, NULL ); + ok( hr == DI_OK, "CreateEffect returned %#x\n", hr ); + set_hid_expect( file, NULL, 0 ); + + set_hid_expect( file, &expect_destroy, sizeof(expect_destroy) ); + ref = IDirectInputEffect_Release( effect ); + ok( ref == 0, "Release returned %d\n", ref ); + set_hid_expect( file, NULL, 0 ); +} + static void test_force_feedback_joystick( void ) { #include "psh_hid_macros.h" @@ -5885,10 +6123,11 @@ static void test_force_feedback_joystick( void ) 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, 2), + LOGICAL_MAXIMUM(1, 3), PHYSICAL_MINIMUM(1, 1), - PHYSICAL_MAXIMUM(1, 2), + PHYSICAL_MAXIMUM(1, 3), REPORT_SIZE(1, 8), REPORT_COUNT(1, 1), OUTPUT(1, Data|Ary|Abs), @@ -5935,6 +6174,24 @@ static void test_force_feedback_joystick( void ) 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_PERIODIC_REPORT), @@ -5980,6 +6237,66 @@ static void test_force_feedback_joystick( void ) 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), + 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, END_COLLECTION, }; #undef REPORT_ID_OR_USAGE_PAGE @@ -5992,7 +6309,8 @@ static void test_force_feedback_joystick( void ) static const DIDEVCAPS expect_caps = { .dwSize = sizeof(DIDEVCAPS), - .dwFlags = DIDC_FORCEFEEDBACK | DIDC_ATTACHED | DIDC_EMULATED | DIDC_STARTDELAY | DIDC_FFFADE | DIDC_FFATTACK, + .dwFlags = DIDC_FORCEFEEDBACK | DIDC_ATTACHED | DIDC_EMULATED | DIDC_STARTDELAY | + DIDC_FFFADE | DIDC_FFATTACK | DIDC_DEADBAND | DIDC_SATURATION, .dwDevType = DIDEVTYPE_HID | (DI8DEVTYPEJOYSTICK_LIMITED << 8) | DI8DEVTYPE_JOYSTICK, .dwAxes = 3, .dwButtons = 2, @@ -6061,7 +6379,7 @@ static void test_force_feedback_joystick( void ) { .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW), .guidType = GUID_Button, - .dwOfs = 0x38, + .dwOfs = 0x60, .dwType = DIDFT_PSHBUTTON|DIDFT_MAKEINSTANCE(0)|DIDFT_FFEFFECTTRIGGER, .dwFlags = DIDOI_FFEFFECTTRIGGER, .tszName = L"Button 0", @@ -6073,7 +6391,7 @@ static void test_force_feedback_joystick( void ) { .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW), .guidType = GUID_Button, - .dwOfs = 0x39, + .dwOfs = 0x61, .dwType = DIDFT_PSHBUTTON|DIDFT_MAKEINSTANCE(1)|DIDFT_FFEFFECTTRIGGER, .dwFlags = DIDOI_FFEFFECTTRIGGER, .tszName = L"Button 1", @@ -6085,7 +6403,7 @@ static void test_force_feedback_joystick( void ) { .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW), .guidType = GUID_Unknown, - .dwOfs = 0x3a, + .dwOfs = 0x62, .dwType = DIDFT_NODATA|DIDFT_MAKEINSTANCE(5)|DIDFT_OUTPUT, .dwFlags = 0x80008000, .tszName = L"DC Device Reset", @@ -6109,7 +6427,7 @@ static void test_force_feedback_joystick( void ) { .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW), .guidType = GUID_Unknown, - .dwOfs = 0x3b, + .dwOfs = 0x63, .dwType = DIDFT_NODATA|DIDFT_MAKEINSTANCE(7)|DIDFT_OUTPUT, .dwFlags = 0x80008000, .tszName = L"Op Effect Start", @@ -6121,7 +6439,7 @@ static void test_force_feedback_joystick( void ) { .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW), .guidType = GUID_Unknown, - .dwOfs = 0x3c, + .dwOfs = 0x64, .dwType = DIDFT_NODATA|DIDFT_MAKEINSTANCE(8)|DIDFT_OUTPUT, .dwFlags = 0x80008000, .tszName = L"Op Effect Start Solo", @@ -6133,7 +6451,7 @@ static void test_force_feedback_joystick( void ) { .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW), .guidType = GUID_Unknown, - .dwOfs = 0x3d, + .dwOfs = 0x65, .dwType = DIDFT_NODATA|DIDFT_MAKEINSTANCE(9)|DIDFT_OUTPUT, .dwFlags = 0x80008000, .tszName = L"Op Effect Stop", @@ -6169,7 +6487,7 @@ static void test_force_feedback_joystick( void ) { .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW), .guidType = GUID_Unknown, - .dwOfs = 0x3e, + .dwOfs = 0x66, .dwType = DIDFT_NODATA|DIDFT_MAKEINSTANCE(12)|DIDFT_OUTPUT, .dwFlags = 0x80008000, .tszName = L"ET Square", @@ -6181,7 +6499,7 @@ static void test_force_feedback_joystick( void ) { .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW), .guidType = GUID_Unknown, - .dwOfs = 0x3f, + .dwOfs = 0x67, .dwType = DIDFT_NODATA|DIDFT_MAKEINSTANCE(13)|DIDFT_OUTPUT, .dwFlags = 0x80008000, .tszName = L"ET Sine", @@ -6193,9 +6511,21 @@ static void test_force_feedback_joystick( void ) { .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW), .guidType = GUID_Unknown, - .dwOfs = 0x40, + .dwOfs = 0x68, .dwType = DIDFT_NODATA|DIDFT_MAKEINSTANCE(14)|DIDFT_OUTPUT, .dwFlags = 0x80008000, + .tszName = L"ET Spring", + .wCollectionNumber = 8, + .wUsagePage = HID_USAGE_PAGE_PID, + .wUsage = PID_USAGE_ET_SPRING, + .wReportId = 3, + }, + { + .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW), + .guidType = GUID_Unknown, + .dwOfs = 0x69, + .dwType = DIDFT_NODATA|DIDFT_MAKEINSTANCE(15)|DIDFT_OUTPUT, + .dwFlags = 0x80008000, .tszName = L"Z Axis", .wCollectionNumber = 9, .wUsagePage = HID_USAGE_PAGE_GENERIC, @@ -6205,8 +6535,8 @@ static void test_force_feedback_joystick( void ) { .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW), .guidType = GUID_Unknown, - .dwOfs = 0x41, - .dwType = DIDFT_NODATA|DIDFT_MAKEINSTANCE(15)|DIDFT_OUTPUT, + .dwOfs = 0x6a, + .dwType = DIDFT_NODATA|DIDFT_MAKEINSTANCE(16)|DIDFT_OUTPUT, .dwFlags = 0x80008000, .tszName = L"Y Axis", .wCollectionNumber = 9, @@ -6217,8 +6547,8 @@ static void test_force_feedback_joystick( void ) { .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW), .guidType = GUID_Unknown, - .dwOfs = 0x42, - .dwType = DIDFT_NODATA|DIDFT_MAKEINSTANCE(16)|DIDFT_OUTPUT, + .dwOfs = 0x6b, + .dwType = DIDFT_NODATA|DIDFT_MAKEINSTANCE(17)|DIDFT_OUTPUT, .dwFlags = 0x80008000, .tszName = L"X Axis", .wCollectionNumber = 9, @@ -6229,8 +6559,8 @@ static void test_force_feedback_joystick( void ) { .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW), .guidType = GUID_Unknown, - .dwOfs = 0x43, - .dwType = DIDFT_NODATA|DIDFT_MAKEINSTANCE(17)|DIDFT_OUTPUT, + .dwOfs = 0x6c, + .dwType = DIDFT_NODATA|DIDFT_MAKEINSTANCE(18)|DIDFT_OUTPUT, .dwFlags = 0x80008000, .tszName = L"Direction Enable", .wCollectionNumber = 7, @@ -6242,7 +6572,7 @@ static void test_force_feedback_joystick( void ) .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW), .guidType = GUID_Unknown, .dwOfs = 0x18, - .dwType = DIDFT_NODATA|DIDFT_MAKEINSTANCE(18)|DIDFT_OUTPUT, + .dwType = DIDFT_NODATA|DIDFT_MAKEINSTANCE(19)|DIDFT_OUTPUT, .dwFlags = 0x80008000, .tszName = L"Start Delay", .wCollectionNumber = 7, @@ -6256,7 +6586,7 @@ static void test_force_feedback_joystick( void ) .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW), .guidType = GUID_Unknown, .dwOfs = 0x1c, - .dwType = DIDFT_NODATA|DIDFT_MAKEINSTANCE(19)|DIDFT_OUTPUT, + .dwType = DIDFT_NODATA|DIDFT_MAKEINSTANCE(20)|DIDFT_OUTPUT, .dwFlags = 0x80008000, .tszName = L"Duration", .wCollectionNumber = 7, @@ -6270,7 +6600,7 @@ static void test_force_feedback_joystick( void ) .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW), .guidType = GUID_Unknown, .dwOfs = 0x20, - .dwType = DIDFT_NODATA|DIDFT_MAKEINSTANCE(20)|DIDFT_OUTPUT, + .dwType = DIDFT_NODATA|DIDFT_MAKEINSTANCE(21)|DIDFT_OUTPUT, .dwFlags = 0x80008000, .tszName = L"Trigger Button", .wCollectionNumber = 7, @@ -6282,10 +6612,36 @@ static void test_force_feedback_joystick( void ) .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW), .guidType = GUID_Unknown, .dwOfs = 0x24, - .dwType = DIDFT_NODATA|DIDFT_MAKEINSTANCE(21)|DIDFT_OUTPUT, + .dwType = DIDFT_NODATA|DIDFT_MAKEINSTANCE(22)|DIDFT_OUTPUT, .dwFlags = 0x80008000, - .tszName = L"Magnitude", + .tszName = L"Unknown 22", + .wCollectionNumber = 10, + .wUsagePage = HID_USAGE_PAGE_ORDINAL, + .wUsage = 2, + .wReportId = 3, + .wExponent = -2, + }, + { + .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW), + .guidType = GUID_Unknown, + .dwOfs = 0x28, + .dwType = DIDFT_NODATA|DIDFT_MAKEINSTANCE(23)|DIDFT_OUTPUT, + .dwFlags = 0x80008000, + .tszName = L"Unknown 23", .wCollectionNumber = 10, + .wUsagePage = HID_USAGE_PAGE_ORDINAL, + .wUsage = 1, + .wReportId = 3, + .wExponent = -2, + }, + { + .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW), + .guidType = GUID_Unknown, + .dwOfs = 0x2c, + .dwType = DIDFT_NODATA|DIDFT_MAKEINSTANCE(24)|DIDFT_OUTPUT, + .dwFlags = 0x80008000, + .tszName = L"Magnitude", + .wCollectionNumber = 11, .wUsagePage = HID_USAGE_PAGE_PID, .wUsage = PID_USAGE_MAGNITUDE, .wReportId = 5, @@ -6293,11 +6649,11 @@ static void test_force_feedback_joystick( void ) { .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW), .guidType = GUID_Unknown, - .dwOfs = 0x28, - .dwType = DIDFT_NODATA|DIDFT_MAKEINSTANCE(22)|DIDFT_OUTPUT, + .dwOfs = 0x30, + .dwType = DIDFT_NODATA|DIDFT_MAKEINSTANCE(25)|DIDFT_OUTPUT, .dwFlags = 0x80008000, .tszName = L"Fade Level", - .wCollectionNumber = 11, + .wCollectionNumber = 12, .wUsagePage = HID_USAGE_PAGE_PID, .wUsage = PID_USAGE_FADE_LEVEL, .wReportId = 6, @@ -6305,11 +6661,11 @@ static void test_force_feedback_joystick( void ) { .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW), .guidType = GUID_Unknown, - .dwOfs = 0x2c, - .dwType = DIDFT_NODATA|DIDFT_MAKEINSTANCE(23)|DIDFT_OUTPUT, + .dwOfs = 0x34, + .dwType = DIDFT_NODATA|DIDFT_MAKEINSTANCE(26)|DIDFT_OUTPUT, .dwFlags = 0x80008000, .tszName = L"Attack Level", - .wCollectionNumber = 11, + .wCollectionNumber = 12, .wUsagePage = HID_USAGE_PAGE_PID, .wUsage = PID_USAGE_ATTACK_LEVEL, .wReportId = 6, @@ -6317,11 +6673,11 @@ static void test_force_feedback_joystick( void ) { .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW), .guidType = GUID_Unknown, - .dwOfs = 0x30, - .dwType = DIDFT_NODATA|DIDFT_MAKEINSTANCE(24)|DIDFT_OUTPUT, + .dwOfs = 0x38, + .dwType = DIDFT_NODATA|DIDFT_MAKEINSTANCE(27)|DIDFT_OUTPUT, .dwFlags = 0x80008000, .tszName = L"Fade Time", - .wCollectionNumber = 11, + .wCollectionNumber = 12, .wUsagePage = HID_USAGE_PAGE_PID, .wUsage = PID_USAGE_FADE_TIME, .wReportId = 6, @@ -6331,17 +6687,113 @@ static void test_force_feedback_joystick( void ) { .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW), .guidType = GUID_Unknown, - .dwOfs = 0x34, - .dwType = DIDFT_NODATA|DIDFT_MAKEINSTANCE(25)|DIDFT_OUTPUT, + .dwOfs = 0x3c, + .dwType = DIDFT_NODATA|DIDFT_MAKEINSTANCE(28)|DIDFT_OUTPUT, .dwFlags = 0x80008000, .tszName = L"Attack Time", - .wCollectionNumber = 11, + .wCollectionNumber = 12, .wUsagePage = HID_USAGE_PAGE_PID, .wUsage = PID_USAGE_ATTACK_TIME, .wReportId = 6, .dwDimension = 0x1003, .wExponent = -3, }, + { + .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW), + .guidType = GUID_Unknown, + .dwOfs = 0x40, + .dwType = DIDFT_NODATA|DIDFT_MAKEINSTANCE(29)|DIDFT_OUTPUT, + .dwFlags = 0x80008000, + .tszName = L"Unknown 29", + .wCollectionNumber = 14, + .wUsagePage = HID_USAGE_PAGE_ORDINAL, + .wUsage = 2, + .wReportId = 7, + }, + { + .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW), + .guidType = GUID_Unknown, + .dwOfs = 0x44, + .dwType = DIDFT_NODATA|DIDFT_MAKEINSTANCE(30)|DIDFT_OUTPUT, + .dwFlags = 0x80008000, + .tszName = L"Unknown 30", + .wCollectionNumber = 14, + .wUsagePage = HID_USAGE_PAGE_ORDINAL, + .wUsage = 1, + .wReportId = 7, + }, + { + .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW), + .guidType = GUID_Unknown, + .dwOfs = 0x48, + .dwType = DIDFT_NODATA|DIDFT_MAKEINSTANCE(31)|DIDFT_OUTPUT, + .dwFlags = 0x80008000, + .tszName = L"CP Offset", + .wCollectionNumber = 13, + .wUsagePage = HID_USAGE_PAGE_PID, + .wUsage = PID_USAGE_CP_OFFSET, + .wReportId = 7, + }, + { + .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW), + .guidType = GUID_Unknown, + .dwOfs = 0x4c, + .dwType = DIDFT_NODATA|DIDFT_MAKEINSTANCE(32)|DIDFT_OUTPUT, + .dwFlags = 0x80008000, + .tszName = L"Negative Coefficient", + .wCollectionNumber = 13, + .wUsagePage = HID_USAGE_PAGE_PID, + .wUsage = PID_USAGE_NEGATIVE_COEFFICIENT, + .wReportId = 7, + }, + { + .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW), + .guidType = GUID_Unknown, + .dwOfs = 0x50, + .dwType = DIDFT_NODATA|DIDFT_MAKEINSTANCE(33)|DIDFT_OUTPUT, + .dwFlags = 0x80008000, + .tszName = L"Positive Coefficient", + .wCollectionNumber = 13, + .wUsagePage = HID_USAGE_PAGE_PID, + .wUsage = PID_USAGE_POSITIVE_COEFFICIENT, + .wReportId = 7, + }, + { + .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW), + .guidType = GUID_Unknown, + .dwOfs = 0x54, + .dwType = DIDFT_NODATA|DIDFT_MAKEINSTANCE(34)|DIDFT_OUTPUT, + .dwFlags = 0x80008000, + .tszName = L"Negative Saturation", + .wCollectionNumber = 13, + .wUsagePage = HID_USAGE_PAGE_PID, + .wUsage = PID_USAGE_NEGATIVE_SATURATION, + .wReportId = 7, + }, + { + .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW), + .guidType = GUID_Unknown, + .dwOfs = 0x58, + .dwType = DIDFT_NODATA|DIDFT_MAKEINSTANCE(35)|DIDFT_OUTPUT, + .dwFlags = 0x80008000, + .tszName = L"Positive Saturation", + .wCollectionNumber = 13, + .wUsagePage = HID_USAGE_PAGE_PID, + .wUsage = PID_USAGE_POSITIVE_SATURATION, + .wReportId = 7, + }, + { + .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW), + .guidType = GUID_Unknown, + .dwOfs = 0x5c, + .dwType = DIDFT_NODATA|DIDFT_MAKEINSTANCE(36)|DIDFT_OUTPUT, + .dwFlags = 0x80008000, + .tszName = L"Dead Band", + .wCollectionNumber = 13, + .wUsagePage = HID_USAGE_PAGE_PID, + .wUsage = PID_USAGE_DEAD_BAND, + .wReportId = 7, + }, { .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW), .guidType = GUID_Unknown, @@ -6430,18 +6882,44 @@ static void test_force_feedback_joystick( void ) .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW), .guidType = GUID_Unknown, .dwType = DIDFT_COLLECTION|DIDFT_NODATA|DIDFT_MAKEINSTANCE(10), - .tszName = L"Collection 10 - Set Periodic Report", + .tszName = L"Collection 10 - Direction", + .wCollectionNumber = 7, .wUsagePage = HID_USAGE_PAGE_PID, - .wUsage = PID_USAGE_SET_PERIODIC_REPORT, + .wUsage = PID_USAGE_DIRECTION, }, { .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW), .guidType = GUID_Unknown, .dwType = DIDFT_COLLECTION|DIDFT_NODATA|DIDFT_MAKEINSTANCE(11), - .tszName = L"Collection 11 - Set Envelope Report", + .tszName = L"Collection 11 - Set Periodic Report", + .wUsagePage = HID_USAGE_PAGE_PID, + .wUsage = PID_USAGE_SET_PERIODIC_REPORT, + }, + { + .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW), + .guidType = GUID_Unknown, + .dwType = DIDFT_COLLECTION|DIDFT_NODATA|DIDFT_MAKEINSTANCE(12), + .tszName = L"Collection 12 - Set Envelope Report", .wUsagePage = HID_USAGE_PAGE_PID, .wUsage = PID_USAGE_SET_ENVELOPE_REPORT, }, + { + .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW), + .guidType = GUID_Unknown, + .dwType = DIDFT_COLLECTION|DIDFT_NODATA|DIDFT_MAKEINSTANCE(13), + .tszName = L"Collection 13 - Set Condition Report", + .wUsagePage = HID_USAGE_PAGE_PID, + .wUsage = PID_USAGE_SET_CONDITION_REPORT, + }, + { + .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW), + .guidType = GUID_Unknown, + .dwType = DIDFT_COLLECTION|DIDFT_NODATA|DIDFT_MAKEINSTANCE(14), + .tszName = L"Collection 14 - Type Specific Block Offset", + .wCollectionNumber = 13, + .wUsagePage = HID_USAGE_PAGE_PID, + .wUsage = PID_USAGE_TYPE_SPECIFIC_BLOCK_OFFSET, + }, }; const DIEFFECTINFOW expect_effects[] = { @@ -6449,9 +6927,9 @@ static void test_force_feedback_joystick( void ) .dwSize = sizeof(DIEFFECTINFOW), .guid = GUID_Square, .dwEffType = DIEFT_PERIODIC | DIEFT_STARTDELAY | DIEFT_FFFADE | DIEFT_FFATTACK, - .dwStaticParams = DIEP_AXES | DIEP_TYPESPECIFICPARAMS | DIEP_STARTDELAY | + .dwStaticParams = DIEP_AXES | DIEP_DIRECTION | DIEP_TYPESPECIFICPARAMS | DIEP_STARTDELAY | DIEP_DURATION | DIEP_TRIGGERBUTTON | DIEP_ENVELOPE, - .dwDynamicParams = DIEP_AXES | DIEP_TYPESPECIFICPARAMS | DIEP_STARTDELAY | + .dwDynamicParams = DIEP_AXES | DIEP_DIRECTION | DIEP_TYPESPECIFICPARAMS | DIEP_STARTDELAY | DIEP_DURATION | DIEP_TRIGGERBUTTON | DIEP_ENVELOPE, .tszName = L"GUID_Square", }, @@ -6459,11 +6937,21 @@ static void test_force_feedback_joystick( void ) .dwSize = sizeof(DIEFFECTINFOW), .guid = GUID_Sine, .dwEffType = DIEFT_PERIODIC | DIEFT_STARTDELAY | DIEFT_FFFADE | DIEFT_FFATTACK, - .dwStaticParams = DIEP_AXES | DIEP_TYPESPECIFICPARAMS | DIEP_STARTDELAY | + .dwStaticParams = DIEP_AXES | DIEP_DIRECTION | DIEP_TYPESPECIFICPARAMS | DIEP_STARTDELAY | DIEP_DURATION | DIEP_TRIGGERBUTTON | DIEP_ENVELOPE, - .dwDynamicParams = DIEP_AXES | DIEP_TYPESPECIFICPARAMS | DIEP_STARTDELAY | + .dwDynamicParams = DIEP_AXES | DIEP_DIRECTION | DIEP_TYPESPECIFICPARAMS | DIEP_STARTDELAY | DIEP_DURATION | DIEP_TRIGGERBUTTON | DIEP_ENVELOPE, .tszName = L"GUID_Sine", + }, + { + .dwSize = sizeof(DIEFFECTINFOW), + .guid = GUID_Spring, + .dwEffType = DIEFT_CONDITION | DIEFT_STARTDELAY | DIEFT_DEADBAND | DIEFT_SATURATION, + .dwStaticParams = DIEP_AXES | DIEP_DIRECTION | DIEP_TYPESPECIFICPARAMS | DIEP_STARTDELAY | + DIEP_DURATION, + .dwDynamicParams = DIEP_AXES | DIEP_DIRECTION | DIEP_TYPESPECIFICPARAMS | DIEP_STARTDELAY | + DIEP_DURATION, + .tszName = L"GUID_Spring", } };
@@ -6554,6 +7042,7 @@ static void test_force_feedback_joystick( void ) hr = IDirectInputDevice8_GetCapabilities( device, &caps ); ok( hr == DI_OK, "GetCapabilities returned %#x\n", hr ); check_member( caps, expect_caps, "%d", dwSize ); + todo_wine check_member( caps, expect_caps, "%#x", dwFlags ); check_member( caps, expect_caps, "%#x", dwDevType ); check_member( caps, expect_caps, "%d", dwAxes ); @@ -6708,6 +7197,7 @@ static void test_force_feedback_joystick( void ) ok( hr == DIERR_INVALIDPARAM, "SendDeviceData returned %#x\n", hr );
test_periodic_effect( device, file ); + test_condition_effect( device, file );
set_hid_expect( file, &expect_dc_reset, sizeof(expect_dc_reset) ); hr = IDirectInputDevice8_Unacquire( device );
Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/winebus.sys/bus_sdl.c | 30 ++++++++++- dlls/winebus.sys/bus_udev.c | 23 ++++++++- dlls/winebus.sys/hid.c | 91 +++++++++++++++++++++++++++++++++ dlls/winebus.sys/unix_private.h | 13 +++++ 4 files changed, 155 insertions(+), 2 deletions(-)
diff --git a/dlls/winebus.sys/bus_sdl.c b/dlls/winebus.sys/bus_sdl.c index 78de600fda6..36efd5cc850 100644 --- a/dlls/winebus.sys/bus_sdl.c +++ b/dlls/winebus.sys/bus_sdl.c @@ -208,6 +208,10 @@ static BOOL descriptor_add_haptic(struct sdl_device *impl) if (impl->effect_support & SDL_HAPTIC_TRIANGLE) usages[count++] = PID_USAGE_ET_TRIANGLE; if (impl->effect_support & SDL_HAPTIC_SAWTOOTHUP) usages[count++] = PID_USAGE_ET_SAWTOOTH_UP; if (impl->effect_support & SDL_HAPTIC_SAWTOOTHDOWN) usages[count++] = PID_USAGE_ET_SAWTOOTH_DOWN; + if (impl->effect_support & SDL_HAPTIC_SPRING) usages[count++] = PID_USAGE_ET_SPRING; + if (impl->effect_support & SDL_HAPTIC_DAMPER) usages[count++] = PID_USAGE_ET_DAMPER; + if (impl->effect_support & SDL_HAPTIC_INERTIA) usages[count++] = PID_USAGE_ET_INERTIA; + if (impl->effect_support & SDL_HAPTIC_FRICTION) usages[count++] = PID_USAGE_ET_FRICTION;
if (!hid_device_add_physical(&impl->unix_device, usages, count)) return FALSE; @@ -562,7 +566,31 @@ static NTSTATUS sdl_device_physical_effect_update(struct unix_device *iface, BYT case PID_USAGE_ET_DAMPER: case PID_USAGE_ET_INERTIA: case PID_USAGE_ET_FRICTION: - FIXME("not implemented!"); + effect.condition.length = params->duration; + effect.condition.delay = params->start_delay; + effect.condition.button = params->trigger_button; + effect.condition.interval = params->trigger_repeat_interval; + effect.condition.direction.type = SDL_HAPTIC_SPHERICAL; + effect.condition.direction.dir[0] = params->direction[0] * 36000 / 256; + effect.condition.direction.dir[1] = params->direction[1] * 36000 / 256; + if (params->condition_count >= 1) + { + effect.condition.right_sat[0] = params->condition[0].positive_saturation; + effect.condition.left_sat[0] = params->condition[0].negative_saturation; + effect.condition.right_coeff[0] = params->condition[0].positive_coefficient; + effect.condition.left_coeff[0] = params->condition[0].negative_coefficient; + effect.condition.deadband[0] = params->condition[0].dead_band; + effect.condition.center[0] = params->condition[0].center_point_offset; + } + if (params->condition_count >= 2) + { + effect.condition.right_sat[1] = params->condition[1].positive_saturation; + effect.condition.left_sat[1] = params->condition[1].negative_saturation; + effect.condition.right_coeff[1] = params->condition[1].positive_coefficient; + effect.condition.left_coeff[1] = params->condition[1].negative_coefficient; + effect.condition.deadband[1] = params->condition[1].dead_band; + effect.condition.center[1] = params->condition[1].center_point_offset; + } break;
case PID_USAGE_ET_CONSTANT_FORCE: diff --git a/dlls/winebus.sys/bus_udev.c b/dlls/winebus.sys/bus_udev.c index 8a47b232148..251c74574af 100644 --- a/dlls/winebus.sys/bus_udev.c +++ b/dlls/winebus.sys/bus_udev.c @@ -672,6 +672,10 @@ static NTSTATUS build_report_descriptor(struct unix_device *iface, struct udev_d if (test_bit(ffbits, FF_TRIANGLE)) usages[count++] = PID_USAGE_ET_TRIANGLE; if (test_bit(ffbits, FF_SAW_UP)) usages[count++] = PID_USAGE_ET_SAWTOOTH_UP; if (test_bit(ffbits, FF_SAW_DOWN)) usages[count++] = PID_USAGE_ET_SAWTOOTH_DOWN; + if (test_bit(ffbits, FF_SPRING)) usages[count++] = PID_USAGE_ET_SPRING; + if (test_bit(ffbits, FF_DAMPER)) usages[count++] = PID_USAGE_ET_DAMPER; + if (test_bit(ffbits, FF_INERTIA)) usages[count++] = PID_USAGE_ET_INERTIA; + if (test_bit(ffbits, FF_FRICTION)) usages[count++] = PID_USAGE_ET_FRICTION;
if (!hid_device_add_physical(iface, usages, count)) return STATUS_NO_MEMORY; @@ -1014,7 +1018,24 @@ static NTSTATUS lnxev_device_physical_effect_update(struct unix_device *iface, B case PID_USAGE_ET_DAMPER: case PID_USAGE_ET_INERTIA: case PID_USAGE_ET_FRICTION: - FIXME("not implemented!"); + if (params->condition_count >= 1) + { + effect.u.condition[0].right_saturation = params->condition[0].positive_saturation; + effect.u.condition[0].left_saturation = params->condition[0].negative_saturation; + effect.u.condition[0].right_coeff = params->condition[0].positive_coefficient; + effect.u.condition[0].left_coeff = params->condition[0].negative_coefficient; + effect.u.condition[0].deadband = params->condition[0].dead_band; + effect.u.condition[0].center = params->condition[0].center_point_offset; + } + if (params->condition_count >= 2) + { + effect.u.condition[1].right_saturation = params->condition[1].positive_saturation; + effect.u.condition[1].left_saturation = params->condition[1].negative_saturation; + effect.u.condition[1].right_coeff = params->condition[1].positive_coefficient; + effect.u.condition[1].left_coeff = params->condition[1].negative_coefficient; + effect.u.condition[1].deadband = params->condition[1].dead_band; + effect.u.condition[1].center = params->condition[1].center_point_offset; + } break;
case PID_USAGE_ET_CONSTANT_FORCE: diff --git a/dlls/winebus.sys/hid.c b/dlls/winebus.sys/hid.c index c25558a5ac4..1cd9cf82d68 100644 --- a/dlls/winebus.sys/hid.c +++ b/dlls/winebus.sys/hid.c @@ -458,6 +458,18 @@ struct pid_set_envelope UINT16 attack_time; UINT16 fade_time; }; + +struct pid_set_condition +{ + BYTE index; + BYTE condition_index; + BYTE center_point_offset; + BYTE positive_coefficient; + BYTE negative_coefficient; + BYTE positive_saturation; + BYTE negative_saturation; + BYTE dead_band; +}; #include "poppack.h"
static BOOL hid_descriptor_add_set_periodic(struct unix_device *iface) @@ -577,6 +589,52 @@ 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) +{ + struct hid_report_descriptor *desc = &iface->hid_report_descriptor; + const BYTE report_id = ++desc->next_report_id[HidP_Output]; + const BYTE template[] = + { + /* Condition Report Definition */ + USAGE(1, PID_USAGE_SET_CONDITION_REPORT), + COLLECTION(1, Logical), + REPORT_ID(1, report_id), + + USAGE(1, PID_USAGE_EFFECT_BLOCK_INDEX), + LOGICAL_MAXIMUM(1, 0x7f), + LOGICAL_MINIMUM(1, 0x00), + REPORT_SIZE(1, 8), + REPORT_COUNT(1, 1), + OUTPUT(1, Data|Var|Abs), + + USAGE(1, PID_USAGE_CP_OFFSET), + USAGE(1, PID_USAGE_POSITIVE_COEFFICIENT), + USAGE(1, PID_USAGE_NEGATIVE_COEFFICIENT), + LOGICAL_MINIMUM(1, -128), + LOGICAL_MAXIMUM(1, +127), + PHYSICAL_MINIMUM(2, -10000), + PHYSICAL_MAXIMUM(2, +10000), + 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, +10000), + REPORT_SIZE(1, 8), + REPORT_COUNT(1, 3), + OUTPUT(1, Data|Var|Abs), + END_COLLECTION, + }; + + iface->hid_physical.set_condition_report = report_id; + return hid_report_descriptor_append(desc, template, sizeof(template)); +} + BOOL hid_device_add_physical(struct unix_device *iface, USAGE *usages, USHORT count) { struct hid_report_descriptor *desc = &iface->hid_report_descriptor; @@ -729,6 +787,7 @@ BOOL hid_device_add_physical(struct unix_device *iface, USAGE *usages, USHORT co }; BOOL periodic = FALSE; BOOL envelope = FALSE; + BOOL condition = FALSE; ULONG i;
if (!hid_report_descriptor_append(desc, device_control_header, sizeof(device_control_header))) @@ -769,12 +828,19 @@ BOOL hid_device_add_physical(struct unix_device *iface, USAGE *usages, USHORT co usages[i] == PID_USAGE_ET_SAWTOOTH_UP || usages[i] == PID_USAGE_ET_SAWTOOTH_DOWN) periodic = envelope = TRUE; + if (usages[i] == PID_USAGE_ET_SPRING || + usages[i] == PID_USAGE_ET_DAMPER || + usages[i] == PID_USAGE_ET_INERTIA || + usages[i] == PID_USAGE_ET_FRICTION) + condition = TRUE; }
if (periodic && !hid_descriptor_add_set_periodic(iface)) return FALSE; if (envelope && !hid_descriptor_add_set_envelope(iface)) return FALSE; + if (condition && !hid_descriptor_add_set_condition(iface)) + return FALSE;
/* HID nary collection indexes start at 1 */ memcpy(iface->hid_physical.effect_types + 1, usages, count * sizeof(*usages)); @@ -901,6 +967,8 @@ static void hid_device_set_output_report(struct unix_device *iface, HID_XFER_PAC params->direction[1] = report->direction[1];
io->Status = iface->hid_vtbl->physical_effect_update(iface, report->index, params); + + params->condition_count = 0; } } else if (packet->reportId == physical->set_periodic_report) @@ -935,6 +1003,29 @@ static void hid_device_set_output_report(struct unix_device *iface, HID_XFER_PAC params->envelope.fade_time = report->fade_time; } } + else if (packet->reportId == physical->set_condition_report) + { + struct pid_set_condition *report = (struct pid_set_condition *)(packet->reportBuffer + 1); + struct effect_params *params = iface->hid_physical.effect_params + report->index; + struct effect_condition *condition; + UINT index; + + io->Information = sizeof(*report) + 1; + if (packet->reportBufferLen < io->Information) + io->Status = STATUS_BUFFER_TOO_SMALL; + else if ((index = params->condition_count++) >= ARRAY_SIZE(params->condition)) + io->Status = STATUS_INVALID_PARAMETER; + else + { + condition = params->condition + index; + condition->center_point_offset = report->center_point_offset; + condition->positive_coefficient = report->positive_coefficient; + condition->negative_coefficient = report->negative_coefficient; + condition->positive_saturation = report->positive_saturation; + condition->negative_saturation = report->negative_saturation; + condition->dead_band = report->dead_band; + } + } else { io->Information = 0; diff --git a/dlls/winebus.sys/unix_private.h b/dlls/winebus.sys/unix_private.h index 71e6716c7fa..f8c27a73b73 100644 --- a/dlls/winebus.sys/unix_private.h +++ b/dlls/winebus.sys/unix_private.h @@ -45,6 +45,16 @@ struct effect_envelope UINT16 fade_time; };
+struct effect_condition +{ + BYTE center_point_offset; + BYTE positive_coefficient; + BYTE negative_coefficient; + BYTE positive_saturation; + BYTE negative_saturation; + BYTE dead_band; +}; + struct effect_params { USAGE effect_type; @@ -57,11 +67,13 @@ struct effect_params BOOL axis_enabled[2]; BOOL direction_enabled; BYTE direction[2]; + BYTE condition_count; /* only for periodic, constant or ramp forces */ struct effect_envelope envelope; union { struct effect_periodic periodic; + struct effect_condition condition[2]; }; };
@@ -136,6 +148,7 @@ struct hid_physical BYTE effect_update_report; BYTE set_periodic_report; BYTE set_envelope_report; + BYTE set_condition_report; };
struct hid_device_state
Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/dinput/joystick_hid.c | 150 +++++++++++++++++++++++++++++++++++++ dlls/dinput8/tests/hid.c | 5 -- 2 files changed, 150 insertions(+), 5 deletions(-)
diff --git a/dlls/dinput/joystick_hid.c b/dlls/dinput/joystick_hid.c index 735b3800b31..e2c1a125903 100644 --- a/dlls/dinput/joystick_hid.c +++ b/dlls/dinput/joystick_hid.c @@ -124,6 +124,20 @@ struct pid_set_envelope struct hid_value_caps *fade_time_caps; };
+struct pid_set_condition +{ + BYTE id; + ULONG collection; + + // BYTE offset_bits; + struct hid_value_caps *center_point_offset_caps; + struct hid_value_caps *positive_coefficient_caps; + struct hid_value_caps *negative_coefficient_caps; + struct hid_value_caps *positive_saturation_caps; + struct hid_value_caps *negative_saturation_caps; + struct hid_value_caps *dead_band_caps; +}; + #define DEVICE_STATE_MAX_SIZE 1024
struct hid_joystick @@ -158,6 +172,7 @@ struct hid_joystick struct pid_effect_update pid_effect_update; struct pid_set_periodic pid_set_periodic; struct pid_set_envelope pid_set_envelope; + struct pid_set_condition pid_set_condition; };
static inline struct hid_joystick *impl_from_IDirectInputDevice8W( IDirectInputDevice8W *iface ) @@ -178,6 +193,7 @@ struct hid_joystick_effect
DWORD axes[6]; LONG directions[6]; + DICONDITION condition[2]; DIENVELOPE envelope; DIPERIODIC periodic; DIEFFECT params; @@ -1123,6 +1139,7 @@ static HRESULT WINAPI hid_joystick_GetEffectInfo( IDirectInputDevice8W *iface, D { struct hid_joystick *impl = impl_from_IDirectInputDevice8W( iface ); struct pid_effect_update *effect_update = &impl->pid_effect_update; + struct pid_set_condition *set_condition = &impl->pid_set_condition; struct pid_set_periodic *set_periodic = &impl->pid_set_periodic; struct pid_set_envelope *set_envelope = &impl->pid_set_envelope; PHIDP_PREPARSED_DATA preparsed = impl->preparsed; @@ -1219,6 +1236,37 @@ static HRESULT WINAPI hid_joystick_GetEffectInfo( IDirectInputDevice8W *iface, D if (set_envelope->fade_time_caps) type |= DIEFT_FFFADE; }
+ if ((collection = set_condition->collection) && (type == DIEFT_CONDITION)) + { + if (set_condition->center_point_offset_caps) + info->dwDynamicParams |= DIEP_TYPESPECIFICPARAMS; + if (set_condition->positive_coefficient_caps) + { + info->dwDynamicParams |= DIEP_TYPESPECIFICPARAMS; + type |= DIEFT_POSNEGCOEFFICIENTS; + } + if (set_condition->negative_coefficient_caps) + { + info->dwDynamicParams |= DIEP_TYPESPECIFICPARAMS; + type |= DIEFT_POSNEGCOEFFICIENTS; + } + if (set_condition->positive_saturation_caps) + { + info->dwDynamicParams |= DIEP_TYPESPECIFICPARAMS; + type |= DIEFT_SATURATION | DIEFT_POSNEGSATURATION; + } + if (set_condition->negative_saturation_caps) + { + info->dwDynamicParams |= DIEP_TYPESPECIFICPARAMS; + type |= DIEFT_SATURATION | DIEFT_POSNEGSATURATION; + } + if (set_condition->dead_band_caps) + { + info->dwDynamicParams |= DIEP_TYPESPECIFICPARAMS; + type |= DIEFT_DEADBAND; + } + } + info->guid = *guid; info->dwEffType = type; info->dwStaticParams = info->dwDynamicParams; @@ -1867,6 +1915,7 @@ static BOOL init_pid_reports( struct hid_joystick *impl, struct hid_value_caps * struct pid_control_report *device_control = &impl->pid_device_control; struct pid_control_report *effect_control = &impl->pid_effect_control; struct pid_effect_update *effect_update = &impl->pid_effect_update; + struct pid_set_condition *set_condition = &impl->pid_set_condition; struct pid_set_periodic *set_periodic = &impl->pid_set_periodic; struct pid_set_envelope *set_envelope = &impl->pid_set_envelope;
@@ -1896,6 +1945,7 @@ static BOOL init_pid_reports( struct hid_joystick *impl, struct hid_value_caps * case PID_USAGE_SET_EFFECT_REPORT: SET_COLLECTION( effect_update ); break; case PID_USAGE_SET_PERIODIC_REPORT: SET_COLLECTION( set_periodic ); break; case PID_USAGE_SET_ENVELOPE_REPORT: SET_COLLECTION( set_envelope ); break; + case PID_USAGE_SET_CONDITION_REPORT: SET_COLLECTION( set_condition ); 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; @@ -1920,6 +1970,7 @@ static BOOL init_pid_caps( struct hid_joystick *impl, struct hid_value_caps *cap struct pid_control_report *device_control = &impl->pid_device_control; struct pid_control_report *effect_control = &impl->pid_effect_control; struct pid_effect_update *effect_update = &impl->pid_effect_update; + struct pid_set_condition *set_condition = &impl->pid_set_condition; struct pid_set_periodic *set_periodic = &impl->pid_set_periodic; struct pid_set_envelope *set_envelope = &impl->pid_set_envelope;
@@ -1963,6 +2014,8 @@ static BOOL init_pid_caps( struct hid_joystick *impl, struct hid_value_caps *cap if (instance->wCollectionNumber == effect_update->axes_coll) { SET_REPORT_ID( effect_update ); + caps->physical_min = 0; + caps->physical_max = 36000; if (effect_update->axis_count >= 6) FIXME( "more than 6 PID axes detected\n" ); else effect_update->axis_caps[effect_update->axis_count] = caps; effect_update->axis_count++; @@ -2020,6 +2073,46 @@ static BOOL init_pid_caps( struct hid_joystick *impl, struct hid_value_caps *cap if (instance->wUsage == PID_USAGE_FADE_TIME) set_envelope->fade_time_caps = caps; } + if (instance->wCollectionNumber == set_condition->collection) + { + SET_REPORT_ID( set_condition ); + if (instance->wUsage == PID_USAGE_CP_OFFSET) + { + caps->physical_min = -10000; + caps->physical_max = 10000; + set_condition->center_point_offset_caps = caps; + } + if (instance->wUsage == PID_USAGE_POSITIVE_COEFFICIENT) + { + caps->physical_min = -10000; + caps->physical_max = 10000; + set_condition->positive_coefficient_caps = caps; + } + if (instance->wUsage == PID_USAGE_NEGATIVE_COEFFICIENT) + { + caps->physical_min = -10000; + caps->physical_max = 10000; + set_condition->negative_coefficient_caps = caps; + } + if (instance->wUsage == PID_USAGE_POSITIVE_SATURATION) + { + caps->physical_min = 0; + caps->physical_max = 10000; + set_condition->positive_saturation_caps = caps; + } + if (instance->wUsage == PID_USAGE_NEGATIVE_SATURATION) + { + caps->physical_min = 0; + caps->physical_max = 10000; + set_condition->negative_saturation_caps = caps; + } + if (instance->wUsage == PID_USAGE_DEAD_BAND) + { + caps->physical_min = 0; + caps->physical_max = 10000; + set_condition->dead_band_caps = caps; + } + }
#undef SET_REPORT_ID
@@ -2128,6 +2221,7 @@ static HRESULT hid_joystick_create_device( IDirectInputImpl *dinput, const GUID impl->pid_effect_update.collection, impl->pid_effect_update.type_coll ); TRACE( "set periodic id %u, coll %u\n", impl->pid_set_periodic.id, impl->pid_set_periodic.collection ); TRACE( "set envelope id %u, coll %u\n", impl->pid_set_envelope.id, impl->pid_set_envelope.collection ); + TRACE( "set condition id %u, coll %u\n", impl->pid_set_condition.id, impl->pid_set_condition.collection );
if (impl->pid_device_control.id) { @@ -2140,6 +2234,14 @@ static HRESULT hid_joystick_create_device( IDirectInputImpl *dinput, const GUID if (impl->pid_set_envelope.fade_level_caps || impl->pid_set_envelope.fade_time_caps) impl->dev_caps.dwFlags |= DIDC_FFFADE; + if (impl->pid_set_condition.positive_coefficient_caps || + impl->pid_set_condition.negative_coefficient_caps) + impl->dev_caps.dwFlags |= DIDC_POSNEGCOEFFICIENTS; + if (impl->pid_set_condition.positive_saturation_caps || + impl->pid_set_condition.negative_saturation_caps) + impl->dev_caps.dwFlags |= DIDC_SATURATION|DIDC_POSNEGSATURATION; + if (impl->pid_set_condition.dead_band_caps) + impl->dev_caps.dwFlags |= DIDC_DEADBAND; impl->dev_caps.dwFFSamplePeriod = 1000000; impl->dev_caps.dwFFMinTimeResolution = 1000000; impl->dev_caps.dwHardwareRevision = 1; @@ -2269,6 +2371,13 @@ static HRESULT WINAPI hid_joystick_effect_Initialize( IDirectInputEffect *iface, case PID_USAGE_ET_DAMPER: case PID_USAGE_ET_INERTIA: case PID_USAGE_ET_FRICTION: + status = HidP_InitializeReportForID( HidP_Output, joystick->pid_set_condition.id, joystick->preparsed, + impl->type_specific_buf[0], report_len ); + if (status != HIDP_STATUS_SUCCESS) return DIERR_DEVICENOTREG; + status = HidP_InitializeReportForID( HidP_Output, joystick->pid_set_condition.id, joystick->preparsed, + impl->type_specific_buf[1], report_len ); + if (status != HIDP_STATUS_SUCCESS) return DIERR_DEVICENOTREG; + break; case PID_USAGE_ET_CONSTANT_FORCE: case PID_USAGE_ET_RAMP: case PID_USAGE_ET_CUSTOM_FORCE_DATA: @@ -2412,6 +2521,12 @@ static HRESULT WINAPI hid_joystick_effect_GetParameters( IDirectInputEffect *ifa case PID_USAGE_ET_DAMPER: case PID_USAGE_ET_INERTIA: case PID_USAGE_ET_FRICTION: + count = impl->params.cbTypeSpecificParams; + capacity = params->cbTypeSpecificParams; + params->cbTypeSpecificParams = count; + if (capacity < count) return DIERR_MOREDATA; + memcpy( params->lpvTypeSpecificParams, impl->condition, params->cbTypeSpecificParams ); + break; case PID_USAGE_ET_CONSTANT_FORCE: case PID_USAGE_ET_RAMP: case PID_USAGE_ET_CUSTOM_FORCE_DATA: @@ -2554,11 +2669,22 @@ static HRESULT WINAPI hid_joystick_effect_SetParameters( IDirectInputEffect *ifa if (memcmp( &impl->periodic, params->lpvTypeSpecificParams, sizeof(DIPERIODIC) )) impl->modified = TRUE; memcpy( &impl->periodic, params->lpvTypeSpecificParams, sizeof(DIPERIODIC) ); + impl->params.cbTypeSpecificParams = sizeof(DIPERIODIC); break; case PID_USAGE_ET_SPRING: case PID_USAGE_ET_DAMPER: case PID_USAGE_ET_INERTIA: case PID_USAGE_ET_FRICTION: + if ((count = impl->params.cAxes)) + { + if (!params->lpvTypeSpecificParams) return E_POINTER; + if (params->cbTypeSpecificParams != count * sizeof(DICONDITION) && + params->cbTypeSpecificParams != sizeof(DICONDITION)) + return DIERR_INVALIDPARAM; + memcpy( impl->condition, params->lpvTypeSpecificParams, params->cbTypeSpecificParams ); + impl->params.cbTypeSpecificParams = params->cbTypeSpecificParams; + } + break; case PID_USAGE_ET_CONSTANT_FORCE: case PID_USAGE_ET_RAMP: case PID_USAGE_ET_CUSTOM_FORCE_DATA: @@ -2767,6 +2893,7 @@ static HRESULT WINAPI hid_joystick_effect_Download( IDirectInputEffect *iface ) 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; + struct pid_set_condition *set_condition = &impl->joystick->pid_set_condition; struct pid_set_periodic *set_periodic = &impl->joystick->pid_set_periodic; struct pid_set_envelope *set_envelope = &impl->joystick->pid_set_envelope; ULONG report_len = impl->joystick->caps.OutputReportByteLength; @@ -2833,6 +2960,29 @@ static HRESULT WINAPI hid_joystick_effect_Download( IDirectInputEffect *iface ) if (WriteFile( device, impl->type_specific_buf[1], report_len, NULL, NULL )) hr = DI_OK; else hr = DIERR_INPUTLOST; break; + case PID_USAGE_ET_SPRING: + case PID_USAGE_ET_DAMPER: + case PID_USAGE_ET_INERTIA: + case PID_USAGE_ET_FRICTION: + for (i = 0; i < min( 2, impl->params.cbTypeSpecificParams / sizeof(DICONDITION) ); ++i) + { + set_parameter_value( impl, impl->type_specific_buf[i], set_condition->center_point_offset_caps, + impl->condition[i].lOffset ); + set_parameter_value( impl, impl->type_specific_buf[i], set_condition->positive_coefficient_caps, + impl->condition[i].lPositiveCoefficient ); + set_parameter_value( impl, impl->type_specific_buf[i], set_condition->negative_coefficient_caps, + impl->condition[i].lNegativeCoefficient ); + set_parameter_value( impl, impl->type_specific_buf[i], set_condition->positive_saturation_caps, + impl->condition[i].dwPositiveSaturation ); + set_parameter_value( impl, impl->type_specific_buf[i], set_condition->negative_saturation_caps, + impl->condition[i].dwNegativeSaturation ); + set_parameter_value( impl, impl->type_specific_buf[i], set_condition->dead_band_caps, + impl->condition[i].lDeadBand ); + + if (WriteFile( device, impl->type_specific_buf[i], report_len, NULL, NULL )) hr = DI_OK; + else hr = DIERR_INPUTLOST; + } + break; }
set_parameter_value_us( impl, impl->effect_update_buf, effect_update->duration_caps, diff --git a/dlls/dinput8/tests/hid.c b/dlls/dinput8/tests/hid.c index 35f7ccab4e6..cdb19808fcd 100644 --- a/dlls/dinput8/tests/hid.c +++ b/dlls/dinput8/tests/hid.c @@ -5778,7 +5778,6 @@ static void test_condition_effect( IDirectInputDevice8W *device, HANDLE file ) /* set condition */ { .code = IOCTL_HID_WRITE_REPORT, - .todo = TRUE, .report_id = 7, .report_len = 8, .report_buf = {0x07,0x00,0xf9,0x19,0xd9,0xff,0xff,0x99}, @@ -5786,7 +5785,6 @@ static void test_condition_effect( IDirectInputDevice8W *device, HANDLE file ) /* set condition */ { .code = IOCTL_HID_WRITE_REPORT, - .todo = TRUE, .report_id = 7, .report_len = 8, .report_buf = {0x07,0x00,0x4c,0x3f,0xcc,0x4c,0x33,0x19}, @@ -5794,7 +5792,6 @@ static void test_condition_effect( IDirectInputDevice8W *device, HANDLE file ) /* update effect */ { .code = IOCTL_HID_WRITE_REPORT, - .todo = TRUE, .report_id = 3, .report_len = 11, .report_buf = {0x03,0x01,0x03,0x08,0x01,0x00,0x06,0x00,0x01,0x55,0x00}, @@ -5927,10 +5924,8 @@ static void test_condition_effect( IDirectInputDevice8W *device, HANDLE file )
set_hid_expect( file, expect_create, sizeof(expect_create) ); hr = IDirectInputDevice8_CreateEffect( device, &GUID_Spring, &expect_desc, &effect, NULL ); - todo_wine ok( hr == DI_OK, "CreateEffect returned %#x\n", hr ); set_hid_expect( file, NULL, 0 ); - if (hr != DI_OK) return;
check_params.expect_effect = effect; hr = IDirectInputDevice8_EnumCreatedEffectObjects( device, check_created_effect_objects, &check_params, 0 );
Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/winebus.sys/bus_sdl.c | 15 ++++++++- dlls/winebus.sys/bus_udev.c | 8 ++++- dlls/winebus.sys/hid.c | 56 +++++++++++++++++++++++++++++++++ dlls/winebus.sys/unix_private.h | 7 +++++ 4 files changed, 84 insertions(+), 2 deletions(-)
diff --git a/dlls/winebus.sys/bus_sdl.c b/dlls/winebus.sys/bus_sdl.c index 36efd5cc850..14546467863 100644 --- a/dlls/winebus.sys/bus_sdl.c +++ b/dlls/winebus.sys/bus_sdl.c @@ -212,6 +212,7 @@ static BOOL descriptor_add_haptic(struct sdl_device *impl) if (impl->effect_support & SDL_HAPTIC_DAMPER) usages[count++] = PID_USAGE_ET_DAMPER; if (impl->effect_support & SDL_HAPTIC_INERTIA) usages[count++] = PID_USAGE_ET_INERTIA; if (impl->effect_support & SDL_HAPTIC_FRICTION) usages[count++] = PID_USAGE_ET_FRICTION; + if (impl->effect_support & SDL_HAPTIC_CONSTANT) usages[count++] = PID_USAGE_ET_CONSTANT_FORCE;
if (!hid_device_add_physical(&impl->unix_device, usages, count)) return FALSE; @@ -594,8 +595,20 @@ static NTSTATUS sdl_device_physical_effect_update(struct unix_device *iface, BYT break;
case PID_USAGE_ET_CONSTANT_FORCE: - FIXME("not implemented!"); + effect.constant.length = params->duration; + effect.constant.delay = params->start_delay; + effect.constant.button = params->trigger_button; + effect.constant.interval = params->trigger_repeat_interval; + effect.constant.direction.type = SDL_HAPTIC_SPHERICAL; + effect.constant.direction.dir[0] = params->direction[0] * 36000 / 256; + effect.constant.direction.dir[1] = params->direction[1] * 36000 / 256; + effect.constant.level = params->constant_force.magnitude; + effect.constant.attack_length = params->envelope.attack_time; + effect.constant.attack_level = params->envelope.attack_level; + effect.constant.fade_length = params->envelope.fade_time; + effect.constant.fade_level = params->envelope.fade_level; break; + case PID_USAGE_ET_RAMP: FIXME("not implemented!"); break; diff --git a/dlls/winebus.sys/bus_udev.c b/dlls/winebus.sys/bus_udev.c index 251c74574af..6067500117c 100644 --- a/dlls/winebus.sys/bus_udev.c +++ b/dlls/winebus.sys/bus_udev.c @@ -676,6 +676,7 @@ static NTSTATUS build_report_descriptor(struct unix_device *iface, struct udev_d if (test_bit(ffbits, FF_DAMPER)) usages[count++] = PID_USAGE_ET_DAMPER; if (test_bit(ffbits, FF_INERTIA)) usages[count++] = PID_USAGE_ET_INERTIA; if (test_bit(ffbits, FF_FRICTION)) usages[count++] = PID_USAGE_ET_FRICTION; + if (test_bit(ffbits, FF_CONSTANT)) usages[count++] = PID_USAGE_ET_CONSTANT_FORCE;
if (!hid_device_add_physical(iface, usages, count)) return STATUS_NO_MEMORY; @@ -1039,8 +1040,13 @@ static NTSTATUS lnxev_device_physical_effect_update(struct unix_device *iface, B break;
case PID_USAGE_ET_CONSTANT_FORCE: - FIXME("not implemented!"); + effect.u.constant.level = params->constant_force.magnitude; + effect.u.constant.envelope.attack_length = params->envelope.attack_time; + effect.u.constant.envelope.attack_level = params->envelope.attack_level; + effect.u.constant.envelope.fade_length = params->envelope.fade_time; + effect.u.constant.envelope.fade_level = params->envelope.fade_level; break; + case PID_USAGE_ET_RAMP: FIXME("not implemented!"); break; diff --git a/dlls/winebus.sys/hid.c b/dlls/winebus.sys/hid.c index 1cd9cf82d68..4c55367e78d 100644 --- a/dlls/winebus.sys/hid.c +++ b/dlls/winebus.sys/hid.c @@ -470,6 +470,13 @@ struct pid_set_condition BYTE negative_saturation; BYTE dead_band; }; + +struct pid_set_constant_force +{ + BYTE index; + UINT16 magnitude; +}; + #include "poppack.h"
static BOOL hid_descriptor_add_set_periodic(struct unix_device *iface) @@ -635,6 +642,39 @@ static BOOL hid_descriptor_add_set_condition(struct unix_device *iface) return hid_report_descriptor_append(desc, template, sizeof(template)); }
+static BOOL hid_descriptor_add_set_constant_force(struct unix_device *iface) +{ + struct hid_report_descriptor *desc = &iface->hid_report_descriptor; + const BYTE report_id = ++desc->next_report_id[HidP_Output]; + const BYTE template[] = + { + /* Constant Force Report Definition */ + USAGE(1, PID_USAGE_SET_CONSTANT_FORCE_REPORT), + COLLECTION(1, Logical), + REPORT_ID(1, report_id), + + USAGE(1, PID_USAGE_EFFECT_BLOCK_INDEX), + LOGICAL_MAXIMUM(1, 0x7f), + LOGICAL_MINIMUM(1, 0x00), + REPORT_SIZE(1, 8), + REPORT_COUNT(1, 1), + OUTPUT(1, Data|Var|Abs), + + USAGE(1, PID_USAGE_MAGNITUDE), + LOGICAL_MINIMUM(2, 0xff01), + LOGICAL_MAXIMUM(2, 0x00ff), + PHYSICAL_MINIMUM(2, -1000), + PHYSICAL_MAXIMUM(2, 1000), + REPORT_SIZE(1, 16), + REPORT_COUNT(1, 1), + OUTPUT(1, Data|Var|Abs), + END_COLLECTION, + }; + + iface->hid_physical.set_constant_force_report = report_id; + return hid_report_descriptor_append(desc, template, sizeof(template)); +} + BOOL hid_device_add_physical(struct unix_device *iface, USAGE *usages, USHORT count) { struct hid_report_descriptor *desc = &iface->hid_report_descriptor; @@ -788,6 +828,7 @@ BOOL hid_device_add_physical(struct unix_device *iface, USAGE *usages, USHORT co BOOL periodic = FALSE; BOOL envelope = FALSE; BOOL condition = FALSE; + BOOL constant_force = FALSE; ULONG i;
if (!hid_report_descriptor_append(desc, device_control_header, sizeof(device_control_header))) @@ -833,6 +874,8 @@ BOOL hid_device_add_physical(struct unix_device *iface, USAGE *usages, USHORT co usages[i] == PID_USAGE_ET_INERTIA || usages[i] == PID_USAGE_ET_FRICTION) condition = TRUE; + if (usages[i] == PID_USAGE_ET_CONSTANT_FORCE) + envelope = constant_force = TRUE; }
if (periodic && !hid_descriptor_add_set_periodic(iface)) @@ -841,6 +884,8 @@ BOOL hid_device_add_physical(struct unix_device *iface, USAGE *usages, USHORT co return FALSE; if (condition && !hid_descriptor_add_set_condition(iface)) return FALSE; + if (constant_force && !hid_descriptor_add_set_constant_force(iface)) + return FALSE;
/* HID nary collection indexes start at 1 */ memcpy(iface->hid_physical.effect_types + 1, usages, count * sizeof(*usages)); @@ -1026,6 +1071,17 @@ static void hid_device_set_output_report(struct unix_device *iface, HID_XFER_PAC condition->dead_band = report->dead_band; } } + else if (packet->reportId == physical->set_constant_force_report) + { + struct pid_set_constant_force *report = (struct pid_set_constant_force *)(packet->reportBuffer + 1); + struct effect_params *params = iface->hid_physical.effect_params + report->index; + + io->Information = sizeof(*report) + 1; + if (packet->reportBufferLen < io->Information) + io->Status = STATUS_BUFFER_TOO_SMALL; + else + params->constant_force.magnitude = report->magnitude; + } else { io->Information = 0; diff --git a/dlls/winebus.sys/unix_private.h b/dlls/winebus.sys/unix_private.h index f8c27a73b73..bd0e3272593 100644 --- a/dlls/winebus.sys/unix_private.h +++ b/dlls/winebus.sys/unix_private.h @@ -55,6 +55,11 @@ struct effect_condition BYTE dead_band; };
+struct effect_constant_force +{ + UINT16 magnitude; +}; + struct effect_params { USAGE effect_type; @@ -74,6 +79,7 @@ struct effect_params { struct effect_periodic periodic; struct effect_condition condition[2]; + struct effect_constant_force constant_force; }; };
@@ -149,6 +155,7 @@ struct hid_physical BYTE set_periodic_report; BYTE set_envelope_report; BYTE set_condition_report; + BYTE set_constant_force_report; };
struct hid_device_state
Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/dinput/joystick_hid.c | 62 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+)
diff --git a/dlls/dinput/joystick_hid.c b/dlls/dinput/joystick_hid.c index e2c1a125903..fc991f5f093 100644 --- a/dlls/dinput/joystick_hid.c +++ b/dlls/dinput/joystick_hid.c @@ -138,6 +138,13 @@ struct pid_set_condition struct hid_value_caps *dead_band_caps; };
+struct pid_set_constant_force +{ + BYTE id; + ULONG collection; + struct hid_value_caps *magnitude_caps; +}; + #define DEVICE_STATE_MAX_SIZE 1024
struct hid_joystick @@ -173,6 +180,7 @@ struct hid_joystick struct pid_set_periodic pid_set_periodic; struct pid_set_envelope pid_set_envelope; struct pid_set_condition pid_set_condition; + struct pid_set_constant_force pid_set_constant_force; };
static inline struct hid_joystick *impl_from_IDirectInputDevice8W( IDirectInputDevice8W *iface ) @@ -193,6 +201,7 @@ struct hid_joystick_effect
DWORD axes[6]; LONG directions[6]; + DICONSTANTFORCE constant_force; DICONDITION condition[2]; DIENVELOPE envelope; DIPERIODIC periodic; @@ -1912,6 +1921,7 @@ static BOOL init_data_format( struct hid_joystick *impl, struct hid_value_caps * static BOOL init_pid_reports( struct hid_joystick *impl, struct hid_value_caps *caps, DIDEVICEOBJECTINSTANCEW *instance, void *data ) { + struct pid_set_constant_force *set_constant_force = &impl->pid_set_constant_force; struct pid_control_report *device_control = &impl->pid_device_control; struct pid_control_report *effect_control = &impl->pid_effect_control; struct pid_effect_update *effect_update = &impl->pid_effect_update; @@ -1946,6 +1956,7 @@ static BOOL init_pid_reports( struct hid_joystick *impl, struct hid_value_caps * case PID_USAGE_SET_PERIODIC_REPORT: SET_COLLECTION( set_periodic ); break; case PID_USAGE_SET_ENVELOPE_REPORT: SET_COLLECTION( set_envelope ); break; case PID_USAGE_SET_CONDITION_REPORT: SET_COLLECTION( set_condition ); break; + case PID_USAGE_SET_CONSTANT_FORCE_REPORT: SET_COLLECTION( set_constant_force ); 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; @@ -1967,6 +1978,7 @@ static BOOL init_pid_reports( struct hid_joystick *impl, struct hid_value_caps * static BOOL init_pid_caps( struct hid_joystick *impl, struct hid_value_caps *caps, DIDEVICEOBJECTINSTANCEW *instance, void *data ) { + struct pid_set_constant_force *set_constant_force = &impl->pid_set_constant_force; struct pid_control_report *device_control = &impl->pid_device_control; struct pid_control_report *effect_control = &impl->pid_effect_control; struct pid_effect_update *effect_update = &impl->pid_effect_update; @@ -2113,6 +2125,16 @@ static BOOL init_pid_caps( struct hid_joystick *impl, struct hid_value_caps *cap set_condition->dead_band_caps = caps; } } + if (instance->wCollectionNumber == set_constant_force->collection) + { + SET_REPORT_ID( set_constant_force ); + if (instance->wUsage == PID_USAGE_MAGNITUDE) + { + caps->physical_min = -10000; + caps->physical_max = 10000; + set_constant_force->magnitude_caps = caps; + } + }
#undef SET_REPORT_ID
@@ -2222,6 +2244,8 @@ static HRESULT hid_joystick_create_device( IDirectInputImpl *dinput, const GUID TRACE( "set periodic id %u, coll %u\n", impl->pid_set_periodic.id, impl->pid_set_periodic.collection ); TRACE( "set envelope id %u, coll %u\n", impl->pid_set_envelope.id, impl->pid_set_envelope.collection ); TRACE( "set condition id %u, coll %u\n", impl->pid_set_condition.id, impl->pid_set_condition.collection ); + TRACE( "set constant force id %u, coll %u\n", impl->pid_set_constant_force.id, + impl->pid_set_constant_force.collection );
if (impl->pid_device_control.id) { @@ -2379,6 +2403,13 @@ static HRESULT WINAPI hid_joystick_effect_Initialize( IDirectInputEffect *iface, if (status != HIDP_STATUS_SUCCESS) return DIERR_DEVICENOTREG; break; case PID_USAGE_ET_CONSTANT_FORCE: + status = HidP_InitializeReportForID( HidP_Output, joystick->pid_set_constant_force.id, joystick->preparsed, + impl->type_specific_buf[0], report_len ); + if (status != HIDP_STATUS_SUCCESS) return DIERR_DEVICENOTREG; + status = HidP_InitializeReportForID( HidP_Output, joystick->pid_set_envelope.id, joystick->preparsed, + impl->type_specific_buf[1], report_len ); + if (status != HIDP_STATUS_SUCCESS) return DIERR_DEVICENOTREG; + break; case PID_USAGE_ET_RAMP: case PID_USAGE_ET_CUSTOM_FORCE_DATA: FIXME( "effect type %#x not implemented!\n", type ); @@ -2528,6 +2559,10 @@ static HRESULT WINAPI hid_joystick_effect_GetParameters( IDirectInputEffect *ifa memcpy( params->lpvTypeSpecificParams, impl->condition, params->cbTypeSpecificParams ); break; case PID_USAGE_ET_CONSTANT_FORCE: + if (!params->lpvTypeSpecificParams) return E_POINTER; + if (params->cbTypeSpecificParams != sizeof(DICONSTANTFORCE)) return DIERR_INVALIDPARAM; + memcpy( params->lpvTypeSpecificParams, &impl->constant_force, sizeof(DICONSTANTFORCE) ); + break; case PID_USAGE_ET_RAMP: case PID_USAGE_ET_CUSTOM_FORCE_DATA: FIXME( "DIEP_TYPESPECIFICPARAMS not implemented!\n" ); @@ -2686,6 +2721,13 @@ static HRESULT WINAPI hid_joystick_effect_SetParameters( IDirectInputEffect *ifa } break; case PID_USAGE_ET_CONSTANT_FORCE: + if (!params->lpvTypeSpecificParams) return E_POINTER; + if (params->cbTypeSpecificParams != sizeof(DICONSTANTFORCE)) return DIERR_INVALIDPARAM; + if (memcmp( &impl->constant_force, params->lpvTypeSpecificParams, sizeof(DICONSTANTFORCE) )) + impl->modified = TRUE; + memcpy( &impl->constant_force, params->lpvTypeSpecificParams, sizeof(DICONSTANTFORCE) ); + impl->params.cbTypeSpecificParams = sizeof(DICONSTANTFORCE); + break; case PID_USAGE_ET_RAMP: case PID_USAGE_ET_CUSTOM_FORCE_DATA: FIXME( "DIEP_TYPESPECIFICPARAMS not implemented!\n" ); @@ -2892,6 +2934,7 @@ static HRESULT WINAPI hid_joystick_effect_Download( IDirectInputEffect *iface ) { static const DWORD complete_mask = DIEP_AXES | DIEP_DIRECTION | DIEP_TYPESPECIFICPARAMS; struct hid_joystick_effect *impl = impl_from_IDirectInputEffect( iface ); + struct pid_set_constant_force *set_constant_force = &impl->joystick->pid_set_constant_force; struct pid_effect_update *effect_update = &impl->joystick->pid_effect_update; struct pid_set_condition *set_condition = &impl->joystick->pid_set_condition; struct pid_set_periodic *set_periodic = &impl->joystick->pid_set_periodic; @@ -2983,6 +3026,25 @@ static HRESULT WINAPI hid_joystick_effect_Download( IDirectInputEffect *iface ) else hr = DIERR_INPUTLOST; } break; + case PID_USAGE_ET_CONSTANT_FORCE: + set_parameter_value( impl, impl->type_specific_buf[0], set_constant_force->magnitude_caps, + impl->constant_force.lMagnitude ); + + if (WriteFile( device, impl->type_specific_buf[0], report_len, NULL, NULL )) hr = DI_OK; + else hr = DIERR_INPUTLOST; + + set_parameter_value( impl, impl->type_specific_buf[1], set_envelope->attack_level_caps, + impl->envelope.dwAttackLevel ); + set_parameter_value_us( impl, impl->type_specific_buf[1], set_envelope->attack_time_caps, + impl->envelope.dwAttackTime ); + set_parameter_value( impl, impl->type_specific_buf[1], set_envelope->fade_level_caps, + impl->envelope.dwFadeLevel ); + set_parameter_value_us( impl, impl->type_specific_buf[1], set_envelope->fade_time_caps, + impl->envelope.dwFadeTime ); + + if (WriteFile( device, impl->type_specific_buf[1], report_len, NULL, NULL )) hr = DI_OK; + else hr = DIERR_INPUTLOST; + break; }
set_parameter_value_us( impl, impl->effect_update_buf, effect_update->duration_caps,
Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/winebus.sys/bus_sdl.c | 16 ++++++++- dlls/winebus.sys/bus_udev.c | 9 ++++- dlls/winebus.sys/hid.c | 59 +++++++++++++++++++++++++++++++++ dlls/winebus.sys/unix_private.h | 8 +++++ 4 files changed, 90 insertions(+), 2 deletions(-)
diff --git a/dlls/winebus.sys/bus_sdl.c b/dlls/winebus.sys/bus_sdl.c index 14546467863..4fb91e4dc6d 100644 --- a/dlls/winebus.sys/bus_sdl.c +++ b/dlls/winebus.sys/bus_sdl.c @@ -213,6 +213,7 @@ static BOOL descriptor_add_haptic(struct sdl_device *impl) if (impl->effect_support & SDL_HAPTIC_INERTIA) usages[count++] = PID_USAGE_ET_INERTIA; if (impl->effect_support & SDL_HAPTIC_FRICTION) usages[count++] = PID_USAGE_ET_FRICTION; if (impl->effect_support & SDL_HAPTIC_CONSTANT) usages[count++] = PID_USAGE_ET_CONSTANT_FORCE; + if (impl->effect_support & SDL_HAPTIC_RAMP) usages[count++] = PID_USAGE_ET_RAMP;
if (!hid_device_add_physical(&impl->unix_device, usages, count)) return FALSE; @@ -610,8 +611,21 @@ static NTSTATUS sdl_device_physical_effect_update(struct unix_device *iface, BYT break;
case PID_USAGE_ET_RAMP: - FIXME("not implemented!"); + effect.ramp.length = params->duration; + effect.ramp.delay = params->start_delay; + effect.ramp.button = params->trigger_button; + effect.ramp.interval = params->trigger_repeat_interval; + effect.ramp.direction.type = SDL_HAPTIC_SPHERICAL; + effect.ramp.direction.dir[0] = params->direction[0] * 36000 / 256; + effect.ramp.direction.dir[1] = params->direction[1] * 36000 / 256; + effect.ramp.start = params->ramp_force.ramp_start; + effect.ramp.end = params->ramp_force.ramp_end; + effect.ramp.attack_length = params->envelope.attack_time; + effect.ramp.attack_level = params->envelope.attack_level; + effect.ramp.fade_length = params->envelope.fade_time; + effect.ramp.fade_level = params->envelope.fade_level; break; + case PID_USAGE_ET_CUSTOM_FORCE_DATA: FIXME("not implemented!"); break; diff --git a/dlls/winebus.sys/bus_udev.c b/dlls/winebus.sys/bus_udev.c index 6067500117c..cc54d6490be 100644 --- a/dlls/winebus.sys/bus_udev.c +++ b/dlls/winebus.sys/bus_udev.c @@ -677,6 +677,7 @@ static NTSTATUS build_report_descriptor(struct unix_device *iface, struct udev_d if (test_bit(ffbits, FF_INERTIA)) usages[count++] = PID_USAGE_ET_INERTIA; if (test_bit(ffbits, FF_FRICTION)) usages[count++] = PID_USAGE_ET_FRICTION; 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)) return STATUS_NO_MEMORY; @@ -1048,8 +1049,14 @@ static NTSTATUS lnxev_device_physical_effect_update(struct unix_device *iface, B break;
case PID_USAGE_ET_RAMP: - FIXME("not implemented!"); + effect.u.ramp.start_level = params->ramp_force.ramp_start; + effect.u.ramp.end_level = params->ramp_force.ramp_end; + effect.u.ramp.envelope.attack_length = params->envelope.attack_time; + effect.u.ramp.envelope.attack_level = params->envelope.attack_level; + effect.u.ramp.envelope.fade_length = params->envelope.fade_time; + effect.u.ramp.envelope.fade_level = params->envelope.fade_level; break; + case PID_USAGE_ET_CUSTOM_FORCE_DATA: FIXME("not implemented!"); break; diff --git a/dlls/winebus.sys/hid.c b/dlls/winebus.sys/hid.c index 4c55367e78d..a0f07f66392 100644 --- a/dlls/winebus.sys/hid.c +++ b/dlls/winebus.sys/hid.c @@ -477,6 +477,12 @@ struct pid_set_constant_force UINT16 magnitude; };
+struct pid_set_ramp_force +{ + BYTE index; + BYTE ramp_start; + BYTE ramp_end; +}; #include "poppack.h"
static BOOL hid_descriptor_add_set_periodic(struct unix_device *iface) @@ -675,6 +681,40 @@ static BOOL hid_descriptor_add_set_constant_force(struct unix_device *iface) return hid_report_descriptor_append(desc, template, sizeof(template)); }
+static BOOL hid_descriptor_add_set_ramp_force(struct unix_device *iface) +{ + struct hid_report_descriptor *desc = &iface->hid_report_descriptor; + const BYTE report_id = ++desc->next_report_id[HidP_Output]; + const BYTE template[] = + { + /* Ramp Force Report Definition */ + USAGE(1, PID_USAGE_SET_RAMP_FORCE_REPORT), + COLLECTION(1, Logical), + REPORT_ID(1, report_id), + + USAGE(1, PID_USAGE_EFFECT_BLOCK_INDEX), + LOGICAL_MAXIMUM(1, 0x7f), + LOGICAL_MINIMUM(1, 0x00), + REPORT_SIZE(1, 8), + REPORT_COUNT(1, 1), + OUTPUT(1, Data|Var|Abs), + + USAGE(1, PID_USAGE_RAMP_START), + USAGE(1, PID_USAGE_RAMP_END), + LOGICAL_MINIMUM(1, 0x80), + LOGICAL_MAXIMUM(1, 0x7f), + PHYSICAL_MINIMUM(2, -10000), + PHYSICAL_MAXIMUM(2, +10000), + REPORT_SIZE(1, 8), + REPORT_COUNT(1, 2), + OUTPUT(1, Data|Var|Abs), + END_COLLECTION, + }; + + iface->hid_physical.set_ramp_force_report = report_id; + return hid_report_descriptor_append(desc, template, sizeof(template)); +} + BOOL hid_device_add_physical(struct unix_device *iface, USAGE *usages, USHORT count) { struct hid_report_descriptor *desc = &iface->hid_report_descriptor; @@ -829,6 +869,7 @@ BOOL hid_device_add_physical(struct unix_device *iface, USAGE *usages, USHORT co BOOL envelope = FALSE; BOOL condition = FALSE; BOOL constant_force = FALSE; + BOOL ramp_force = FALSE; ULONG i;
if (!hid_report_descriptor_append(desc, device_control_header, sizeof(device_control_header))) @@ -876,6 +917,8 @@ BOOL hid_device_add_physical(struct unix_device *iface, USAGE *usages, USHORT co condition = TRUE; if (usages[i] == PID_USAGE_ET_CONSTANT_FORCE) envelope = constant_force = TRUE; + if (usages[i] == PID_USAGE_ET_RAMP) + envelope = ramp_force = TRUE; }
if (periodic && !hid_descriptor_add_set_periodic(iface)) @@ -886,6 +929,8 @@ BOOL hid_device_add_physical(struct unix_device *iface, USAGE *usages, USHORT co return FALSE; if (constant_force && !hid_descriptor_add_set_constant_force(iface)) return FALSE; + if (ramp_force && !hid_descriptor_add_set_ramp_force(iface)) + return FALSE;
/* HID nary collection indexes start at 1 */ memcpy(iface->hid_physical.effect_types + 1, usages, count * sizeof(*usages)); @@ -1082,6 +1127,20 @@ static void hid_device_set_output_report(struct unix_device *iface, HID_XFER_PAC else params->constant_force.magnitude = report->magnitude; } + else if (packet->reportId == physical->set_ramp_force_report) + { + struct pid_set_ramp_force *report = (struct pid_set_ramp_force *)(packet->reportBuffer + 1); + struct effect_params *params = iface->hid_physical.effect_params + report->index; + + io->Information = sizeof(*report) + 1; + if (packet->reportBufferLen < io->Information) + io->Status = STATUS_BUFFER_TOO_SMALL; + else + { + params->ramp_force.ramp_start = report->ramp_start; + params->ramp_force.ramp_end = report->ramp_end; + } + } else { io->Information = 0; diff --git a/dlls/winebus.sys/unix_private.h b/dlls/winebus.sys/unix_private.h index bd0e3272593..61c6dbb1deb 100644 --- a/dlls/winebus.sys/unix_private.h +++ b/dlls/winebus.sys/unix_private.h @@ -60,6 +60,12 @@ struct effect_constant_force UINT16 magnitude; };
+struct effect_ramp_force +{ + BYTE ramp_start; + BYTE ramp_end; +}; + struct effect_params { USAGE effect_type; @@ -80,6 +86,7 @@ struct effect_params struct effect_periodic periodic; struct effect_condition condition[2]; struct effect_constant_force constant_force; + struct effect_ramp_force ramp_force; }; };
@@ -156,6 +163,7 @@ struct hid_physical BYTE set_envelope_report; BYTE set_condition_report; BYTE set_constant_force_report; + BYTE set_ramp_force_report; };
struct hid_device_state
Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/dinput/joystick_hid.c | 70 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+)
diff --git a/dlls/dinput/joystick_hid.c b/dlls/dinput/joystick_hid.c index fc991f5f093..4bbafa5d6bd 100644 --- a/dlls/dinput/joystick_hid.c +++ b/dlls/dinput/joystick_hid.c @@ -145,6 +145,14 @@ struct pid_set_constant_force struct hid_value_caps *magnitude_caps; };
+struct pid_set_ramp_force +{ + BYTE id; + ULONG collection; + struct hid_value_caps *start_caps; + struct hid_value_caps *end_caps; +}; + #define DEVICE_STATE_MAX_SIZE 1024
struct hid_joystick @@ -181,6 +189,7 @@ struct hid_joystick struct pid_set_envelope pid_set_envelope; struct pid_set_condition pid_set_condition; struct pid_set_constant_force pid_set_constant_force; + struct pid_set_ramp_force pid_set_ramp_force; };
static inline struct hid_joystick *impl_from_IDirectInputDevice8W( IDirectInputDevice8W *iface ) @@ -202,6 +211,7 @@ struct hid_joystick_effect DWORD axes[6]; LONG directions[6]; DICONSTANTFORCE constant_force; + DIRAMPFORCE ramp_force; DICONDITION condition[2]; DIENVELOPE envelope; DIPERIODIC periodic; @@ -1922,6 +1932,7 @@ static BOOL init_pid_reports( struct hid_joystick *impl, struct hid_value_caps * DIDEVICEOBJECTINSTANCEW *instance, void *data ) { struct pid_set_constant_force *set_constant_force = &impl->pid_set_constant_force; + struct pid_set_ramp_force *set_ramp_force = &impl->pid_set_ramp_force; struct pid_control_report *device_control = &impl->pid_device_control; struct pid_control_report *effect_control = &impl->pid_effect_control; struct pid_effect_update *effect_update = &impl->pid_effect_update; @@ -1957,6 +1968,7 @@ static BOOL init_pid_reports( struct hid_joystick *impl, struct hid_value_caps * case PID_USAGE_SET_ENVELOPE_REPORT: SET_COLLECTION( set_envelope ); break; case PID_USAGE_SET_CONDITION_REPORT: SET_COLLECTION( set_condition ); break; 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_CONTROL: SET_SUB_COLLECTION( device_control, control_coll ); break; case PID_USAGE_EFFECT_OPERATION: SET_SUB_COLLECTION( effect_control, control_coll ); break; @@ -1979,6 +1991,7 @@ static BOOL init_pid_caps( struct hid_joystick *impl, struct hid_value_caps *cap DIDEVICEOBJECTINSTANCEW *instance, void *data ) { struct pid_set_constant_force *set_constant_force = &impl->pid_set_constant_force; + struct pid_set_ramp_force *set_ramp_force = &impl->pid_set_ramp_force; struct pid_control_report *device_control = &impl->pid_device_control; struct pid_control_report *effect_control = &impl->pid_effect_control; struct pid_effect_update *effect_update = &impl->pid_effect_update; @@ -2135,6 +2148,22 @@ static BOOL init_pid_caps( struct hid_joystick *impl, struct hid_value_caps *cap set_constant_force->magnitude_caps = caps; } } + if (instance->wCollectionNumber == set_ramp_force->collection) + { + SET_REPORT_ID( set_ramp_force ); + if (instance->wUsage == PID_USAGE_RAMP_START) + { + caps->physical_min = -10000; + caps->physical_max = 10000; + set_ramp_force->start_caps = caps; + } + if (instance->wUsage == PID_USAGE_RAMP_END) + { + caps->physical_min = -10000; + caps->physical_max = 10000; + set_ramp_force->start_caps = caps; + } + }
#undef SET_REPORT_ID
@@ -2246,6 +2275,7 @@ static HRESULT hid_joystick_create_device( IDirectInputImpl *dinput, const GUID TRACE( "set condition id %u, coll %u\n", impl->pid_set_condition.id, impl->pid_set_condition.collection ); TRACE( "set constant force id %u, coll %u\n", impl->pid_set_constant_force.id, 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 );
if (impl->pid_device_control.id) { @@ -2411,6 +2441,13 @@ static HRESULT WINAPI hid_joystick_effect_Initialize( IDirectInputEffect *iface, if (status != HIDP_STATUS_SUCCESS) return DIERR_DEVICENOTREG; break; case PID_USAGE_ET_RAMP: + status = HidP_InitializeReportForID( HidP_Output, joystick->pid_set_ramp_force.id, joystick->preparsed, + impl->type_specific_buf[0], report_len ); + if (status != HIDP_STATUS_SUCCESS) return DIERR_DEVICENOTREG; + status = HidP_InitializeReportForID( HidP_Output, joystick->pid_set_envelope.id, joystick->preparsed, + impl->type_specific_buf[1], report_len ); + if (status != HIDP_STATUS_SUCCESS) return DIERR_DEVICENOTREG; + break; case PID_USAGE_ET_CUSTOM_FORCE_DATA: FIXME( "effect type %#x not implemented!\n", type ); impl->type_specific_buf[0][0] = 0; @@ -2564,6 +2601,10 @@ static HRESULT WINAPI hid_joystick_effect_GetParameters( IDirectInputEffect *ifa memcpy( params->lpvTypeSpecificParams, &impl->constant_force, sizeof(DICONSTANTFORCE) ); break; case PID_USAGE_ET_RAMP: + if (!params->lpvTypeSpecificParams) return E_POINTER; + if (params->cbTypeSpecificParams != sizeof(DIRAMPFORCE)) return DIERR_INVALIDPARAM; + memcpy( params->lpvTypeSpecificParams, &impl->constant_force, sizeof(DIRAMPFORCE) ); + break; case PID_USAGE_ET_CUSTOM_FORCE_DATA: FIXME( "DIEP_TYPESPECIFICPARAMS not implemented!\n" ); return DIERR_UNSUPPORTED; @@ -2729,6 +2770,13 @@ static HRESULT WINAPI hid_joystick_effect_SetParameters( IDirectInputEffect *ifa impl->params.cbTypeSpecificParams = sizeof(DICONSTANTFORCE); break; case PID_USAGE_ET_RAMP: + if (!params->lpvTypeSpecificParams) return E_POINTER; + if (params->cbTypeSpecificParams != sizeof(DIRAMPFORCE)) return DIERR_INVALIDPARAM; + if (memcmp( &impl->constant_force, params->lpvTypeSpecificParams, sizeof(DIRAMPFORCE) )) + impl->modified = TRUE; + memcpy( &impl->constant_force, params->lpvTypeSpecificParams, sizeof(DIRAMPFORCE) ); + impl->params.cbTypeSpecificParams = sizeof(DIRAMPFORCE); + break; case PID_USAGE_ET_CUSTOM_FORCE_DATA: FIXME( "DIEP_TYPESPECIFICPARAMS not implemented!\n" ); return DIERR_UNSUPPORTED; @@ -2935,6 +2983,7 @@ static HRESULT WINAPI hid_joystick_effect_Download( IDirectInputEffect *iface ) static const DWORD complete_mask = DIEP_AXES | DIEP_DIRECTION | DIEP_TYPESPECIFICPARAMS; struct hid_joystick_effect *impl = impl_from_IDirectInputEffect( iface ); struct pid_set_constant_force *set_constant_force = &impl->joystick->pid_set_constant_force; + struct pid_set_ramp_force *set_ramp_force = &impl->joystick->pid_set_ramp_force; struct pid_effect_update *effect_update = &impl->joystick->pid_effect_update; struct pid_set_condition *set_condition = &impl->joystick->pid_set_condition; struct pid_set_periodic *set_periodic = &impl->joystick->pid_set_periodic; @@ -3033,6 +3082,27 @@ static HRESULT WINAPI hid_joystick_effect_Download( IDirectInputEffect *iface ) if (WriteFile( device, impl->type_specific_buf[0], report_len, NULL, NULL )) hr = DI_OK; else hr = DIERR_INPUTLOST;
+ set_parameter_value( impl, impl->type_specific_buf[1], set_envelope->attack_level_caps, + impl->envelope.dwAttackLevel ); + set_parameter_value_us( impl, impl->type_specific_buf[1], set_envelope->attack_time_caps, + impl->envelope.dwAttackTime ); + set_parameter_value( impl, impl->type_specific_buf[1], set_envelope->fade_level_caps, + impl->envelope.dwFadeLevel ); + set_parameter_value_us( impl, impl->type_specific_buf[1], set_envelope->fade_time_caps, + impl->envelope.dwFadeTime ); + + if (WriteFile( device, impl->type_specific_buf[1], report_len, NULL, NULL )) hr = DI_OK; + else hr = DIERR_INPUTLOST; + break; + case PID_USAGE_ET_RAMP: + set_parameter_value( impl, impl->type_specific_buf[0], set_ramp_force->start_caps, + impl->ramp_force.lStart ); + set_parameter_value( impl, impl->type_specific_buf[0], set_ramp_force->end_caps, + impl->ramp_force.lEnd ); + + if (WriteFile( device, impl->type_specific_buf[0], report_len, NULL, NULL )) hr = DI_OK; + else hr = DIERR_INPUTLOST; + set_parameter_value( impl, impl->type_specific_buf[1], set_envelope->attack_level_caps, impl->envelope.dwAttackLevel ); set_parameter_value_us( impl, impl->type_specific_buf[1], set_envelope->attack_time_caps,