FFB Autocenter introduced in https://gitlab.winehq.org/wine/wine/-/merge_requests/4911 had one major misunderstanding.
The USB PID standard doesn't actually define any explicit way to autocenter a device. One could of course use the spring effect with a deadzone of 0 and dead band of 0. This is what I'm actually working on for the Linux PID driver (spring + friction/damper).
Some devices implement autocenter in firmware when they receive the DC Disable Actuators command. Very few, if not just one, implement this weird autocenter effect on slot 1. This is, from what I can gather, only implemented on the MS SideWinder joystick(s) and the Windows' USB PID driver is created around these devices.
Windows PID driver is a bit out of spec, is quite permissive when it comes to fields missing in the descriptor (basically, only effect types and their effect type blocks are optional). Another thing it does is handling of this out-of-spec autocentering for their joysticks. Funnliy enough, the creator of the Linux PID driver based the initial code on testing with MS Sidewinder so it's autocentering is supported.
This is where the autocentering mentioned in the MR comes from. It's not the directinput api that does it but the Windows PID driver. As such, autocentering on reset should be left to the drivers, not handeled by Wine.
SDL lacks full reset support and Linux is even more barebones, whre it's not even possible to query the device state, effects etc (something I'm working on slowly). As such, when games send out RESET to prepare the device, the device starts autocentering for no good reason and the effect is not removed once other effect are uploaded and played which would be the case for MS sidewinder.
In any case, even Windows in inconsistent and I think the directinput documentation has some errors. directinput ffb api is largely based on USB PID but:


Testing with my Moza Racing R9 wheelbase, the DC Reset PID command is sent, but there's no DC Disable Actuators in sight. The games still call reset then enable actuators though.
tl;dr Set autocentering to 0 instead of max value when DISFFC_RESET is received to remove the unwanted autocenter behavior.
Signed-off-by: Tomasz Pakuła tomasz.pakula.oficjalny@gmail.com
-- v5: winebus: Do not touch autocenter on device init and device reset
From: Tomasz Pakuła tomasz.pakula.oficjalny@gmail.com
FFB Autocenter introduced in https://gitlab.winehq.org/wine/wine/-/merge_requests/4911 had one major misunderstanding.
The USB PID standard doesn't actually define any explicit way to autocenter a device. One could of course use the spring effect with a deadzone of 0 and dead band of 0. This is what I'm actually working on for the Linux PID driver (spring + friction/damper).
Some devices implement autocenter in firmware when they receive the DC Disable Actuators command. Very few, if not just one, implement this weird autocenter effect on slot 1. This is, from what I can gather, only implemented on the MS SideWinder joystick(s) and the Windows' USB PID driver is created around these devices.
Windows PID driver is a bit out of spec, is quite permissive when it comes to fields missing in the descriptor (basically, only effect types and their effect type blocks are optional). Another thing it does is handling of this out-of-spec autocentering for their joysticks. Funnliy enough, the creator of the Linux PID driver based the initial code on testing with MS Sidewinder so it's autocentering is supported.
This is where the autocentering mentioned in the MR comes from. It's not the directinput api that does it but the Windows PID driver. As such, autocentering on reset should be left to the drivers, not handeled by Wine.
SDL lacks full reset support and Linux is even more barebones, whre it's not even possible to query the device state, effects etc (something I'm working on slowly). As such, when games send out RESET to prepare the device, the device starts autocentering for no good reason and the effect is not removed once other effect are uploaded and played which would be the case for MS sidewinder.
tl;dr Set autocentering to 0 instead of max value when DISFFC_RESET is reveived to remove the unwanted autocenter behavior.
Signed-off-by: Tomasz Pakuła tomasz.pakula.oficjalny@gmail.com --- dlls/winebus.sys/bus_sdl.c | 2 -- dlls/winebus.sys/bus_udev.c | 2 -- 2 files changed, 4 deletions(-)
diff --git a/dlls/winebus.sys/bus_sdl.c b/dlls/winebus.sys/bus_sdl.c index 5cec049a845..665e4d2eeb3 100644 --- a/dlls/winebus.sys/bus_sdl.c +++ b/dlls/winebus.sys/bus_sdl.c @@ -550,7 +550,6 @@ 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); @@ -560,7 +559,6 @@ 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); diff --git a/dlls/winebus.sys/bus_udev.c b/dlls/winebus.sys/bus_udev.c index 39b810b7588..f3b210890b0 100644 --- a/dlls/winebus.sys/bus_udev.c +++ b/dlls/winebus.sys/bus_udev.c @@ -911,7 +911,6 @@ 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) @@ -921,7 +920,6 @@ 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");
On Thu Jul 31 17:03:22 2025 +0000, Tomasz Pakuła wrote:
Well, I guess just not touching autocenter on evdev and sdl would be enough for now. Autocenter would still work with hidraw. At least with evdev, I have hopes that proper deice control will be in this year
This sounds reasonable to me.
@twhitehead which Wine backend / device did you implement this for initially? Could this be better fixed somehow through hidraw or better linux kernel driver support?
Okay, so the current change only removes the autocenter modification from evdev and SDL bus. I think that's at least the best way forward as I don't think SDL doesn't explicitly consider any autocenter state it's default and evdev for sure doesn't.
The USB info for my device is
ID 045e:001b Microsoft Corp. SideWinder Force Feedback 2 Joystick
I could certainly implement something like this in `hid-pidff` but without the proper device control plumbing, it would still be hit-and miss. Currently, we could just enable/disable autocenter based on the effect count.
On Thu Jul 31 16:54:02 2025 +0000, Tomasz Pakuła wrote:
I meant specifically PID devices. Simucube, old simagic firmware, Cammus etc.
Ah sorry, then i genuinely don't know for sure, but from our testing, nor Simagic, nor VRS, nor Cammus, nor Simucube has this autocentering at first effect. OpenFFBoard does have internal autocentering while FF_ACTUATOR is disabled, and this autocenter is not triggerable from userspace at all.
Sorry, i thought you meant "any joystick in linux that has autocentering", because this issue specifically affects them
On Thu Jul 31 17:10:07 2025 +0000, Tomasz Pakuła wrote:
The USB info for my device is
ID 045e:001b Microsoft Corp. SideWinder Force Feedback 2 Joystick
I could certainly implement something like this in `hid-pidff` but without the proper device control plumbing, it would still be hit-and miss. Currently, we could just enable/disable autocenter based on the effect count.
Shouldn't this just work OOTB through hidraw?
On Thu Jul 31 17:19:37 2025 +0000, Tomasz Pakuła wrote:
I think I'm unclear on what "user release the device" means in `DIPROPAUTOCENTER_ON: The device should automatically center when the user releases the device.`
This means, physically, when user releases the handle(wheel, etc), device should return to the center. It does not necessarily means that device should have some way of 'hands detection', simple spring is just fine for it
On Thu Jul 31 17:10:47 2025 +0000, Rémi Bernon wrote:
Shouldn't this just work OOTB through hidraw?
@rbernon I implemented this for the Microsoft Sidewinder Force Feedback 2 Joystick (ID 045e:001b). My initial use case was just my OS's (NixOS) default wine install. I have since switched to using my OS's default Valve (Proton) install in order to use OpenVR too.
The problem that I first ran into was that autocenter was permanently enabled on my device and overlaid on all effects as `DIPROPAUTOCENTER_OFF` was just ignored. So I implemented support for it based on testing with fedit.exe (the MS Force Edit program included in DirectX SDK 8.1) under Windows (using Wireshark to sniff the USB bus) and under Wine in Linux.
I can't recall what bus it was. But I did mess around with them all. Wound up submitting a few patches upstream to SDL too to improve the state of things there too. As an example, I noticed the square wave effect didn't work under the SDL bus. Turned out this was due to a [lack of bits](https://github.com/libsdl-org/SDL/issues/8753) for some of the newer rumble effects.
I am actually still not using it for the flight simulation software though as it uses [`EnumEffectsInFile` too](https://learn.microsoft.com/en-us/previous-versions/windows/desktop/ee418645...)), and that isn't currently implemented in wine. Some [initial work was done on this back in the day](https://list.winehq.org/pipermail/wine-devel/2014-February/103057.html) in python though, so I hope to get some time at some point and get that cleaned up and ported over and included in wine too.
On Thu Jul 31 20:52:11 2025 +0000, Tyson Whitehead wrote:
@rbernon I implemented this for the Microsoft Sidewinder Force Feedback 2 Joystick (ID 045e:001b). My initial use case was just my OS's (NixOS) default wine install. I have since switched to using my OS's default Valve (Proton) install in order to use OpenVR too. The problem that I first ran into was that autocenter was permanently enabled on my device and overlaid on all effects as `DIPROPAUTOCENTER_OFF` was just ignored. So I implemented support for it based on testing with fedit.exe (the MS Force Edit program included in DirectX SDK 8.1) under Windows (using Wireshark to sniff the USB bus) and under Wine in Linux. I can't recall what bus it was. But I did mess around with them all. Wound up submitting a few patches upstream to SDL too to improve the state of things there too. As an example, I noticed the square wave effect didn't work under the SDL bus. Turned out this was due to a [lack of bits](https://github.com/libsdl-org/SDL/issues/8753) for some of the newer rumble effects. I am actually still not using it for the flight simulation software though as it uses [`EnumEffectsInFile` too](https://learn.microsoft.com/en-us/previous-versions/windows/desktop/ee418645...)), and that isn't currently implemented in wine. Some [initial work was done on this back in the day](https://list.winehq.org/pipermail/wine-devel/2014-February/103057.html) in python though, so I hope to get some time at some point and get that cleaned up and ported over and included in wine too.
Oh yeah, the autocenter not disabling itself was again the fault of the `hid-pidff` driver. It was made about 20 years ago and never updated and it had some bugs + was very strict.
Together with @JacKeTUs we've made heavy updates, fixes and mofifications to the Linux PID driver and we're still doing further work but the initial big batch was only upstreamed in Linux 6.15.
From that, I pivoted into trying to work on the ffb api as a whole to improve its functionality. I think, in the end, it will be very similar to how Directinout does things. Minus a few annoyances.
If I am not mistaken, the latest version of this commit no longer makes the ``` pSDL_HapticSetAutocenter(impl->sdl_haptic, 0); ``` or ``` lnxev_device_physical_device_set_autocenter(iface, 0); ``` calls one the `PID_USAGE_DC_STOP_ALL_EFFECTS` command for, respectively, the SDL and UDEV busses.
My understanding of the code is this will leave me back in my initial situation of having autocenter turned on on my device and overlaying all the effects despite the program having specified the `DIPROPAUTOCENTER_OFF` property as autocenter on is on by default [under the stock linux kernel driver](https://github.com/torvalds/linux/blob/cbbf0a759ff96c80dfc32192a2cc427b79447...
Really, I am find with whatever is best. I just want to be able to make my Sidewinder 2 work without have to patch my kernel or wine. I don't mind setting a registry entry or environment variable.
I did have a look through @rbernon's commit https://gitlab.winehq.org/wine/wine/-/merge_requests/8686 and I believe it meets this criteria. I think it will guess wrong be by default on my device as it doesn't look like it exports an autocenter entry in sysfs, but it lets me set a registry entry to get around this.
One thing that I did wonder about is if it would be a good idea to have autocentering semantics (quirks?) specified in the udev device database given this is the usual spot for that sort of thing.
On Thu Jul 31 23:23:45 2025 +0000, Tyson Whitehead wrote:
If I am not mistaken, the latest version of this commit no longer makes the
pSDL_HapticSetAutocenter(impl->sdl_haptic, 0);
or
lnxev_device_physical_device_set_autocenter(iface, 0);
calls on the `PID_USAGE_DC_STOP_ALL_EFFECTS` command for, respectively, the SDL and UDEV busses. My understanding is, as autocenter is on by default [under the stock linux kernel driver](https://github.com/torvalds/linux/blob/cbbf0a759ff96c80dfc32192a2cc427b79447...), this will leave me back with autocentering turned on on my device and overlaying all the effects despite the program having explicitly specified the `DIPROPAUTOCENTER_OFF` property.
Your understanding is wrong. I redid the reset sequence a few months back and if the device was empty (no effects were uploaded), the driver will send reset, stop all effects, enable actuators.
Autocenter will be on only on device init.
On Thu Jul 31 23:23:45 2025 +0000, Tomasz Pakuła wrote:
Your understanding is wrong. I redid the reset sequence a few months back and if the device was empty (no effects were uploaded), the driver will send reset, stop all effects, enable actuators. Autocenter will be on only on device init.
At the end of the day, you can just use the hidraw mode for your joystick to get better compatibility
On Thu Jul 31 23:32:50 2025 +0000, Tomasz Pakuła wrote:
At the end of the day, you can just use the hidraw mode for your joystick to get better compatibility
Right. I see [that commit in upstream now](https://github.com/torvalds/linux/commit/cb3fd788e3fa5358602a49809c4eb491153...) (looks like you have done a ton of work on fixing up the upstream driver like you said, thanks very much for that!). So, if I update my kernel, I shouldn't windup with autocenter being on despite the application having specified `DIPROPAUTOCENTER_OFF` if wine were to not send any autocenter commands.
I guess the flip side of that is that `DIPROPAUTOCENTER_ON` won't work. That said, I don't know if there are any programs that actually use that. Certainly not the one I am interested in. It would seem a bit odd to want autocentering overlaid on all your other effects. As the [kernel documentation says](https://www.kernel.org/doc/html/latest/input/ff.html) _the autocenter feature quite disturbs the rendering of effects_.
Will this have an effect on programs that want to just use it as a joystick and know nothing about force feedback? I guess I should update to the latest kernel and spend some time testing things out. :slight_smile:
On Thu Jul 31 21:02:10 2025 +0000, Tomasz Pakuła wrote:
Oh yeah, the autocenter not disabling itself was again the fault of the `hid-pidff` driver. It was made about 20 years ago and never updated and it had some bugs + was very strict. Together with @JacKeTUs we've made heavy updates, fixes and mofifications to the Linux PID driver and we're still doing further work but the initial big batch was only upstreamed in Linux 6.15. From that, I pivoted into trying to work on the ffb api as a whole to improve its functionality. I think, in the end, it will be very similar to how Directinout does things. Minus a few annoyances.
@TomaszPakula Someone may have mentioned this already, but could it be that ETS2/ATS doesn't set `DIPROPAUTOCENTER_OFF` and that is the reason that `PID_USAGE_DC_STOP_ALL_EFFECTS` isn't set on reset? That is, the developers just tested on wheels where the default was off and never noticed they should really be setting `DIPROPAUTOCENTER_OFF` to ensure this.
On Fri Aug 1 00:04:10 2025 +0000, Tyson Whitehead wrote:
@TomaszPakula Someone may have mentioned this already, but could it be that ETS2/ATS doesn't set `DIPROPAUTOCENTER_OFF` and that is the reason that `PID_USAGE_DC_STOP_ALL_EFFECTS` isn't set on reset? That is, the developers just tested on wheels where the default was off and never noticed they should really be setting `DIPROPAUTOCENTER_OFF` to ensure this.
I'll have to test again. Maybe they are actually chcecking if a device even supports autocenter and only disable it it if the support is there. Hard to say without having another device to test with.
Was thinking a bit more about this. A windows program can specify * nothing, * `DIPROPAUTOCENTER_OFF`, or * `DIPROPAUTOCENTER_ON`
Clearly if the program explicitly specifies `DIPROPAUTOCENTER_ON` or `DIPROPAUTOCENTER_OFF` there is only one correct thing to do, and that is give it what it wants. So then, maybe this whole thing is really about what happens when nothing is specified?
My initial patch had [this code](https://gitlab.winehq.org/wine/wine/-/blob/42a63687cd6991de03402d81bf79b773a...) ``` if (impl->base.autocenter == DIPROPAUTOCENTER_OFF) hid_joystick_send_force_feedback_command( iface, DISFFC_STOPALL, FALSE ); ``` which makes it go * nothing → autocenter on (just a DISFFC_RESET is sent)
but maybe what we really want is ``` if (impl->base.autocenter != DIPROPAUTOCENTER_ON) hid_joystick_send_force_feedback_command( iface, DISFFC_STOPALL, FALSE ); ``` which would make it go * nothing → autocenter off (DISFFC_RESET, and DISFFC_STOPALL are sent)
@TomaszPakula would this change give you correct behaviour for your devices with ETS2/ATS?
I guess you would probably also want to change [the initial value](https://gitlab.winehq.org/wine/wine/-/blob/42a63687cd6991de03402d81bf79b773a...) from ``` device->autocenter = DIPROPAUTOCENTER_ON; ``` to ``` device->autocenter = DIPROPAUTOCENTER_OFF; ```
I'm thinking the advantage of this over these patches so far is that it continues to honour a program explicitly setting either `DIPROPAUTOCENTER_OFF` and `DIPROPAUTOCENTER_ON`, compared to the other proposals, which I believe cause explicitly setting one these two to silently fail.
For example, I believe the current version of the patch, which just does away with the SDL/UDEV autocenter calls, would mean that, for the SDL/UDEV buses, specifying `DIPROPAUTOCENTER_OFF` won't work on old kernels and specifying `DIPROPAUTOCENTER_ON` won't work for new kernels. While arguably the later is better than the former, it still seems pretty unideal.
Thoughts?
On Fri Aug 1 11:57:39 2025 +0000, Tyson Whitehead wrote:
Was thinking a bit more about this. A windows program can specify
- nothing,
- `DIPROPAUTOCENTER_OFF`, or
- `DIPROPAUTOCENTER_ON`
Clearly if the program explicitly specifies `DIPROPAUTOCENTER_ON` or `DIPROPAUTOCENTER_OFF` there is only one correct thing to do, and that is give it what it wants. So then, maybe this whole thing is really about what happens when nothing is specified? My initial patch had [this code](https://gitlab.winehq.org/wine/wine/-/blob/42a63687cd6991de03402d81bf79b773a...)
if (impl->base.autocenter == DIPROPAUTOCENTER_OFF) hid_joystick_send_force_feedback_command( iface, DISFFC_STOPALL, FALSE );
which makes it go
- nothing → autocenter on (just a DISFFC_RESET is sent)
but maybe what we really want is
if (impl->base.autocenter != DIPROPAUTOCENTER_ON) hid_joystick_send_force_feedback_command( iface, DISFFC_STOPALL, FALSE );
which would make it go
- nothing → autocenter off (DISFFC_RESET, and DISFFC_STOPALL are sent)
@TomaszPakula would this change give you correct behaviour for your devices with ETS2/ATS?
Wow. That wasn't though out. `impl->base.autocenter` is a boolean, so those two are exactly the same. Setting the initial value as in the comment below should be all that is required.
On Fri Aug 1 00:08:49 2025 +0000, Tomasz Pakuła wrote:
I'll have to test again. Maybe they are actually chcecking if a device even supports autocenter and only disable it it if the support is there. Hard to say without having another device to test with.
After https://gitlab.winehq.org/wine/wine/-/merge_requests/8686 there's support for enabling hidraw for specific VID/PID directly in the registry (see `...\WineBus\Devices<VID/PID>\HidRaw` documentation in https://wiki.winehq.org/Useful_Registry_Keys).
This can now be used to configure prefixes to use hidraw for the Sidewinder joystick. (Note that this also probably need some udev rules as well if hidraw is not enabled by default, but that's outside of Wine scope)
Note that I have also created https://gitlab.winehq.org/wine/wine/-/merge_requests/8721 which would allow these options to be set through a new environment variable. I think it is useful but I'm not yet sure whether @julliard will like to have a new public environment variable.
With this I think we can now have this MR merged, and leave autocenter handling decision to each device Linux driver.
This merge request was approved by Rémi Bernon.
Eh sorry, you'll need to fix the build first. Probably just need to remove the now unused functions.
On Wed Aug 6 09:33:34 2025 +0000, Rémi Bernon wrote:
Eh sorry, you'll need to fix the build first. Probably just need to remove the now unused functions.
I'll get on that, though sad that we can't leave them in for now as they will be useful in the future. Well, I'll save it for myself and reintroduce them later