Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Windows Joystick Impl for Gamepad Trigger Separate Polling #2428

Open
ShadowMarker789 opened this issue Feb 27, 2023 · 8 comments
Open

Windows Joystick Impl for Gamepad Trigger Separate Polling #2428

ShadowMarker789 opened this issue Feb 27, 2023 · 8 comments

Comments

@ShadowMarker789
Copy link

Change Windows Joystick Implementation to a modern API that allows the consuming programmer to query the two analog triggers of the gamepad device separately.

The current sf::Joystick implementation uses Windows DirectInput v8 which has limitations and is deprecated.

Below exerpt from Microsoft Documentation

Note
Use of legacy DirectInput is not recommended, and DirectInput is not available for Windows Store apps.
...
The Xbox Controller is properly enumerated on DirectInput, and can be used with the DirectInputAPIs. However, some functionality provided by XInput will be missing from the DirectInput implementation:
The left and right trigger buttons will act as a single button, not independently
The vibration effects will not be available
Querying for headset devices will not be available
The combination of the left and right triggers in DirectInput is by design. Games have always assumed that DirectInput device axes are centered when there is no user interaction with the device. However, the Xbox controller was designed to register minimum value, not center, when the triggers are not being held. Older games would therefore assume user interaction.

Microsoft recommended moving to XInput from DirectInput a while ago, really pushing for Gamepad standardization on windows gaming machines, but it's also simultaneously reasonable to want to be able to support multiple different kinds of gaming controllers and not just XInput-capable ones.

The newer gaming-input APIs in modern windows have newer APIs that let one do this.

The most compelling candidate to me is Windows.Gaming.Input::RawGameController

Which itself has an API not too dissimilar to that of sf::Joystick

AxisCount
The number of axes on the raw game controller.
ButtonCount
The number of buttons on the raw game controller.
DisplayName
The name of the controller, provided by the hardware.
GetCurrentReading(Boolean[], GameControllerSwitchPosition[], Double[])
Gets a snapshot of the raw game controller's state.

Being the ones that immediately caught my eye.

Limitations and negatives

Windows Minimum Version Supported

Device family | Windows 10 Creators Update (introduced in 10.0.15063.0)

Which is akin to Windows 10 any version, so that won't work on Windows 7. I would like to raise a question and discussion about what version(s) of Windows SFML is looking to target as a minimum version.

Focus

This API only gives you non-zero input when the window is in focus. If current focused window is not owned by the calling process, then a zero input is given regardless of the actual state of the controller.

Potentially a sticky issue - Has anyone here playing a game with multiple displays trailed their cursor over to the other display to use a separate app while still controlling the game with the offhand on their controller?

Alternative APIs

RawInput

Even lower-level and ancient in comparison to DirectInput. It will report the axes separately, but it is a pain to use, and I wouldn't want to be a maintainer for RawInput. If you do your flags right on registration you can get out-of-focus controller input, but it'll be restricted to a particular HWND for the calling process's WndProc

GameInput

Newer than Windows.Gaming.Input, but not reliably present on consumer machines, I believe it was pushed out in a windows update not that long ago, and there are redistributables for GameInput

XInput

Older than Windows.Gaming.Input, with multiple versions available, v 1.4 being the newest. Restricted to gamepad-style controllers though, and it does give you input regardless of window focus.

Multiple?

Potentially one can query for the presence of libraries at runtime and selectively use consumers of said libraries depending on what can be found.

In this way, the joystick initializer could ...

Test for GameInput presence (LoadLibraryW of gameinput.dll) and if present, use a consumer of GameInput to implement the joystick eventing.

If not found, check for Windows minimum version - if it's greater than or equal to 10.0.15063.0, use WGI

If not meeting the min-ver, fallback to DirectInput v8 as per the current implementation.

This would let most users use the axes of the gamepad triggers independently, and still have some functionality for those poor users stuck on Windows 7.

HOWEVER - it would require that the relevant headers for WGI and GameInput are not directly linked, they would need to be treated differently, as we still want the build application to be able to run without the presence of those libraries! (This implies a large amount of programming work)

Your environment

Windows, this is an issue/discussion that covers multiple versions.

Steps to reproduce

(I'm not actually a C/C++ developer so I can't make a simple example app to demonstrate the linking of the two axes on Windows, sorry!)

Expected behavior

The axes for the gamepad controllers' left and right triggers should be reported independently as two separate buttons or axes.

Actual behavior

There is a single axes reported for both triggers, holding down both triggers reports the same as holding down neither trigger.

This is potentially a very large discussion block, very happy to have feedback on and replies for discussing this and SFML's joystick implementations going forward.

@MarioLiebisch
Copy link
Member

Never looked too deep into the new input library, but I originally wanted to implement XInput for Force Feedback, which I then dropped due to the limitation to 4 gamepads/joysticks. And combining/aligning both together is kind of a huge PITA already. I don't think that would be different for the Windows Gaming thing (which I think is more or less just an extra UWP layer/API on top of XInput).

XInput has the advantage of providing built-in force feedback possibilities.

As a downside, there's still the two different input standards: A DirectInput controllers won't necessarily be recognized by XInput, unless the driver provides both (and vice-versa). This makes workarounds necessary, such as the rather popular x360ce. At least as far as Steam games goes, this wouldn't be a problem (since Steam Input is providing the necessary wrappers as a layer between).

@ShadowMarker789
Copy link
Author

Never looked too deep into the new input library, but I originally wanted to implement XInput for Force Feedback, which I then dropped due to the limitation to 4 gamepads/joysticks. And combining/aligning both together is kind of a huge PITA already. I don't think that would be different for the Windows Gaming thing (which I think is more or less just an extra UWP layer/API on top of XInput).

XInput has the advantage of providing built-in force feedback possibilities.

As a downside, there's still the two different input standards: A DirectInput controllers won't necessarily be recognized by XInput, unless the driver provides both (and vice-versa). This makes workarounds necessary, such as the rather popular x360ce. At least as far as Steam games goes, this wouldn't be a problem (since Steam Input is providing the necessary wrappers as a layer between).

Regarding device-count, I'm not seeing any upper limit on devices in either Windows.Gaming.Input nor GameInput.
If I were to put together a test-application, would you be able to test that it works with 5+ devices? (I do not own that many devices, and I do not have the budget to throw around money right now, as I'm planning my wedding)

Both of these APIs provide means to do vibration / force-feedback.

@MarioLiebisch
Copy link
Member

Ah, sorry, I completely missed the reply. I think I could get 5+ devices ready, but I also just looked around and seems as if the new API doesn't utilize XInput and therefore has a limit of 8 devices.

@ShadowMarker789
Copy link
Author

After some testing I can confirm that RawInput also is not able to distinguish between left and right triggers, so I see no reason to consider it over the current DirectInput implementation.

@kojackdev
Copy link

I've been experimenting with GameInput as an alternative to directinput and xinput in my own input library, so here's some things I've found (although note my experience with GameInput is just 1 day worth so far).

The issue of xbox controllers merging the trigger axes when not accessed by XInput unfortunately affects other low level APIs like RawInput and HID. IIRC, DirectInput is fed data from RawInput internally, so MS expose Xbox controllers as HID devices with the merged triggers. This means HID level, RawInput (which is HID data passed through the windows message system) and DirectInput all end up with the same issue. XInput can see the real data coming from the controller, which is separate trigger axes.

SFML can't really move to XInput only, that would block all non XBox peripherals. Sticking to just DirectInput means everything works, but XBox controllers have no vibration and the merged triggers (but other gamepads are fine).

One option is to do both. MS provide some code (about 100 lines) that can detect if a directinput device is actually an xbox controller. If it is,
reject it and use XInput instead. https://learn.microsoft.com/en-us/windows/win32/xinput/xinput-and-directinput
I don't think it would be too difficult to have SFML's joystick code internally choose between the two.

GameInput is an interesting option. It works with all DirectInput compatible game style devices (joysticks, gamepads, rudder pedals, wheels, etc) but also gives XInput features to Xbox controllers (separate triggers, vibration). The code is easier than DirectInput, but a little harder than XInput.
The biggest issues I see with GameInput:

  • Only newer versions of Windows 10 and above support it.
  • I see people on steam forums and other places trying to force the GameInput service to not run because they think its bad or something.
  • It may be inconvenient for builds.
    For the last one... the issue is GameInput is part of the MS Game Development Kit. This comes as a 300MB installer that then downloads 1.2GB of SDK. The GameInput part of that is a 2KB lib file and a 64KB header (amongst 34 other GDK headers, it doesn't appear to depend on any of them). But legally we most likely can't just copy the lib and headers into SFML, MS tend to not like redistributing SDK pieces. Needing users to install 1.2GB just for better xbox controller support is not great when only 66KB total of that is actually needed.

I do like it though. I'm replacing my own DirectInput and XInput code with just GameInput. One particular thing I like in it is it assigns persistent IDs to devices that will be consistent over disconnects and even system reboots. Identical devices are even identified uniquely as long as they don't change usb ports. So it's great for anything with complex input configuration (like flight sims).

On the issue of focus, GameInput does allow out of focus reading of some devices (gamepads, joysticks, etc) but not others (mouse and keyboard). There might be flags for the mouse/keyboard to change that, I haven't looked. But this proposal wasn't about replacing the keyboard and mouse code in SFML too.

@MarioLiebisch
Copy link
Member

MarioLiebisch commented Jan 2, 2024

SFML can't really move to XInput only, that would block all non XBox peripherals.

Many modern games actually only support XInput, so it wouldn't be that off, but there'd still be the maximum number of devices as an issue.

One option is to do both. MS provide some code [...]

Yep, I remember looking at that, but never got around testing it.

Only newer versions of Windows 10 and above support it.

Since Windows 7, 8, and 8.1 are all in their end of life with no security updates etc. I'd say this is a non-issue.

I see people on steam forums and other places trying to force the GameInput service [...]

No offense to anyone, but some people can be overly paranoid. But also are you sure you're not confusing this with SteamInput, which provides its own emulation layer (which might cause issues with some game/hardware combinations)?

It may be inconvenient for builds.

I can certainly see that, but at the same time the Windows/Platform SDK itself is rather big already (and I was pretty sure the headers are included already; I might be off and/or this might have changed, too).

On the issue of focus, GameInput does allow out of focus reading of some devices (gamepads, joysticks, etc) but not others (mouse and keyboard).

SFML can/could/should just continue using the classic hooks for this. I see no reason to change that. As far as I'm aware GameInput has this in there mostly for Xbox development.

@Bambo-Borris
Copy link
Contributor

Bambo-Borris commented Jan 2, 2024

I've been experimenting with GameInput as an alternative to directinput and xinput in my own input library, so here's some things I've found (although note my experience with GameInput is just 1 day worth so far).

The issue of xbox controllers merging the trigger axes when not accessed by XInput unfortunately affects other low level APIs like RawInput and HID. IIRC, DirectInput is fed data from RawInput internally, so MS expose Xbox controllers as HID devices with the merged triggers. This means HID level, RawInput (which is HID data passed through the windows message system) and DirectInput all end up with the same issue. XInput can see the real data coming from the controller, which is separate trigger axes.

SFML can't really move to XInput only, that would block all non XBox peripherals. Sticking to just DirectInput means everything works, but XBox controllers have no vibration and the merged triggers (but other gamepads are fine).

One option is to do both. MS provide some code (about 100 lines) that can detect if a directinput device is actually an xbox controller. If it is,
reject it and use XInput instead. https://learn.microsoft.com/en-us/windows/win32/xinput/xinput-and-directinput
I don't think it would be too difficult to have SFML's joystick code internally choose between the two.

GameInput is an interesting option. It works with all DirectInput compatible game style devices (joysticks, gamepads, rudder pedals, wheels, etc) but also gives XInput features to Xbox controllers (separate triggers, vibration). The code is easier than DirectInput, but a little harder than XInput.
The biggest issues I see with GameInput:

  • Only newer versions of Windows 10 and above support it.
  • I see people on steam forums and other places trying to force the GameInput service to not run because they think its bad or something.
  • It may be inconvenient for builds.
    For the last one... the issue is GameInput is part of the MS Game Development Kit. This comes as a 300MB installer that then downloads 1.2GB of SDK. The GameInput part of that is a 2KB lib file and a 64KB header (amongst 34 other GDK headers, it doesn't appear to depend on any of them). But legally we most likely can't just copy the lib and headers into SFML, MS tend to not like redistributing SDK pieces. Needing users to install 1.2GB just for better xbox controller support is not great when only 66KB total of that is actually needed.

I do like it though. I'm replacing my own DirectInput and XInput code with just GameInput. One particular thing I like in it is it assigns persistent IDs to devices that will be consistent over disconnects and even system reboots. Identical devices are even identified uniquely as long as they don't change usb ports. So it's great for anything with complex input configuration (like flight sims).

On the issue of focus, GameInput does allow out of focus reading of some devices (gamepads, joysticks, etc) but not others (mouse and keyboard). There might be flags for the mouse/keyboard to change that, I haven't looked. But this proposal wasn't about replacing the keyboard and mouse code in SFML too.

All of this aligns with my own and @ShadowMarker789's research on this topic. We added a page to the wiki comparing the various options for gamepad support on Windows. I think the GameInput option is purely impossible as it stands now. The Microsoft docs say it will be merged into the Windows SDK at some point in the future, maybe this is when SFML could rely on it. I think a combination of XInput + DirectInput is the best way to properly support Xbox controllers & other vendor joysticks, there's already an example of this being done by an SFML user who wrote a Gamepad library and open sourced it.

My current game I rely only on XInput and swerve sf::Joystick entirely on Windows. Valve's support for PS controllers, and DS4Win leave me disinterested in venturing down GameInput currently (given its requirements), and DirectInput is just insufficient with the triggers issue. I think the landscape for joystick support on Windows will remain fragmented or troublesome until GameInput becomes accessible outside of GDK.

@kojackdev
Copy link

Yeah, GameInput does seem less suited the more I use it (it's been an interesting 48 hours of experimenting). It sounds good on paper, but I'm finding issues like aggregate keyboard/mouse devices not currently implemented (so I'm getting 6 mice and 6 keyboards), device display names don't work for me, no min/max axes queries, etc. Getting it working with a range of hardware was pretty easy (already got Virpil HOTAS with 60 button throttle, PS5 controller, xbox controller, rudder pedals, arcade stick, etc), but my DirectInput code already supports all of those and using the xbox detection code lets me use xinput just for xbox controllers.

Adding XInput to sf::Joystick while keeping DirectInput seems the best way to go and would allow better xbox controller use while still supporting things like play station controllers.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

6 participants