-
Notifications
You must be signed in to change notification settings - Fork 21
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
fix(CompositeDevice): Ensure proper handling of chord events in a way steam can understand them. #32
Conversation
pastaq
commented
Apr 1, 2024
- Add default configuration for steam that covers 99% of handhelds.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The main issue I see with the current implementation is that the sleep
calls that happen when performing these chords will halt the processing of all events for the CompositeDevice while its sleeping.
To avoid this, I propose we actually spawn a tokio task that will sleep for the desired amount of time, then send a Command
using the sender side of the CompositeDevice channel to tell the device it should process the event after the sleep has finished. Since the sleep is happening in its own task, it will not block the CompositeDevice from processing other events.
e.g.
pub enum Command {
...
WriteEvent(NativeEvent),
...
...
tokio::spawn(async move {
tokio::time::sleep(Duration::from_millis(80)).await;
if let Err(e) = tx.send(Command::WriteEvent(event)) {
log::error!("Failed to send chord event command: {:?}", e);
}
});
...
Then in the run loop, we can process the command sent through the channel after the delay:
match cmd {
...
Command::WriteEvent(event) => {
if let Err(e) = self.write_event(event).await {
log::error!("Failed to write event: {:?}", e);
}
}
...
src/input/composite_device/mod.rs
Outdated
.iter() | ||
.position(|n| n.clone().as_capability() == event.as_capability()); | ||
if idx != Some(events.len() - 1) { | ||
sleep(Duration::from_millis(80)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of using thread::sleep
, we should be using tokio::time::sleep
instead whenever we need to sleep in an async function. Using thread::sleep
is something we should only use for synchronous code to prevent holding up the async runtime.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tried this, the tokio sleeps don't affect the thread we need to sleep so everything breaks.
We can't do this if we want to support devices that use on-release instead of momentary action for their buttons. Take for example the ROG Ally, arguably the second most popular handheld after the Steam Deck. The CC and AC buttons do not emit events when pressed, rather they release events only on release. Approximately 1ms after the "down" events fire the "up" events fire. If we use a tokio to delay before the event is transmitted then the "up" event will arrive 79ms before the "down" event. We could also add an 80 ms delay before any "up" chords but that still only provides 1ms for steam to recognize the chord and use the events, leading to multiple event pass-throughs. |
… steam can understand them.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ship it!
🎉 This PR is included in version 0.10.2 🎉 The release is available on GitHub release Your semantic-release bot 📦🚀 |