dinput: Implement DIPROP_AUTOCENTER.

Autocenter is a default effect playing on the device following power
up or reset. It is disabled by stopping all effects. On at least
some devices (all?) it is a spring effect playing in slot 1.
Capturing Windows USB packets reveals it dinput acquire does (1) a
reset (this enabled autocenter) and, if autocenter is disabled, (2)
a stop all effects (this disabled autocenter).

This logic works regardless of whether autocenter is a spring effect
playing in slot 1 or not. It does mean autocenter can only be set
when the device is not acquired. Testing on Windows reveals setting
autocenter properties while acquired returns DIERR_ACQUIRED even
if the device is exclusively acquired, so this is consistent.
This commit is contained in:
Tyson Whitehead 2024-01-12 23:20:42 -05:00 committed by Alexandre Julliard
parent 63562c05f6
commit 9dc1ddf801
5 changed files with 32 additions and 5 deletions

View file

@ -899,7 +899,7 @@ static HRESULT check_property( struct dinput_device *impl, const GUID *guid, con
switch (LOWORD( guid ))
{
case (DWORD_PTR)DIPROP_AUTOCENTER:
if (impl->status == STATUS_ACQUIRED && !is_exclusively_acquired( impl )) return DIERR_ACQUIRED;
if (impl->status == STATUS_ACQUIRED) return DIERR_ACQUIRED;
break;
case (DWORD_PTR)DIPROP_AXISMODE:
case (DWORD_PTR)DIPROP_BUFFERSIZE:
@ -1290,8 +1290,6 @@ static HRESULT dinput_device_set_property( IDirectInputDevice8W *iface, const GU
{
const DIPROPDWORD *value = (const DIPROPDWORD *)header;
if (!(impl->caps.dwFlags & DIDC_FORCEFEEDBACK)) return DIERR_UNSUPPORTED;
FIXME( "DIPROP_AUTOCENTER stub!\n" );
impl->autocenter = value->dwData;
return DI_OK;
}
@ -2131,6 +2129,7 @@ void dinput_device_init( struct dinput_device *device, const struct dinput_devic
device->caps.dwSize = sizeof(DIDEVCAPS);
device->caps.dwFlags = DIDC_ATTACHED | DIDC_EMULATED;
device->device_gain = 10000;
device->autocenter = DIPROPAUTOCENTER_ON;
device->force_feedback_state = DIGFFS_STOPPED | DIGFFS_EMPTY;
InitializeCriticalSection( &device->crit );
dinput_internal_addref( (device->dinput = dinput) );

View file

@ -1072,7 +1072,12 @@ static HRESULT hid_joystick_send_force_feedback_command( IDirectInputDevice8W *i
if (status != HIDP_STATUS_SUCCESS) return status;
if (!WriteFile( impl->device, report_buf, report_len, NULL, NULL )) return DIERR_INPUTLOST;
if (!unacquire && command == DISFFC_RESET) hid_joystick_send_device_gain( iface, impl->base.device_gain );
if (!unacquire && command == DISFFC_RESET)
{
if (impl->base.autocenter == DIPROPAUTOCENTER_OFF)
hid_joystick_send_force_feedback_command( iface, DISFFC_STOPALL, FALSE );
hid_joystick_send_device_gain( iface, impl->base.device_gain );
}
return DI_OK;
}

View file

@ -2222,7 +2222,6 @@ static BOOL test_force_feedback_joystick( DWORD version )
.report_len = 2,
.report_buf = {1, 0x02},
.broken_id = 8, /* Win8 sends them in the reverse order */
.todo = TRUE,
},
{
.code = IOCTL_HID_WRITE_REPORT,

View file

@ -105,6 +105,7 @@ MAKE_FUNCPTR(SDL_HapticRumbleStop);
MAKE_FUNCPTR(SDL_HapticRumbleSupported);
MAKE_FUNCPTR(SDL_HapticRunEffect);
MAKE_FUNCPTR(SDL_HapticSetGain);
MAKE_FUNCPTR(SDL_HapticSetAutocenter);
MAKE_FUNCPTR(SDL_HapticStopAll);
MAKE_FUNCPTR(SDL_HapticStopEffect);
MAKE_FUNCPTR(SDL_HapticUnpause);
@ -550,6 +551,7 @@ static NTSTATUS sdl_device_physical_device_control(struct unix_device *iface, US
return STATUS_SUCCESS;
case PID_USAGE_DC_STOP_ALL_EFFECTS:
pSDL_HapticStopAll(impl->sdl_haptic);
pSDL_HapticSetAutocenter(impl->sdl_haptic, 0);
return STATUS_SUCCESS;
case PID_USAGE_DC_DEVICE_RESET:
pSDL_HapticStopAll(impl->sdl_haptic);
@ -559,6 +561,7 @@ static NTSTATUS sdl_device_physical_device_control(struct unix_device *iface, US
pSDL_HapticDestroyEffect(impl->sdl_haptic, impl->effect_ids[i]);
impl->effect_ids[i] = -1;
}
pSDL_HapticSetAutocenter(impl->sdl_haptic, 100);
return STATUS_SUCCESS;
case PID_USAGE_DC_DEVICE_PAUSE:
pSDL_HapticPause(impl->sdl_haptic);
@ -1121,6 +1124,7 @@ NTSTATUS sdl_bus_init(void *args)
LOAD_FUNCPTR(SDL_HapticRumbleSupported);
LOAD_FUNCPTR(SDL_HapticRunEffect);
LOAD_FUNCPTR(SDL_HapticSetGain);
LOAD_FUNCPTR(SDL_HapticSetAutocenter);
LOAD_FUNCPTR(SDL_HapticStopAll);
LOAD_FUNCPTR(SDL_HapticStopEffect);
LOAD_FUNCPTR(SDL_HapticUnpause);

View file

@ -929,6 +929,24 @@ static NTSTATUS lnxev_device_physical_effect_run(struct lnxev_device *impl, BYTE
return STATUS_SUCCESS;
}
static NTSTATUS lnxev_device_physical_device_set_autocenter(struct unix_device *iface, BYTE percent)
{
struct lnxev_device *impl = lnxev_impl_from_unix_device(iface);
struct input_event ie =
{
.type = EV_FF,
.code = FF_AUTOCENTER,
.value = 0xffff * percent / 100,
};
TRACE("iface %p, percent %#x.\n", iface, percent);
if (write(impl->base.device_fd, &ie, sizeof(ie)) == -1)
WARN("write failed %d %s\n", errno, strerror(errno));
return STATUS_SUCCESS;
}
static NTSTATUS lnxev_device_physical_device_control(struct unix_device *iface, USAGE control)
{
struct lnxev_device *impl = lnxev_impl_from_unix_device(iface);
@ -972,6 +990,7 @@ static NTSTATUS lnxev_device_physical_device_control(struct unix_device *iface,
if (impl->effect_ids[i] < 0) continue;
lnxev_device_physical_effect_run(impl, i, 0);
}
lnxev_device_physical_device_set_autocenter(iface, 0);
return STATUS_SUCCESS;
case PID_USAGE_DC_DEVICE_RESET:
for (i = 0; i < ARRAY_SIZE(impl->effect_ids); ++i)
@ -981,6 +1000,7 @@ static NTSTATUS lnxev_device_physical_device_control(struct unix_device *iface,
WARN("couldn't free effect, EVIOCRMFF ioctl failed: %d %s\n", errno, strerror(errno));
impl->effect_ids[i] = -1;
}
lnxev_device_physical_device_set_autocenter(iface, 100);
return STATUS_SUCCESS;
case PID_USAGE_DC_DEVICE_PAUSE:
WARN("device pause not supported\n");