feat(host/windows): SendInput input-injection backend
apple / swift (push) Successful in 53s
android / android (push) Successful in 2m4s
ci / rust (push) Failing after 47s
ci / web (push) Successful in 26s
ci / docs-site (push) Successful in 27s
ci / bench (push) Successful in 1m36s
decky / build-publish (push) Successful in 12s
deb / build-publish (push) Successful in 2m12s
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
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 3s
flatpak / build-publish (push) Failing after 2s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 2m56s
rpm / build-publish (bazzite, punktfunk-fedora-rpm) (push) Successful in 4m58s
rpm / build-publish (fedora-44, punktfunk-fedora44-rpm) (push) Failing after 4m16s
docker / deploy-docs (push) Successful in 18s
apple / swift (push) Successful in 53s
android / android (push) Successful in 2m4s
ci / rust (push) Failing after 47s
ci / web (push) Successful in 26s
ci / docs-site (push) Successful in 27s
ci / bench (push) Successful in 1m36s
decky / build-publish (push) Successful in 12s
deb / build-publish (push) Successful in 2m12s
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
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 3s
flatpak / build-publish (push) Failing after 2s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 2m56s
rpm / build-publish (bazzite, punktfunk-fedora-rpm) (push) Successful in 4m58s
rpm / build-publish (fedora-44, punktfunk-fedora44-rpm) (push) Failing after 4m16s
docker / deploy-docs (push) Successful in 18s
Windows InputInjector via SendInput (Win32 KeyboardAndMouse), mirroring the wlroots backend: absolute mouse (MOUSEEVENTF_VIRTUALDESK normalized to the virtual desktop), relative mouse, scancode keyboard (MapVirtualKeyExW + extended-key flagging), scroll (no sign flip — Windows wheel matches GameStream), buttons. Client already sends Windows VK codes (no keycode table). Reattaches the thread to the input desktop (OpenInputDesktop/SetThreadDesktop) to survive UAC/lock switches. New Backend::SendInput, the Windows auto-default in default_backend(), open() arm, windows-crate features. Compiles clean on Windows + Linux. Live injection validates with the in-session host run (SendInput is desktop-isolated from an SSH network logon). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -31,6 +31,8 @@ pub enum Backend {
|
||||
GamescopeEi,
|
||||
/// `/dev/uinput` — universal fallback (but invisible to `WLR_LIBINPUT_NO_DEVICES=1`).
|
||||
Uinput,
|
||||
/// Windows `SendInput` (Win32 KeyboardAndMouse) — the Windows host path.
|
||||
SendInput,
|
||||
}
|
||||
|
||||
pub fn open(backend: Backend) -> Result<Box<dyn InputInjector>> {
|
||||
@@ -71,6 +73,16 @@ pub fn open(backend: Backend) -> Result<Box<dyn InputInjector>> {
|
||||
anyhow::bail!("gamescope EIS input requires Linux")
|
||||
}
|
||||
}
|
||||
Backend::SendInput => {
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
Ok(Box::new(sendinput::SendInputInjector::open()?))
|
||||
}
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
{
|
||||
anyhow::bail!("SendInput injection requires Windows")
|
||||
}
|
||||
}
|
||||
other => anyhow::bail!("injection backend {other:?} not implemented"),
|
||||
}
|
||||
}
|
||||
@@ -87,23 +99,31 @@ pub fn default_backend() -> Backend {
|
||||
"libei" | "ei" | "portal" => return Backend::Libei,
|
||||
"gamescope" | "gamescope-ei" => return Backend::GamescopeEi,
|
||||
"uinput" => return Backend::Uinput,
|
||||
"sendinput" | "win" | "windows" => return Backend::SendInput,
|
||||
other => tracing::warn!(
|
||||
value = other,
|
||||
"unknown PUNKTFUNK_INPUT_BACKEND — auto-detecting"
|
||||
),
|
||||
}
|
||||
}
|
||||
if std::env::var("PUNKTFUNK_COMPOSITOR")
|
||||
.is_ok_and(|v| v.trim().eq_ignore_ascii_case("gamescope"))
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
return Backend::GamescopeEi;
|
||||
Backend::SendInput
|
||||
}
|
||||
let desktop = std::env::var("XDG_CURRENT_DESKTOP").unwrap_or_default();
|
||||
let d = desktop.to_ascii_uppercase();
|
||||
if d.contains("KDE") || d.contains("GNOME") {
|
||||
Backend::Libei
|
||||
} else {
|
||||
Backend::WlrVirtual
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
{
|
||||
if std::env::var("PUNKTFUNK_COMPOSITOR")
|
||||
.is_ok_and(|v| v.trim().eq_ignore_ascii_case("gamescope"))
|
||||
{
|
||||
return Backend::GamescopeEi;
|
||||
}
|
||||
let desktop = std::env::var("XDG_CURRENT_DESKTOP").unwrap_or_default();
|
||||
let d = desktop.to_ascii_uppercase();
|
||||
if d.contains("KDE") || d.contains("GNOME") {
|
||||
Backend::Libei
|
||||
} else {
|
||||
Backend::WlrVirtual
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -295,3 +315,5 @@ pub mod gamepad {
|
||||
mod libei;
|
||||
#[cfg(target_os = "linux")]
|
||||
mod wlr;
|
||||
#[cfg(target_os = "windows")]
|
||||
mod sendinput;
|
||||
|
||||
Reference in New Issue
Block a user