Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
384a0adc83 | ||
|
|
553676282a | ||
|
|
02e9cc4691 |
Generated
+30
-30
@@ -947,7 +947,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cursor-probe"
|
name = "cursor-probe"
|
||||||
version = "0.22.1"
|
version = "0.22.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"pf-capture",
|
"pf-capture",
|
||||||
@@ -1036,7 +1036,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "display-disturb"
|
name = "display-disturb"
|
||||||
version = "0.22.1"
|
version = "0.22.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows 0.62.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"windows 0.62.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
@@ -2221,7 +2221,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "latency-probe"
|
name = "latency-probe"
|
||||||
version = "0.22.1"
|
version = "0.22.2"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lazy_static"
|
name = "lazy_static"
|
||||||
@@ -2326,7 +2326,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libvpl-sys"
|
name = "libvpl-sys"
|
||||||
version = "0.22.1"
|
version = "0.22.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bindgen",
|
"bindgen",
|
||||||
"cmake",
|
"cmake",
|
||||||
@@ -2361,7 +2361,7 @@ checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "loss-harness"
|
name = "loss-harness"
|
||||||
version = "0.22.1"
|
version = "0.22.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"punktfunk-core",
|
"punktfunk-core",
|
||||||
]
|
]
|
||||||
@@ -2850,7 +2850,7 @@ checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pf-capture"
|
name = "pf-capture"
|
||||||
version = "0.22.1"
|
version = "0.22.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"ashpd",
|
"ashpd",
|
||||||
@@ -2871,7 +2871,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pf-client-core"
|
name = "pf-client-core"
|
||||||
version = "0.22.1"
|
version = "0.22.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"ash",
|
"ash",
|
||||||
@@ -2896,7 +2896,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pf-clipboard"
|
name = "pf-clipboard"
|
||||||
version = "0.22.1"
|
version = "0.22.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"ashpd",
|
"ashpd",
|
||||||
@@ -2914,7 +2914,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pf-console-ui"
|
name = "pf-console-ui"
|
||||||
version = "0.22.1"
|
version = "0.22.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"ash",
|
"ash",
|
||||||
@@ -2935,7 +2935,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pf-encode"
|
name = "pf-encode"
|
||||||
version = "0.22.1"
|
version = "0.22.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"ash",
|
"ash",
|
||||||
@@ -2959,7 +2959,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pf-ffvk"
|
name = "pf-ffvk"
|
||||||
version = "0.22.1"
|
version = "0.22.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ash",
|
"ash",
|
||||||
"bindgen",
|
"bindgen",
|
||||||
@@ -2968,7 +2968,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pf-frame"
|
name = "pf-frame"
|
||||||
version = "0.22.1"
|
version = "0.22.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"libc",
|
"libc",
|
||||||
@@ -2980,7 +2980,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pf-gpu"
|
name = "pf-gpu"
|
||||||
version = "0.22.1"
|
version = "0.22.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"pf-host-config",
|
"pf-host-config",
|
||||||
@@ -2994,11 +2994,11 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pf-host-config"
|
name = "pf-host-config"
|
||||||
version = "0.22.1"
|
version = "0.22.2"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pf-inject"
|
name = "pf-inject"
|
||||||
version = "0.22.1"
|
version = "0.22.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"ashpd",
|
"ashpd",
|
||||||
@@ -3027,14 +3027,14 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pf-paths"
|
name = "pf-paths"
|
||||||
version = "0.22.1"
|
version = "0.22.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"tracing",
|
"tracing",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pf-presenter"
|
name = "pf-presenter"
|
||||||
version = "0.22.1"
|
version = "0.22.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"ash",
|
"ash",
|
||||||
@@ -3049,7 +3049,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pf-vdisplay"
|
name = "pf-vdisplay"
|
||||||
version = "0.22.1"
|
version = "0.22.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"ashpd",
|
"ashpd",
|
||||||
@@ -3082,7 +3082,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pf-win-display"
|
name = "pf-win-display"
|
||||||
version = "0.22.1"
|
version = "0.22.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"pf-paths",
|
"pf-paths",
|
||||||
@@ -3094,7 +3094,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pf-zerocopy"
|
name = "pf-zerocopy"
|
||||||
version = "0.22.1"
|
version = "0.22.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"ash",
|
"ash",
|
||||||
@@ -3302,7 +3302,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "punktfunk-cli"
|
name = "punktfunk-cli"
|
||||||
version = "0.22.1"
|
version = "0.22.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"pf-client-core",
|
"pf-client-core",
|
||||||
"punktfunk-core",
|
"punktfunk-core",
|
||||||
@@ -3313,7 +3313,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "punktfunk-client-android"
|
name = "punktfunk-client-android"
|
||||||
version = "0.22.1"
|
version = "0.22.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"android_logger",
|
"android_logger",
|
||||||
"jni",
|
"jni",
|
||||||
@@ -3329,7 +3329,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "punktfunk-client-linux"
|
name = "punktfunk-client-linux"
|
||||||
version = "0.22.1"
|
version = "0.22.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-channel",
|
"async-channel",
|
||||||
@@ -3346,7 +3346,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "punktfunk-client-session"
|
name = "punktfunk-client-session"
|
||||||
version = "0.22.1"
|
version = "0.22.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"pf-client-core",
|
"pf-client-core",
|
||||||
@@ -3361,7 +3361,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "punktfunk-client-windows"
|
name = "punktfunk-client-windows"
|
||||||
version = "0.22.1"
|
version = "0.22.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-channel",
|
"async-channel",
|
||||||
"ffmpeg-next",
|
"ffmpeg-next",
|
||||||
@@ -3381,7 +3381,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "punktfunk-core"
|
name = "punktfunk-core"
|
||||||
version = "0.22.1"
|
version = "0.22.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aes-gcm",
|
"aes-gcm",
|
||||||
"bytes",
|
"bytes",
|
||||||
@@ -3413,7 +3413,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "punktfunk-host"
|
name = "punktfunk-host"
|
||||||
version = "0.22.1"
|
version = "0.22.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aes",
|
"aes",
|
||||||
"aes-gcm",
|
"aes-gcm",
|
||||||
@@ -3497,7 +3497,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "punktfunk-probe"
|
name = "punktfunk-probe"
|
||||||
version = "0.22.1"
|
version = "0.22.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"mdns-sd",
|
"mdns-sd",
|
||||||
@@ -3511,7 +3511,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "punktfunk-tray"
|
name = "punktfunk-tray"
|
||||||
version = "0.22.1"
|
version = "0.22.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"ksni",
|
"ksni",
|
||||||
@@ -3534,7 +3534,7 @@ checksum = "d55d956fa96f5ec02be2e13af0e20391a5aa83d6a074e3ad368959d0fab299ea"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pyrowave-sys"
|
name = "pyrowave-sys"
|
||||||
version = "0.22.1"
|
version = "0.22.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bindgen",
|
"bindgen",
|
||||||
"cmake",
|
"cmake",
|
||||||
|
|||||||
+1
-1
@@ -51,7 +51,7 @@ exclude = [
|
|||||||
ndk = { path = "clients/android/native/vendor/ndk" }
|
ndk = { path = "clients/android/native/vendor/ndk" }
|
||||||
|
|
||||||
[workspace.package]
|
[workspace.package]
|
||||||
version = "0.22.1"
|
version = "0.22.2"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
rust-version = "1.82"
|
rust-version = "1.82"
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
|
|||||||
@@ -385,9 +385,15 @@ impl WinDsIdentity {
|
|||||||
WinDsIdentity {
|
WinDsIdentity {
|
||||||
devtype: 0,
|
devtype: 0,
|
||||||
instance_prefix: "pf_pad",
|
instance_prefix: "pf_pad",
|
||||||
hwid: "pf_gamepad",
|
// ⚠️ A HARDWARE ID, not the package name. `pf-dualsense` became `pf-gamepad` in
|
||||||
|
// 560e663a and this line was renamed with it — but the INF deliberately kept the four
|
||||||
|
// OLD hardware ids, so `pf_gamepad` matched no INF of ours. PnP then fell through to
|
||||||
|
// the devnode's synthesized USB ids, bound Microsoft's inbox `input.inf`/`HidUsb`, and
|
||||||
|
// that cannot start on a software-enumerated devnode: CM_PROB_FAILED_START, no HID
|
||||||
|
// child, no channel proof, and a pad no game ever saw. `hwid_matches_inf` pins it now.
|
||||||
|
hwid: "pf_dualsense",
|
||||||
usb_vid_pid: "VID_054C&PID_0CE6",
|
usb_vid_pid: "VID_054C&PID_0CE6",
|
||||||
description: "punktfunk Virtual DualSense",
|
description: "Punktfunk Virtual DualSense",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -397,7 +403,7 @@ impl WinDsIdentity {
|
|||||||
instance_prefix: "pf_edge",
|
instance_prefix: "pf_edge",
|
||||||
hwid: "pf_dualsenseedge",
|
hwid: "pf_dualsenseedge",
|
||||||
usb_vid_pid: "VID_054C&PID_0DF2",
|
usb_vid_pid: "VID_054C&PID_0DF2",
|
||||||
description: "punktfunk Virtual DualSense Edge",
|
description: "Punktfunk Virtual DualSense Edge",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -633,12 +639,12 @@ pub fn deck_spike_hold(index: u8, secs: u64) -> Result<()> {
|
|||||||
instance: &inst,
|
instance: &inst,
|
||||||
container_tag: 0x5046_4453, // "PFDS"
|
container_tag: 0x5046_4453, // "PFDS"
|
||||||
container_index: index,
|
container_index: index,
|
||||||
hwid: "pf_steamdeck",
|
hwid: super::steam_deck_windows::DECK_HWID,
|
||||||
usb_vid_pid: "VID_28DE&PID_1205",
|
usb_vid_pid: "VID_28DE&PID_1205",
|
||||||
// The Deck's controller interface — the promotion gate the first spike run hit
|
// The Deck's controller interface — the promotion gate the first spike run hit
|
||||||
// (hidapi parses MI_ from the child hwids; absent = interface 0, Steam wants 2).
|
// (hidapi parses MI_ from the child hwids; absent = interface 0, Steam wants 2).
|
||||||
usb_mi: Some(2),
|
usb_mi: Some(2),
|
||||||
description: "punktfunk Virtual Steam Deck (spike)",
|
description: "Punktfunk Virtual Steam Deck (spike)",
|
||||||
})?;
|
})?;
|
||||||
// The spike drives a real pad channel, so it takes the same devnode-proved delivery a session
|
// The spike drives a real pad channel, so it takes the same devnode-proved delivery a session
|
||||||
// pad does — no special case, and no reason for a bring-up tool to run on the old trust.
|
// pad does — no special case, and no reason for a bring-up tool to run on the old trust.
|
||||||
@@ -802,6 +808,58 @@ mod drain_tests {
|
|||||||
assert_eq!(got, vec![vec![0x02, 99]]);
|
assert_eq!(got, vec![vec![0x02, 99]]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Every hardware id the host puts on a pad devnode must be one the shipped INF actually
|
||||||
|
/// declares — otherwise PnP matches none of our models, falls through to the synthesized USB
|
||||||
|
/// ids on the same devnode, and binds Microsoft's inbox `input.inf`/`HidUsb`, which cannot
|
||||||
|
/// start on a software-enumerated devnode. The result is a pad that exists, never starts, and
|
||||||
|
/// never answers a channel proof; that is exactly what shipped in 0.22.0/0.22.1 for the plain
|
||||||
|
/// DualSense, because the `pf-dualsense` -> `pf-gamepad` PACKAGE rename also rewrote this
|
||||||
|
/// HARDWARE id (560e663a). The ids are a binding contract with every installed system, so they
|
||||||
|
/// must outlive any future package rename — this test is what makes that structural.
|
||||||
|
#[test]
|
||||||
|
fn hwid_matches_inf() {
|
||||||
|
let inx = concat!(
|
||||||
|
env!("CARGO_MANIFEST_DIR"),
|
||||||
|
"/../../packaging/windows/drivers/pf-gamepad/pf_gamepad.inx"
|
||||||
|
);
|
||||||
|
let inf = std::fs::read_to_string(inx).expect("read pf_gamepad.inx");
|
||||||
|
// The [Models] lines: `%DeviceDesc…%=pfGamepad, <hwid>[, <hwid>…]`.
|
||||||
|
let declared: Vec<String> = inf
|
||||||
|
.lines()
|
||||||
|
.map(str::trim)
|
||||||
|
.filter(|l| !l.starts_with(';'))
|
||||||
|
.filter_map(|l| l.split_once("=pfGamepad,"))
|
||||||
|
.flat_map(|(_, ids)| {
|
||||||
|
ids.split(',')
|
||||||
|
.map(|id| id.trim().to_ascii_lowercase())
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
assert!(
|
||||||
|
declared.len() >= 4,
|
||||||
|
"parsed {} hardware ids out of {inx} — the [Models] shape changed and this test went \
|
||||||
|
vacuous; fix the parse rather than deleting the assert",
|
||||||
|
declared.len()
|
||||||
|
);
|
||||||
|
for hwid in [
|
||||||
|
WinDsIdentity::dualsense().hwid,
|
||||||
|
WinDsIdentity::dualsense_edge().hwid,
|
||||||
|
super::super::dualshock4_windows::DS4_HWID,
|
||||||
|
super::super::steam_deck_windows::DECK_HWID,
|
||||||
|
] {
|
||||||
|
let want = hwid.to_ascii_lowercase();
|
||||||
|
let rooted = format!("root\\{want}");
|
||||||
|
assert!(
|
||||||
|
declared
|
||||||
|
.iter()
|
||||||
|
.any(|d| d.as_str() == want || d.as_str() == rooted),
|
||||||
|
"the host creates pad devnodes with hardware id {hwid:?}, which pf_gamepad.inx \
|
||||||
|
does not declare (it has {declared:?}) — PnP would bind inbox input.inf/HidUsb \
|
||||||
|
instead and the pad would never start"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn legacy_driver_still_drains_the_latest_slot() {
|
fn legacy_driver_still_drains_the_latest_slot() {
|
||||||
let mut buf = section();
|
let mut buf = section();
|
||||||
|
|||||||
@@ -21,6 +21,10 @@ use anyhow::Result;
|
|||||||
use punktfunk_core::quic::{HidOutput, RichInput};
|
use punktfunk_core::quic::{HidOutput, RichInput};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
|
/// The hardware id this pad's devnode carries. Must be one `pf_gamepad.inx` declares — a package
|
||||||
|
/// rename must never touch it (`dualsense_windows::tests::hwid_matches_inf` enforces that).
|
||||||
|
pub(super) const DS4_HWID: &str = "pf_dualshock4";
|
||||||
|
|
||||||
/// A single virtual DualShock 4: the `SwDeviceCreate`'d `pf_ds4_<index>` devnode plus the sealed
|
/// A single virtual DualShock 4: the `SwDeviceCreate`'d `pf_ds4_<index>` devnode plus the sealed
|
||||||
/// shared-memory channel. Dropping it removes the devnode and closes both sections.
|
/// shared-memory channel. Dropping it removes the devnode and closes both sections.
|
||||||
/// `pub`: the type appears as `type Pad` in the `PadProto` impl (a public trait), like the
|
/// `pub`: the type appears as `type Pad` in the `PadProto` impl (a public trait), like the
|
||||||
@@ -66,10 +70,10 @@ impl Ds4WinPad {
|
|||||||
instance: &inst,
|
instance: &inst,
|
||||||
container_tag: 0x5046_4453, // "PFDS"
|
container_tag: 0x5046_4453, // "PFDS"
|
||||||
container_index: index,
|
container_index: index,
|
||||||
hwid: "pf_dualshock4",
|
hwid: DS4_HWID,
|
||||||
usb_vid_pid: "VID_054C&PID_09CC",
|
usb_vid_pid: "VID_054C&PID_09CC",
|
||||||
usb_mi: None,
|
usb_mi: None,
|
||||||
description: "punktfunk Virtual DualShock 4",
|
description: "Punktfunk Virtual DualShock 4",
|
||||||
})?; // Propagate, do NOT swallow — see below.
|
})?; // Propagate, do NOT swallow — see below.
|
||||||
let (hsw, instance_id) = (Some(hsw), instance_id);
|
let (hsw, instance_id) = (Some(hsw), instance_id);
|
||||||
// Swallowing a create failure here (the previous behaviour) latched the pad slot to
|
// Swallowing a create failure here (the previous behaviour) latched the pad slot to
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ fn create_swdevice(index: u8) -> Result<(HSWDEVICE, Option<String>)> {
|
|||||||
.encode_utf16()
|
.encode_utf16()
|
||||||
.chain(std::iter::once(0))
|
.chain(std::iter::once(0))
|
||||||
.collect();
|
.collect();
|
||||||
let desc: Vec<u16> = "punktfunk Virtual Xbox 360 (XUSB)"
|
let desc: Vec<u16> = "Punktfunk Virtual Xbox 360 (XUSB)"
|
||||||
.encode_utf16()
|
.encode_utf16()
|
||||||
.chain(std::iter::once(0))
|
.chain(std::iter::once(0))
|
||||||
.collect();
|
.collect();
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ impl VirtualMouse {
|
|||||||
// a mouse (nothing fingerprints them); reusing the shared profile keeps one code path.
|
// a mouse (nothing fingerprints them); reusing the shared profile keeps one code path.
|
||||||
usb_vid_pid: "VID_5046&PID_4D4F",
|
usb_vid_pid: "VID_5046&PID_4D4F",
|
||||||
usb_mi: None,
|
usb_mi: None,
|
||||||
description: "punktfunk Virtual Mouse",
|
description: "Punktfunk Virtual Mouse",
|
||||||
}) {
|
}) {
|
||||||
Ok((h, i)) => (Some(h), i),
|
Ok((h, i)) => (Some(h), i),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
@@ -386,7 +386,7 @@ pub fn channel_proof_probe() -> Result<()> {
|
|||||||
hwid: "pf_mouse",
|
hwid: "pf_mouse",
|
||||||
usb_vid_pid: "VID_5046&PID_4D4F",
|
usb_vid_pid: "VID_5046&PID_4D4F",
|
||||||
usb_mi: None,
|
usb_mi: None,
|
||||||
description: "punktfunk Virtual Mouse (channel-proof probe)",
|
description: "Punktfunk Virtual Mouse (channel-proof probe)",
|
||||||
})?;
|
})?;
|
||||||
let _sw = super::gamepad_raii::SwDevice::new(hsw);
|
let _sw = super::gamepad_raii::SwDevice::new(hsw);
|
||||||
let Some(instance_id) = instance_id else {
|
let Some(instance_id) = instance_id else {
|
||||||
|
|||||||
@@ -30,6 +30,10 @@ use anyhow::Result;
|
|||||||
use punktfunk_core::quic::RichInput;
|
use punktfunk_core::quic::RichInput;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
|
/// The hardware id this pad's devnode carries. Must be one `pf_gamepad.inx` declares — a package
|
||||||
|
/// rename must never touch it (`dualsense_windows::tests::hwid_matches_inf` enforces that).
|
||||||
|
pub(super) const DECK_HWID: &str = "pf_steamdeck";
|
||||||
|
|
||||||
/// A single virtual Steam Deck: the `SwDeviceCreate`'d `pf_deck_<index>` devnode plus the sealed
|
/// A single virtual Steam Deck: the `SwDeviceCreate`'d `pf_deck_<index>` devnode plus the sealed
|
||||||
/// shared-memory channel. Dropping it removes the devnode and closes both sections.
|
/// shared-memory channel. Dropping it removes the devnode and closes both sections.
|
||||||
/// `pub`: the type appears as `type Pad` in the `PadProto` impl (a public trait).
|
/// `pub`: the type appears as `type Pad` in the `PadProto` impl (a public trait).
|
||||||
@@ -70,13 +74,13 @@ impl DeckWinPad {
|
|||||||
instance: &inst,
|
instance: &inst,
|
||||||
container_tag: 0x5046_4453, // "PFDS"
|
container_tag: 0x5046_4453, // "PFDS"
|
||||||
container_index: index,
|
container_index: index,
|
||||||
hwid: "pf_steamdeck",
|
hwid: DECK_HWID,
|
||||||
usb_vid_pid: "VID_28DE&PID_1205",
|
usb_vid_pid: "VID_28DE&PID_1205",
|
||||||
// The wired Deck controller interface — WITHOUT this the HID child carries no MI_
|
// The wired Deck controller interface — WITHOUT this the HID child carries no MI_
|
||||||
// token, hidapi reports interface 0, and Steam never claims the pad (the N4
|
// token, hidapi reports interface 0, and Steam never claims the pad (the N4
|
||||||
// spike's run-1 failure).
|
// spike's run-1 failure).
|
||||||
usb_mi: Some(2),
|
usb_mi: Some(2),
|
||||||
description: "punktfunk Virtual Steam Deck",
|
description: "Punktfunk Virtual Steam Deck",
|
||||||
})?; // Propagate — swallowing latched the slot to a pad with no devnode (see the DS4 twin).
|
})?; // Propagate — swallowing latched the slot to a pad with no devnode (see the DS4 twin).
|
||||||
let (hsw, instance_id) = (Some(hsw), instance_id);
|
let (hsw, instance_id) = (Some(hsw), instance_id);
|
||||||
// The DATA section goes to whoever THIS devnode says is serving it — not to whatever pid
|
// The DATA section goes to whoever THIS devnode says is serving it — not to whatever pid
|
||||||
|
|||||||
@@ -92,7 +92,7 @@ unsafe fn ioctl(h: HANDLE, code: u32, input: &[u8], output: &mut [u8]) -> Result
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Reap the ghost (NOT-present) "punktfunk" virtual-monitor device nodes that `IddCxMonitorDeparture`
|
/// Reap the ghost (NOT-present) "punktfunk" virtual-monitor device nodes that `IddCxMonitorDeparture`
|
||||||
/// leaves behind. Each departed monitor leaves a not-present "Generic Monitor (punktfunk)" PDO that keeps
|
/// leaves behind. Each departed monitor leaves a not-present "Generic Monitor (Punktfunk)" PDO that keeps
|
||||||
/// pinning an OS VidPN target against the IddCx adapter's fixed monitor-slot budget; once ~16 accumulate,
|
/// pinning an OS VidPN target against the IddCx adapter's fixed monitor-slot budget; once ~16 accumulate,
|
||||||
/// `IOCTL_ADD` wedges at 0x80070490 (`ERROR_NOT_FOUND`) and every session black-screens until a manual
|
/// `IOCTL_ADD` wedges at 0x80070490 (`ERROR_NOT_FOUND`) and every session black-screens until a manual
|
||||||
/// reset/reboot. Removing the not-present PDOs frees the slots — the in-process equivalent of
|
/// reset/reboot. Removing the not-present PDOs frees the slots — the in-process equivalent of
|
||||||
@@ -532,7 +532,7 @@ impl VdisplayDriver for PfVdisplayDriver {
|
|||||||
tracing::warn!("pf-vdisplay IOCTL_CLEAR_ALL failed on startup (continuing)");
|
tracing::warn!("pf-vdisplay IOCTL_CLEAR_ALL failed on startup (continuing)");
|
||||||
}
|
}
|
||||||
// CLEAR_ALL only departs the driver's own (in-process) monitor list; it can NOT remove the
|
// CLEAR_ALL only departs the driver's own (in-process) monitor list; it can NOT remove the
|
||||||
// OS-side not-present "Generic Monitor (punktfunk)" PDOs that a previous host-run's monitor
|
// OS-side not-present "Generic Monitor (Punktfunk)" PDOs that a previous host-run's monitor
|
||||||
// departures left behind. Reap those here so a fresh host start begins with a clean IddCx
|
// departures left behind. Reap those here so a fresh host start begins with a clean IddCx
|
||||||
// monitor-slot budget — prevents the 0x80070490 slot-exhaustion wedge from carrying across
|
// monitor-slot budget — prevents the 0x80070490 slot-exhaustion wedge from carrying across
|
||||||
// restarts (the reason a restart's CLEAR_ALL alone never recovered it before).
|
// restarts (the reason a restart's CLEAR_ALL alone never recovered it before).
|
||||||
|
|||||||
@@ -471,7 +471,7 @@ fn web_setup(args: &[String]) -> Result<()> {
|
|||||||
"firewall",
|
"firewall",
|
||||||
"delete",
|
"delete",
|
||||||
"rule",
|
"rule",
|
||||||
"name=punktfunk web console (TCP 47992)",
|
"name=Punktfunk web console (TCP 47992)",
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
if !run_quiet(
|
if !run_quiet(
|
||||||
@@ -481,7 +481,7 @@ fn web_setup(args: &[String]) -> Result<()> {
|
|||||||
"firewall",
|
"firewall",
|
||||||
"add",
|
"add",
|
||||||
"rule",
|
"rule",
|
||||||
"name=punktfunk web console (TCP 47992)",
|
"name=Punktfunk web console (TCP 47992)",
|
||||||
"dir=in",
|
"dir=in",
|
||||||
"action=allow",
|
"action=allow",
|
||||||
"protocol=TCP",
|
"protocol=TCP",
|
||||||
|
|||||||
@@ -995,7 +995,7 @@ fn add_firewall_rules(allow_public: bool) {
|
|||||||
("UDP", "UDP", "47998-48010,9777,5353"),
|
("UDP", "UDP", "47998-48010,9777,5353"),
|
||||||
];
|
];
|
||||||
for (suffix, proto, ports) in rules {
|
for (suffix, proto, ports) in rules {
|
||||||
let name = format!("punktfunk {suffix}");
|
let name = format!("Punktfunk {suffix}");
|
||||||
let ok = run_quiet(
|
let ok = run_quiet(
|
||||||
"netsh",
|
"netsh",
|
||||||
&[
|
&[
|
||||||
@@ -1028,7 +1028,9 @@ fn add_firewall_rules(allow_public: bool) {
|
|||||||
|
|
||||||
fn remove_firewall_rules() {
|
fn remove_firewall_rules() {
|
||||||
for suffix in ["TCP", "UDP"] {
|
for suffix in ["TCP", "UDP"] {
|
||||||
let name = format!("punktfunk {suffix}");
|
// Capital P is the brand; netsh matches a rule name case-INSENSITIVELY, so this still
|
||||||
|
// reaps the lowercase rules every release up to 0.22.1 created — no orphans on upgrade.
|
||||||
|
let name = format!("Punktfunk {suffix}");
|
||||||
let _ = run_quiet(
|
let _ = run_quiet(
|
||||||
"netsh",
|
"netsh",
|
||||||
&[
|
&[
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
Wire-compatible with 0.21.x and 0.22.x — nothing about streaming changed, and everything already paired keeps working. This release only touches Windows PCs you stream *to*; the apps on your phone, tablet, Mac and TV are unchanged, as are Linux hosts.
|
||||||
|
|
||||||
|
**If you stream to a Windows PC and use a controller, update that PC.** On 0.22.0 and 0.22.1 your controller worked everywhere in the app but no game on the PC ever saw it. Update using the Windows installer rather than replacing the program by hand — the repair includes the controller drivers themselves.
|
||||||
|
|
||||||
|
## Fixed
|
||||||
|
|
||||||
|
- **Controllers work again on Windows.** Your controller paired, the app responded to it, and a DualSense's touchpad could even still move the mouse pointer on the PC — but games saw no controller at all. Windows had been attaching one of its own built-in drivers to Punktfunk's virtual controller instead of Punktfunk's, and that driver cannot run on a controller that isn't physically plugged in, so the controller was created and then never started. Nothing you could change in the app worked around it.
|
||||||
|
|
||||||
|
This hit almost everybody, because it hit the controller type Punktfunk emulates by default. If you had gone into settings and explicitly chosen DualShock 4, Xbox 360, DualSense Edge or Steam Deck, yours kept working the whole time — only the default was broken. Both 0.22.0 and 0.22.1 are affected; 0.21.0 and earlier are not.
|
||||||
|
|
||||||
|
## Improved
|
||||||
|
|
||||||
|
- **Punktfunk is spelled Punktfunk on Windows.** The virtual display, controllers, mouse and firewall entries Punktfunk creates all announced themselves in lower case. They now carry the proper name — in Device Manager, in your monitor list, and in Windows Firewall. Purely cosmetic, and it appears once the updated drivers install.
|
||||||
|
|
||||||
|
## Under the hood (for developers)
|
||||||
|
|
||||||
|
- The controller regression was a one-line hardware-id slip. `560e663a` renamed the driver *package* `pf-dualsense` → `pf-gamepad` and its message asserted the four hardware ids were untouched — but `WinDsIdentity::dualsense()` was renamed along with it, so the host began advertising `pf_gamepad`, which `pf_gamepad.inx` does not declare (it still binds `root\pf_dualsense` / `pf_dualsense` / `pf_dualshock4` / `pf_dualsenseedge` / `pf_steamdeck` — deliberately, as the contract with already-installed systems). PnP matched none of our models, fell through to the USB ids the same devnode synthesizes for the DualSense identity (`USB\VID_054C&PID_0CE6`, `USB\Class_03`), and Microsoft's inbox `input.inf` won on signature. `HidUsb` then bound a software-enumerated devnode with no USB port behind it and could not start — `CM_PROB_FAILED_START`. Without a start, hidclass never enumerates the collection PDO, so there is no device interface, so the devnode cannot answer a channel proof, and the gamepad channel's v3 delivery gate correctly refused to hand over the shared input section. Every layer downstream behaved exactly as designed.
|
||||||
|
- Measured against a clean install, all four identities served by the one `pf_gamepad.inf`: DualSense (`pf_gamepad`) → `input.inf`/`HidUsb`, failed start; DualShock 4 (`pf_dualshock4`), Edge (`pf_dualsenseedge`) and the virtual mouse (`pf_mouse`) → our package, attached. `devgen /add /hardwareid "root\pf_dualsense"` binds our INF with no problem code.
|
||||||
|
- A new `hwid_matches_inf` test parses `pf_gamepad.inx`'s `[Models]` section and asserts every hardware id the host puts on a pad devnode is declared there, with a vacuity check on the parse so a shape change fails loudly rather than passing empty. `DS4_HWID` / `DECK_HWID` exist so the test pins the same constants the create paths use.
|
||||||
|
- Why the installer and not a binary swap: re-creating a software device with a known instance id revives the existing devnode with its previously-bound driver and never re-ranks against the driver store. Instance ids did not change, so a PC still holding a stale virtual pad bound to `input.inf` would revive `input.inf` even with the correct hardware id. `driver install --gamepad` sweeps those devnodes; replacing the host executable alone does not.
|
||||||
|
- The rename covers the four driver INFs' `[Strings]`, every `SwDeviceProfile` description, `pf-mouse`'s HID manufacturer and product strings, pf-vdisplay's IddCx endpoint names, the EDID `0xFC` display-name descriptor (one byte — `Edid::generate_with` recomputes both block checksums), and the netsh rule names. Left in lower case on purpose, because they are identities rather than display names: the `SwDeviceCreate` enumerator (it *is* the `SWD\PUNKTFUNK\…` instance-id path), `%ProgramData%\punktfunk`, the `CN=punktfunk-driver` cert subject, and `install.rs`' already-lowercased device probes. netsh, `Get-NetFirewallRule -DisplayName` and PowerShell's `-match` are case-insensitive, so the delete paths still reap what earlier releases created.
|
||||||
|
- No wire, ABI or driver-protocol changes: wire protocol 2, C ABI 13, Windows virtual-gamepad channel 3, virtual-display driver protocol 6 — identical to 0.22.0 and 0.22.1.
|
||||||
@@ -90,13 +90,13 @@ ServiceBinary="%13%\pf_gamepad.dll"
|
|||||||
pf_gamepad.dll
|
pf_gamepad.dll
|
||||||
|
|
||||||
[Strings]
|
[Strings]
|
||||||
ProviderString ="punktfunk"
|
ProviderString ="Punktfunk"
|
||||||
ManufacturerString ="punktfunk"
|
ManufacturerString ="Punktfunk"
|
||||||
ClassName ="HID device"
|
ClassName ="HID device"
|
||||||
Disk_Description ="punktfunk Gamepad Installation Disk"
|
Disk_Description ="Punktfunk Gamepad Installation Disk"
|
||||||
; One per hardware id — these are what Device Manager shows. Keep them aligned with the product
|
; One per hardware id — these are what Device Manager shows. Keep them aligned with the product
|
||||||
; strings the driver serves per device_type (src/lib.rs `on_get_string`).
|
; strings the driver serves per device_type (src/lib.rs `on_get_string`).
|
||||||
DeviceDesc ="punktfunk Virtual DualSense"
|
DeviceDesc ="Punktfunk Virtual DualSense"
|
||||||
DeviceDescDS4 ="punktfunk Virtual DualShock 4"
|
DeviceDescDS4 ="Punktfunk Virtual DualShock 4"
|
||||||
DeviceDescEdge ="punktfunk Virtual DualSense Edge"
|
DeviceDescEdge ="Punktfunk Virtual DualSense Edge"
|
||||||
DeviceDescDeck ="punktfunk Virtual Steam Deck Controller"
|
DeviceDescDeck ="Punktfunk Virtual Steam Deck Controller"
|
||||||
|
|||||||
@@ -72,8 +72,8 @@ ServiceBinary="%13%\pf_mouse.dll"
|
|||||||
pf_mouse.dll
|
pf_mouse.dll
|
||||||
|
|
||||||
[Strings]
|
[Strings]
|
||||||
ProviderString ="punktfunk"
|
ProviderString ="Punktfunk"
|
||||||
ManufacturerString ="punktfunk"
|
ManufacturerString ="Punktfunk"
|
||||||
ClassName ="HID device"
|
ClassName ="HID device"
|
||||||
Disk_Description ="punktfunk Mouse Installation Disk"
|
Disk_Description ="Punktfunk Mouse Installation Disk"
|
||||||
DeviceDesc ="punktfunk Virtual Mouse"
|
DeviceDesc ="Punktfunk Virtual Mouse"
|
||||||
|
|||||||
@@ -428,14 +428,14 @@ fn on_get_string(request: &Request) -> NTSTATUS {
|
|||||||
};
|
};
|
||||||
let string_id = id_val & 0xFFFF;
|
let string_id = id_val & 0xFFFF;
|
||||||
let s: String = match string_id {
|
let s: String = match string_id {
|
||||||
0 | 0x000E => "punktfunk".into(),
|
0 | 0x000E => "Punktfunk".into(),
|
||||||
// (2) The SERIAL carries the channel proof — the one transport measured to reach a UMDF HID
|
// (2) The SERIAL carries the channel proof — the one transport measured to reach a UMDF HID
|
||||||
// minidriver from user mode (`HidD_GetSerialNumberString`, zero-access handle, verified on
|
// minidriver from user mode (`HidD_GetSerialNumberString`, zero-access handle, verified on
|
||||||
// .173). Safe HERE and only here: nothing reads the virtual mouse's serial, whereas the pads'
|
// .173). Safe HERE and only here: nothing reads the virtual mouse's serial, whereas the pads'
|
||||||
// serials are what SDL and Steam dedup controllers on. The old value was the inert
|
// serials are what SDL and Steam dedup controllers on. The old value was the inert
|
||||||
// "PFMOUSE00"; the proof text is just as inert and does the security work.
|
// "PFMOUSE00"; the proof text is just as inert and does the security work.
|
||||||
2 | 0x0010 => ChannelProof::new(CHANNEL.index(), std::process::id()).to_hid_string(),
|
2 | 0x0010 => ChannelProof::new(CHANNEL.index(), std::process::id()).to_hid_string(),
|
||||||
_ => "punktfunk Virtual Mouse".into(),
|
_ => "Punktfunk Virtual Mouse".into(),
|
||||||
};
|
};
|
||||||
let mut wide: Vec<u8> = Vec::with_capacity(s.len() * 2 + 2);
|
let mut wide: Vec<u8> = Vec::with_capacity(s.len() * 2 + 2);
|
||||||
for u in s.encode_utf16() {
|
for u in s.encode_utf16() {
|
||||||
|
|||||||
@@ -75,10 +75,10 @@ UMDriverCopy=12,UMDF
|
|||||||
pf_vdisplay.dll
|
pf_vdisplay.dll
|
||||||
|
|
||||||
[Strings]
|
[Strings]
|
||||||
ManufacturerName="punktfunk"
|
ManufacturerName="Punktfunk"
|
||||||
DiskName="punktfunk Virtual Display Installation Disk"
|
DiskName="Punktfunk Virtual Display Installation Disk"
|
||||||
WudfRdDisplayName="Windows Driver Foundation - User-mode Driver Framework Reflector"
|
WudfRdDisplayName="Windows Driver Foundation - User-mode Driver Framework Reflector"
|
||||||
DeviceName="punktfunk Virtual Display"
|
DeviceName="Punktfunk Virtual Display"
|
||||||
|
|
||||||
REG_MULTI_SZ=0x00010000
|
REG_MULTI_SZ=0x00010000
|
||||||
REG_SZ=0x00000000
|
REG_SZ=0x00000000
|
||||||
|
|||||||
@@ -82,8 +82,8 @@ pub fn init_adapter(device: WDFDEVICE) -> NTSTATUS {
|
|||||||
diag.Size = core::mem::size_of::<iddcx::IDDCX_ENDPOINT_DIAGNOSTIC_INFO>() as u32;
|
diag.Size = core::mem::size_of::<iddcx::IDDCX_ENDPOINT_DIAGNOSTIC_INFO>() as u32;
|
||||||
diag.GammaSupport = iddcx::IDDCX_FEATURE_IMPLEMENTATION::IDDCX_FEATURE_IMPLEMENTATION_NONE;
|
diag.GammaSupport = iddcx::IDDCX_FEATURE_IMPLEMENTATION::IDDCX_FEATURE_IMPLEMENTATION_NONE;
|
||||||
diag.TransmissionType = iddcx::IDDCX_TRANSMISSION_TYPE::IDDCX_TRANSMISSION_TYPE_WIRED_OTHER;
|
diag.TransmissionType = iddcx::IDDCX_TRANSMISSION_TYPE::IDDCX_TRANSMISSION_TYPE_WIRED_OTHER;
|
||||||
diag.pEndPointFriendlyName = wstr!("punktfunk Virtual Display Adapter");
|
diag.pEndPointFriendlyName = wstr!("Punktfunk Virtual Display Adapter");
|
||||||
diag.pEndPointManufacturerName = wstr!("punktfunk");
|
diag.pEndPointManufacturerName = wstr!("Punktfunk");
|
||||||
diag.pEndPointModelName = wstr!("Virtual Display");
|
diag.pEndPointModelName = wstr!("Virtual Display");
|
||||||
// SAFETY: `version` is a stack local that outlives this `init_adapter` call; IddCxAdapterInitAsync
|
// SAFETY: `version` is a stack local that outlives this `init_adapter` call; IddCxAdapterInitAsync
|
||||||
// (below) reads through these pointers SYNCHRONOUSLY, before `version` drops — the pointer never escapes.
|
// (below) reads through these pointers SYNCHRONOUSLY, before `version` drops — the pointer never escapes.
|
||||||
|
|||||||
@@ -6,7 +6,9 @@
|
|||||||
//! made "Use HDR" never appear for the virtual display). The base block declares EDID 1.4 + 10-bit
|
//! made "Use HDR" never appear for the virtual display). The base block declares EDID 1.4 + 10-bit
|
||||||
//! digital so the panel's bit depth is unambiguous.
|
//! digital so the panel's bit depth is unambiguous.
|
||||||
//!
|
//!
|
||||||
//! Identity: manufacturer "PNK" (bytes 8-9), product name "punktfunk" (the 0xFC display descriptor). The
|
//! Identity: manufacturer "PNK" (bytes 8-9), product name "Punktfunk" (the 0xFC display descriptor —
|
||||||
|
//! this is what Windows shows as `Generic Monitor (Punktfunk)`; byte 127's checksum is recomputed in
|
||||||
|
//! [`Edid::generate_with`], so editing the name here needs no hand-patched checksum). The
|
||||||
//! serial-number field (base offset 0x0C, little-endian) encodes the per-monitor index so
|
//! serial-number field (base offset 0x0C, little-endian) encodes the per-monitor index so
|
||||||
//! `parse_monitor_description` can map an EDID the OS hands back to its monitor; [`Edid::generate_with`]
|
//! `parse_monitor_description` can map an EDID the OS hands back to its monitor; [`Edid::generate_with`]
|
||||||
//! patches that serial and recomputes BOTH block checksums (base byte 127 + extension byte 255). The
|
//! patches that serial and recomputes BOTH block checksums (base byte 127 + extension byte 255). The
|
||||||
@@ -34,7 +36,7 @@ const BASE: [u8; 128] = [
|
|||||||
0x45, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E,
|
0x45, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E,
|
||||||
0x00, 0x00, 0x00, 0xFD, 0x00, 0x17, 0xF0, 0x0F, // display range-limits descriptor
|
0x00, 0x00, 0x00, 0xFD, 0x00, 0x17, 0xF0, 0x0F, // display range-limits descriptor
|
||||||
0xFF, 0x0F, 0x00, 0x0A, 0x20, 0x20, 0x20, 0x20,
|
0xFF, 0x0F, 0x00, 0x0A, 0x20, 0x20, 0x20, 0x20,
|
||||||
0x20, 0x20, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x70, // name descriptor "punktfunk"
|
0x20, 0x20, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x50, // name descriptor "Punktfunk"
|
||||||
0x75, 0x6E, 0x6B, 0x74, 0x66, 0x75, 0x6E, 0x6B,
|
0x75, 0x6E, 0x6B, 0x74, 0x66, 0x75, 0x6E, 0x6B,
|
||||||
0x0A, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0x00, // empty 4th descriptor...
|
0x0A, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0x00, // empty 4th descriptor...
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ UmdfLibraryVersion=$UMDFVERSION$
|
|||||||
ServiceBinary=%13%\pf_xusb.dll
|
ServiceBinary=%13%\pf_xusb.dll
|
||||||
|
|
||||||
[Strings]
|
[Strings]
|
||||||
ProviderString = "punktfunk"
|
ProviderString = "Punktfunk"
|
||||||
StdMfg = "(Standard system devices)"
|
StdMfg = "(Standard system devices)"
|
||||||
DiskId1 = "punktfunk XUSB Installation Disk"
|
DiskId1 = "Punktfunk XUSB Installation Disk"
|
||||||
DeviceDesc = "punktfunk Virtual Xbox 360 (XUSB)"
|
DeviceDesc = "Punktfunk Virtual Xbox 360 (XUSB)"
|
||||||
|
|||||||
@@ -351,7 +351,7 @@ Filename: "{app}\punktfunk-host.exe"; Parameters: "driver uninstall --gamepad";
|
|||||||
; Stop + remove the PunktfunkWeb task and its firewall rule (leaves %ProgramData%\punktfunk config,
|
; Stop + remove the PunktfunkWeb task and its firewall rule (leaves %ProgramData%\punktfunk config,
|
||||||
; like the host uninstall does).
|
; like the host uninstall does).
|
||||||
Filename: "powershell.exe"; \
|
Filename: "powershell.exe"; \
|
||||||
Parameters: "-NoProfile -ExecutionPolicy Bypass -Command ""Stop-ScheduledTask -TaskName PunktfunkWeb -ErrorAction SilentlyContinue; Get-NetTCPConnection -LocalPort 47992,3000 -State Listen -ErrorAction SilentlyContinue | ForEach-Object {{ Stop-Process -Id $_.OwningProcess -Force -ErrorAction SilentlyContinue }; Unregister-ScheduledTask -TaskName PunktfunkWeb -Confirm:$false -ErrorAction SilentlyContinue; Get-NetFirewallRule -DisplayName 'punktfunk web console (*' -ErrorAction SilentlyContinue | Remove-NetFirewallRule"""; \
|
Parameters: "-NoProfile -ExecutionPolicy Bypass -Command ""Stop-ScheduledTask -TaskName PunktfunkWeb -ErrorAction SilentlyContinue; Get-NetTCPConnection -LocalPort 47992,3000 -State Listen -ErrorAction SilentlyContinue | ForEach-Object {{ Stop-Process -Id $_.OwningProcess -Force -ErrorAction SilentlyContinue }; Unregister-ScheduledTask -TaskName PunktfunkWeb -Confirm:$false -ErrorAction SilentlyContinue; Get-NetFirewallRule -DisplayName 'Punktfunk web console (*' -ErrorAction SilentlyContinue | Remove-NetFirewallRule"""; \
|
||||||
Flags: runhidden waituntilterminated; RunOnceId: "PunktfunkWebCleanup"
|
Flags: runhidden waituntilterminated; RunOnceId: "PunktfunkWebCleanup"
|
||||||
#endif
|
#endif
|
||||||
#ifdef WithScripting
|
#ifdef WithScripting
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
.DESCRIPTION
|
.DESCRIPTION
|
||||||
Sustained connect/disconnect churn (e.g. a client reconnect loop x the host's 8 pipeline-build
|
Sustained connect/disconnect churn (e.g. a client reconnect loop x the host's 8 pipeline-build
|
||||||
retries - ~100 ADD/REMOVE cycles) exhausts the driver's IddCx monitor slots: the per-monitor
|
retries - ~100 ADD/REMOVE cycles) exhausts the driver's IddCx monitor slots: the per-monitor
|
||||||
target_ids climb, ghost "Generic Monitor (punktfunk)" device nodes pile up, and eventually
|
target_ids climb, ghost "Generic Monitor (Punktfunk)" device nodes pile up, and eventually
|
||||||
IOCTL_ADD returns 0x80070490 ERROR_NOT_FOUND ("Element nicht gefunden"). Every session then fails
|
IOCTL_ADD returns 0x80070490 ERROR_NOT_FOUND ("Element nicht gefunden"). Every session then fails
|
||||||
to create a virtual output -> the client gets a hard blackscreen. A host-service restart's
|
to create a virtual output -> the client gets a hard blackscreen. A host-service restart's
|
||||||
IOCTL_CLEAR_ALL does NOT recover it; the driver instance itself must be reloaded.
|
IOCTL_CLEAR_ALL does NOT recover it; the driver instance itself must be reloaded.
|
||||||
@@ -16,16 +16,16 @@
|
|||||||
1. Stop the host service (it holds the driver's control device).
|
1. Stop the host service (it holds the driver's control device).
|
||||||
2. pnputil /remove-device the GHOST (Status != OK = not-present) punktfunk virtual-monitor nodes
|
2. pnputil /remove-device the GHOST (Status != OK = not-present) punktfunk virtual-monitor nodes
|
||||||
that accumulated - the root of the slot exhaustion.
|
that accumulated - the root of the slot exhaustion.
|
||||||
3. Disable + Enable the pf-vdisplay adapter (ROOT\DISPLAY\*, "punktfunk Virtual Display") to
|
3. Disable + Enable the pf-vdisplay adapter (ROOT\DISPLAY\*, "Punktfunk Virtual Display") to
|
||||||
reload the IddCx driver instance and reset its monitor list. (Restart-PnpDevice does NOT exist
|
reload the IddCx driver instance and reset its monitor list. (Restart-PnpDevice does NOT exist
|
||||||
on this box's PowerShell, so we disable+enable explicitly.)
|
on this box's PowerShell, so we disable+enable explicitly.)
|
||||||
4. Restart the host service.
|
4. Restart the host service.
|
||||||
Avoids a reboot on purpose (this box boots to Proxmox).
|
Avoids a reboot on purpose (this box boots to Proxmox).
|
||||||
|
|
||||||
.PARAMETER Service Host service name. Default PunktfunkHost.
|
.PARAMETER Service Host service name. Default PunktfunkHost.
|
||||||
.PARAMETER AdapterName FriendlyName substring of the IddCx adapter to cycle. Default "punktfunk
|
.PARAMETER AdapterName FriendlyName substring of the IddCx adapter to cycle. Default "Punktfunk
|
||||||
Virtual Display" (NOT SudoVDA's "SudoMaker Virtual Display Adapter").
|
Virtual Display" (NOT SudoVDA's "SudoMaker Virtual Display Adapter").
|
||||||
.PARAMETER GhostMatch FriendlyName substring of the virtual monitors to reap. Default "punktfunk".
|
.PARAMETER GhostMatch FriendlyName substring of the virtual monitors to reap. Default "Punktfunk".
|
||||||
.PARAMETER KeepGhosts Skip the ghost-node cleanup; only cycle the adapter.
|
.PARAMETER KeepGhosts Skip the ghost-node cleanup; only cycle the adapter.
|
||||||
.PARAMETER NoHost Don't stop/start the host service (just reset the driver) - used by
|
.PARAMETER NoHost Don't stop/start the host service (just reset the driver) - used by
|
||||||
redeploy-pf-vdisplay.ps1, which manages the service itself.
|
redeploy-pf-vdisplay.ps1, which manages the service itself.
|
||||||
@@ -41,8 +41,8 @@
|
|||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
param(
|
param(
|
||||||
[string]$Service = 'PunktfunkHost',
|
[string]$Service = 'PunktfunkHost',
|
||||||
[string]$AdapterName = 'punktfunk Virtual Display',
|
[string]$AdapterName = 'Punktfunk Virtual Display',
|
||||||
[string]$GhostMatch = 'punktfunk',
|
[string]$GhostMatch = 'Punktfunk',
|
||||||
[switch]$KeepGhosts,
|
[switch]$KeepGhosts,
|
||||||
[switch]$NoHost,
|
[switch]$NoHost,
|
||||||
[switch]$Verify,
|
[switch]$Verify,
|
||||||
|
|||||||
Reference in New Issue
Block a user