feat(host/windows): ViGEm virtual gamepad backend
apple / swift (push) Successful in 53s
android / android (push) Failing after 2m28s
ci / web (push) Successful in 27s
ci / docs-site (push) Failing after 13s
ci / bench (push) Failing after 0s
deb / build-publish (push) Failing after 1s
ci / rust (push) Failing after 44s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 5s
decky / build-publish (push) Successful in 11s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 4s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 4s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 4s
flatpak / build-publish (push) Failing after 2s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 3s
rpm / build-publish (bazzite, punktfunk-fedora-rpm) (push) Successful in 5m40s
rpm / build-publish (fedora-44, punktfunk-fedora44-rpm) (push) Successful in 5m40s
docker / deploy-docs (push) Successful in 6s
apple / swift (push) Successful in 53s
android / android (push) Failing after 2m28s
ci / web (push) Successful in 27s
ci / docs-site (push) Failing after 13s
ci / bench (push) Failing after 0s
deb / build-publish (push) Failing after 1s
ci / rust (push) Failing after 44s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 5s
decky / build-publish (push) Successful in 11s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 4s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 4s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 4s
flatpak / build-publish (push) Failing after 2s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 3s
rpm / build-publish (bazzite, punktfunk-fedora-rpm) (push) Successful in 5m40s
rpm / build-publish (fedora-44, punktfunk-fedora44-rpm) (push) Successful in 5m40s
docker / deploy-docs (push) Successful in 6s
Windows GamepadManager via vigem-client (ViGEmBus) — the uinput-xpad analogue: one virtual Xbox 360 controller per client pad index, created lazily on first State. GameStream/Moonlight already uses the XInput conventions (low-16 button bits, sticks -32768..32767 +Y up, triggers 0..255), so the GamepadFrame->XGamepad mapping is 1:1. Replaces the non-Linux GamepadManager stub (same new/handle/pump_rumble API the m3 PadBackend drives, so no m3 change). Graceful when ViGEmBus is absent (gamepad disabled, session continues). Compiles clean on Windows + Linux; live-test needs the ViGEmBus driver + a physical pad. Rumble back-channel is a TODO (ViGEm notification API). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Generated
+32
@@ -2658,6 +2658,7 @@ dependencies = [
|
|||||||
"utoipa",
|
"utoipa",
|
||||||
"utoipa-axum",
|
"utoipa-axum",
|
||||||
"utoipa-scalar",
|
"utoipa-scalar",
|
||||||
|
"vigem-client",
|
||||||
"wasapi",
|
"wasapi",
|
||||||
"wayland-backend",
|
"wayland-backend",
|
||||||
"wayland-client",
|
"wayland-client",
|
||||||
@@ -3967,6 +3968,15 @@ version = "0.9.5"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "vigem-client"
|
||||||
|
version = "0.1.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b857e6f99efe1e1eb1e4dfb035de8ae7ec8ec56bd1928edcbd7c6e4427634d52"
|
||||||
|
dependencies = [
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wait-timeout"
|
name = "wait-timeout"
|
||||||
version = "0.2.1"
|
version = "0.2.1"
|
||||||
@@ -4214,6 +4224,22 @@ dependencies = [
|
|||||||
"safe_arch",
|
"safe_arch",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi"
|
||||||
|
version = "0.3.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||||
|
dependencies = [
|
||||||
|
"winapi-i686-pc-windows-gnu",
|
||||||
|
"winapi-x86_64-pc-windows-gnu",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi-i686-pc-windows-gnu"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winapi-util"
|
name = "winapi-util"
|
||||||
version = "0.1.11"
|
version = "0.1.11"
|
||||||
@@ -4223,6 +4249,12 @@ dependencies = [
|
|||||||
"windows-sys 0.61.2",
|
"windows-sys 0.61.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi-x86_64-pc-windows-gnu"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows"
|
name = "windows"
|
||||||
version = "0.62.2"
|
version = "0.62.2"
|
||||||
|
|||||||
@@ -127,6 +127,8 @@ windows = { version = "0.62", features = [
|
|||||||
openh264 = "0.9"
|
openh264 = "0.9"
|
||||||
# WASAPI loopback audio capture (default render endpoint -> 48 kHz stereo f32 for the Opus path).
|
# WASAPI loopback audio capture (default render endpoint -> 48 kHz stereo f32 for the Opus path).
|
||||||
wasapi = "0.23"
|
wasapi = "0.23"
|
||||||
|
# Virtual Xbox 360 gamepad via ViGEmBus (the uinput-xpad analogue) — driver installed separately.
|
||||||
|
vigem-client = "0.1"
|
||||||
# NVENC hardware encoder (NVENC SDK, D3D11 input). The SDK pins `cudarc` with
|
# NVENC hardware encoder (NVENC SDK, D3D11 input). The SDK pins `cudarc` with
|
||||||
# `cuda-version-from-build-system` (a build-time CUDA-toolkit probe); its `ci-check` feature switches
|
# `cuda-version-from-build-system` (a build-time CUDA-toolkit probe); its `ci-check` feature switches
|
||||||
# cudarc to `dynamic-loading` (loads nvcuda.dll at runtime — nothing needed at build), which is how
|
# cudarc to `dynamic-loading` (loads nvcuda.dll at runtime — nothing needed at build), which is how
|
||||||
|
|||||||
@@ -298,8 +298,12 @@ fn gs_button_to_evdev(b: u32) -> Option<u32> {
|
|||||||
pub mod dualsense;
|
pub mod dualsense;
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
pub mod gamepad;
|
pub mod gamepad;
|
||||||
/// Stub — virtual gamepads need Linux uinput; events are dropped elsewhere.
|
/// Windows: virtual Xbox 360 pads via ViGEmBus.
|
||||||
#[cfg(not(target_os = "linux"))]
|
#[cfg(target_os = "windows")]
|
||||||
|
#[path = "inject/gamepad_windows.rs"]
|
||||||
|
pub mod gamepad;
|
||||||
|
/// Stub — virtual gamepads need Linux uinput or Windows ViGEmBus; events are dropped elsewhere.
|
||||||
|
#[cfg(not(any(target_os = "linux", target_os = "windows")))]
|
||||||
pub mod gamepad {
|
pub mod gamepad {
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct GamepadManager;
|
pub struct GamepadManager;
|
||||||
|
|||||||
@@ -0,0 +1,70 @@
|
|||||||
|
//! Windows virtual gamepad via ViGEmBus — the analogue of the Linux uinput Xbox-360 pad.
|
||||||
|
//! One virtual Xbox 360 controller per client pad index. GameStream/Moonlight already uses the
|
||||||
|
//! XInput button/stick/trigger conventions (low 16 button bits, sticks −32768..32767 +Y up,
|
||||||
|
//! triggers 0..255), so the mapping is ~1:1.
|
||||||
|
//!
|
||||||
|
//! Needs the ViGEmBus driver installed (like SudoVDA for the display); absent → gamepad is disabled
|
||||||
|
//! and the session continues without it. Rumble back-channel: TODO (ViGEm notification API).
|
||||||
|
|
||||||
|
use crate::gamestream::gamepad::GamepadEvent;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use vigem_client::{Client, TargetId, XButtons, XGamepad, Xbox360Wired};
|
||||||
|
|
||||||
|
pub struct GamepadManager {
|
||||||
|
client: Option<Arc<Client>>,
|
||||||
|
pads: HashMap<u8, Xbox360Wired<Arc<Client>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GamepadManager {
|
||||||
|
pub fn new() -> GamepadManager {
|
||||||
|
let client = match Client::connect() {
|
||||||
|
Ok(c) => {
|
||||||
|
tracing::info!("ViGEmBus connected (virtual Xbox 360 gamepads)");
|
||||||
|
Some(Arc::new(c))
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
tracing::warn!(
|
||||||
|
error = format!("{e:?}"),
|
||||||
|
"ViGEmBus unavailable — gamepad disabled (install ViGEmBus)"
|
||||||
|
);
|
||||||
|
None
|
||||||
|
}
|
||||||
|
};
|
||||||
|
GamepadManager {
|
||||||
|
client,
|
||||||
|
pads: HashMap::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn handle(&mut self, ev: &GamepadEvent) {
|
||||||
|
let Some(client) = self.client.clone() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let GamepadEvent::State(f) = ev else {
|
||||||
|
return; // Arrival metadata — the pad is created lazily on the first State
|
||||||
|
};
|
||||||
|
let target = self.pads.entry(f.index.max(0) as u8).or_insert_with(|| {
|
||||||
|
let mut t = Xbox360Wired::new(client, TargetId::XBOX360_WIRED);
|
||||||
|
let _ = t.plugin();
|
||||||
|
let _ = t.wait_ready();
|
||||||
|
t
|
||||||
|
});
|
||||||
|
let gp = XGamepad {
|
||||||
|
buttons: XButtons {
|
||||||
|
raw: (f.buttons & 0xffff) as u16,
|
||||||
|
},
|
||||||
|
left_trigger: f.left_trigger,
|
||||||
|
right_trigger: f.right_trigger,
|
||||||
|
thumb_lx: f.ls_x,
|
||||||
|
thumb_ly: f.ls_y,
|
||||||
|
thumb_rx: f.rs_x,
|
||||||
|
thumb_ry: f.rs_y,
|
||||||
|
};
|
||||||
|
let _ = target.update(&gp);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pump_rumble(&mut self, _send: impl FnMut(u16, u16, u16)) {
|
||||||
|
// TODO: wire the ViGEm rumble notification back-channel (Xbox360Wired::request_notification).
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user