feat(windows): bundle pf-vdisplay in the host installer; drop SudoVDA

Switch the Inno Setup installer's virtual-display driver from the vendored SudoVDA
C++ binary to our own all-Rust pf-vdisplay (validated streaming at 5120x1440@240).

- packaging/windows/pf-vdisplay/: vendored SIGNED driver (pf_vdisplay.dll/inf/cat +
  punktfunk-driver.cer, the same cert the gamepad drivers ship), built from
  vdisplay-driver/ via deploy-dev.ps1.
- install-pf-vdisplay.ps1 / stage-pf-vdisplay.ps1: mirror the SudoVDA scripts -
  trust cert -> gated ROOT\pf_vdisplay node via nefconc (NEVER devgen) -> pnputil
  /add-driver /install. Idempotent, best-effort (never aborts the install).
- punktfunk-host.iss + pack-host-installer.ps1: install the pf-vdisplay bundle
  under the existing installdriver task.
- Removed the vendored SudoVDA driver + install-sudovda.ps1 + stage-sudovda.ps1.
- README + windows-host.yml: SudoVDA -> pf-vdisplay.

The host's vdisplay/sudovda.rs backend is unchanged - it drives whichever driver
provides the {e5bcc234} interface, now pf-vdisplay. Live installer build/test on
the runner is the remaining step.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-22 22:16:30 +02:00
parent e27abc065e
commit c5dab484df
14 changed files with 161 additions and 71 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
# Build the punktfunk Windows HOST as a signed Inno Setup installer and publish it to Gitea's generic # Build the punktfunk Windows HOST as a signed Inno Setup installer and publish it to Gitea's generic
# package registry, so a Windows GPU box can install the streaming host (SYSTEM service + bundled # package registry, so a Windows GPU box can install the streaming host (SYSTEM service + bundled
# SudoVDA virtual-display driver + the web management console, run by a scheduled task on a bundled # pf-vdisplay virtual-display driver + the web management console, run by a scheduled task on a bundled
# bun) from one signed setup.exe. Runs on the self-hosted Windows runner # bun) from one signed setup.exe. Runs on the self-hosted Windows runner
# (host mode; scripts/ci/setup-windows-runner.ps1) — same MSVC/Windows-SDK/LLVM env as windows.yml. # (host mode; scripts/ci/setup-windows-runner.ps1) — same MSVC/Windows-SDK/LLVM env as windows.yml.
# #
+24 -20
View File
@@ -6,16 +6,16 @@ generic package registry (`punktfunk-host-windows`) by `.gitea/workflows/windows
## x64 only (no ARM64) ## x64 only (no ARM64)
Unlike the client (which ships x64 + ARM64 MSIX), the host is **x64-only by design**. It is coupled to Unlike the client (which ships x64 + ARM64 MSIX), the host is **x64-only by design**. It is coupled to
an NVIDIA GPU (NVENC, via `nvEncodeAPI64.dll` from the driver) and the **SudoVDA** virtual-display an NVIDIA GPU (NVENC, via `nvEncodeAPI64.dll` from the driver) and the **pf-vdisplay** virtual-display
driver — neither exists on Windows ARM64 (no ARM64 NVIDIA driver; the vendored SudoVDA is x64-only). An driver — neither exists on Windows ARM64 (no ARM64 NVIDIA driver; the driver builds x64-only). An
ARM64 host would install but couldn't encode or create a virtual display, so we don't build one. ARM64 host would install but couldn't encode or create a virtual display, so we don't build one.
Revisit if NVIDIA-ARM Windows PCs + an ARM64 SudoVDA ever ship. Revisit if NVIDIA-ARM Windows PCs ever ship.
## Why not MSIX (like the client) ## Why not MSIX (like the client)
The host installs a **`LocalSystem` SCM service** that `CreateProcessAsUserW`'s from Session 0 into the The host installs a **`LocalSystem` SCM service** that `CreateProcessAsUserW`'s from Session 0 into the
interactive session for secure-desktop (UAC / lock screen) capture, adds firewall rules, and depends interactive session for secure-desktop (UAC / lock screen) capture, adds firewall rules, and depends
on the **SudoVDA** kernel/IDD virtual-display driver. MSIX's sandbox can install **neither** a SYSTEM on the **pf-vdisplay** UMDF/IDD virtual-display driver. MSIX's sandbox can install **neither** a SYSTEM
service of this kind **nor** a driver. So the host ships as a classic elevated installer. service of this kind **nor** a driver. So the host ships as a classic elevated installer.
The installer is deliberately thin: the real install logic — SCM registration, firewall rules, the The installer is deliberately thin: the real install logic — SCM registration, firewall rules, the
@@ -26,9 +26,10 @@ exe into `C:\Program Files\punktfunk\` and calls that subcommand, elevated.
## What the installer does ## What the installer does
- Installs `punktfunk-host.exe` (+ `host.env.example`, this README) to `{app}` (`C:\Program Files\punktfunk`). - Installs `punktfunk-host.exe` (+ `host.env.example`, this README) to `{app}` (`C:\Program Files\punktfunk`).
- **Optional task** *Install the SudoVDA virtual display driver* — imports the driver's self-signed - **Optional task** *Install the pf-vdisplay virtual display driver* — imports the driver's self-signed
cert (machine `Root` + `TrustedPublisher`), creates the `root\sudomaker\sudovda` device node (only cert (machine `Root` + `TrustedPublisher`), creates the `root\pf_vdisplay` device node (only
if absent — `install-sudovda.ps1`), and stages the driver with `pnputil /add-driver /install`. if absent, via nefconc — never devgen`install-pf-vdisplay.ps1`), and stages the driver with
`pnputil /add-driver /install`.
Best-effort: a driver failure warns but never aborts the install (the host degrades to a physical Best-effort: a driver failure warns but never aborts the install (the host degrades to a physical
display without it). display without it).
- Runs `punktfunk-host service install` (idempotent; writes a default `host.env` only if absent, so - Runs `punktfunk-host service install` (idempotent; writes a default `host.env` only if absent, so
@@ -45,7 +46,7 @@ exe into `C:\Program Files\punktfunk\` and calls that subcommand, elevated.
(otherwise the locked exe / respawning supervisor would block the copy), then re-points the service; (otherwise the locked exe / respawning supervisor would block the copy), then re-points the service;
the existing console password is kept (the wizard page is skipped). the existing console password is kept (the wizard page is skipped).
- **Uninstall** (Add/Remove Programs): runs `service uninstall` (stop + delete service + remove - **Uninstall** (Add/Remove Programs): runs `service uninstall` (stop + delete service + remove
firewall rules) and removes the `PunktfunkWeb` task + its firewall rule. The SudoVDA driver and the firewall rules) and removes the `PunktfunkWeb` task + its firewall rule. The pf-vdisplay driver and the
`%ProgramData%\punktfunk` config (incl. `web-password`) are intentionally left in place. `%ProgramData%\punktfunk` config (incl. `web-password`) are intentionally left in place.
Silent install: `punktfunk-host-setup-<ver>.exe /VERYSILENT` (omit the driver with Silent install: `punktfunk-host-setup-<ver>.exe /VERYSILENT` (omit the driver with
@@ -65,21 +66,24 @@ read it from `%ProgramData%\punktfunk\web-password`.
|------|------| |------|------|
| `punktfunk-host.iss` | Inno Setup script (the installer definition). | | `punktfunk-host.iss` | Inno Setup script (the installer definition). |
| `pack-host-installer.ps1` | Orchestrator: cert + sign, stage the driver + FFmpeg + **web console** (`.output` + bun) bundles, run ISCC, sign setup.exe, emit registry paths. | | `pack-host-installer.ps1` | Orchestrator: cert + sign, stage the driver + FFmpeg + **web console** (`.output` + bun) bundles, run ISCC, sign setup.exe, emit registry paths. |
| `stage-sudovda.ps1` | Stage the **vendored** SudoVDA driver + fetch/verify the **pinned** nefcon release into the bundle. | | `stage-pf-vdisplay.ps1` | Stage the **vendored** pf-vdisplay driver + fetch/verify the **pinned** nefcon release into the bundle. |
| `install-sudovda.ps1` | Runs at install time (elevated): trust cert → gated device-node create → `pnputil` install. | | `install-pf-vdisplay.ps1` | Runs at install time (elevated): trust cert → gated device-node create (nefconc)`pnputil` install. |
| `../../scripts/windows/web-run.cmd` | The `PunktfunkWeb` task action: loads the mgmt token + login password env, runs the bundled `bun` on the Nitro server (`:3000`). | | `../../scripts/windows/web-run.cmd` | The `PunktfunkWeb` task action: loads the mgmt token + login password env, runs the bundled `bun` on the Nitro server (`:3000`). |
| `../../scripts/windows/web-setup.ps1` | Install-time (elevated): write the ACL'd console password, register the `PunktfunkWeb` task + firewall rule, start it. | | `../../scripts/windows/web-setup.ps1` | Install-time (elevated): write the ACL'd console password, register the `PunktfunkWeb` task + firewall rule, start it. |
| `sudovda/` | **Vendored** prebuilt SudoVDA driver: `SudoVDA.inf` / `sudovda.cat` / `SudoVDA.dll` / `sudovda.cer`. | | `pf-vdisplay/` | **Vendored** signed pf-vdisplay driver: `pf_vdisplay.inf` / `pf_vdisplay.cat` / `pf_vdisplay.dll` / `punktfunk-driver.cer`. Built from `vdisplay-driver/`. |
| `vdisplay-driver/` | The all-Rust IddCx **driver source** (`pf-vdisplay` crate + vendored `wdf-umdf*` bindings) + `deploy-dev.ps1` (build/sign/install for dev). |
| `nvenc/nvenc.def`, `nvenc/gen-nvenc-importlib.ps1` | Synthesise `nvencodeapi.lib` for the `--features nvenc` link (llvm-dlltool / lib.exe). | | `nvenc/nvenc.def`, `nvenc/gen-nvenc-importlib.ps1` | Synthesise `nvencodeapi.lib` for the `--features nvenc` link (llvm-dlltool / lib.exe). |
> **Vendored driver:** SudoVDA has no upstream release (its repo is a source-only VS solution; Apollo > **Vendored driver:** pf-vdisplay is our **all-Rust IddCx** virtual display (UMDF2), built from
> embeds the driver in its own installer), so the prebuilt **signed** driver is checked in under > `packaging/windows/vdisplay-driver/`. It replaced the vendored SudoVDA C++ driver — full story in
> `sudovda/` (MIT/CC0; v1.10.9.289, signer `CN=sudovda@su.mk`, Class=Display, HWID > [`docs/windows-virtual-display-rust-port.md`](../../docs/windows-virtual-display-rust-port.md). The
> `Root\SudoMaker\SudoVDA`). To refresh it, copy the four files out of a box's driver store > **signed** output (`pf_vdisplay.dll`/`.inf`/`.cat` + `punktfunk-driver.cer`; signer
> (`C:\Windows\System32\DriverStore\FileRepository\sudovda.inf_amd64_*`) and re-derive `sudovda.cer` > `punktfunk-ds-test` — the same cert the gamepad drivers ship, Class=Display, HWID `root\pf_vdisplay`)
> from the `.cat` signer (`(Get-AuthenticodeSignature sudovda.cat).SignerCertificate | Export-Certificate`). > is checked in under `pf-vdisplay/`. To refresh it after a driver-source change, rebuild + re-sign with
> nefcon (the device-node tool) **is** fetched + SHA-256-verified from its pinned release in > `vdisplay-driver/deploy-dev.ps1` and copy the staged `pf_vdisplay.{dll,inf,cat}` over the vendored
> `stage-sudovda.ps1`. > copies. nefcon (the device-node tool — the install creates the node with it, **never** `devgen`, which
> leaves persistent phantom devices) **is** fetched + SHA-256-verified from its pinned release in
> `stage-pf-vdisplay.ps1`.
## Build locally (Windows, MSVC + Windows SDK + Inno Setup) ## Build locally (Windows, MSVC + Windows SDK + Inno Setup)
@@ -91,7 +95,7 @@ $env:PUNKTFUNK_NVENC_LIB_DIR = 'C:\t\nvenc'
# 2. build the host # 2. build the host
cargo build --release -p punktfunk-host --features nvenc cargo build --release -p punktfunk-host --features nvenc
# 3. pack (self-signed unless MSIX_CERT_PFX_B64/MSIX_CERT_PASSWORD are set; -NoDriver to skip SudoVDA) # 3. pack (self-signed unless MSIX_CERT_PFX_B64/MSIX_CERT_PASSWORD are set; -NoDriver to skip pf-vdisplay)
pwsh -File packaging\windows\pack-host-installer.ps1 -Version 0.0.0-dev -TargetDir C:\t\release -OutDir C:\t\out pwsh -File packaging\windows\pack-host-installer.ps1 -Version 0.0.0-dev -TargetDir C:\t\release -OutDir C:\t\out
``` ```
@@ -1,32 +1,35 @@
<# <#
.SYNOPSIS .SYNOPSIS
Install the bundled SudoVDA virtual-display driver. Runs ELEVATED at setup time (invoked from the Install the bundled pf-vdisplay (punktfunk) virtual-display driver our all-Rust IddCx replacement
installer's [Run] section). Best-effort: warns and exits 0 on any failure the host degrades to a for SudoVDA. Runs ELEVATED at setup time (invoked from the installer's [Run] section). Best-effort:
physical display without SudoVDA, so a driver hiccup must never abort the whole install. warns and exits 0 on any failure the host degrades to a physical display without a virtual display,
so a driver hiccup must never abort the whole install.
.DESCRIPTION .DESCRIPTION
-Dir holds the staged payload from fetch-sudovda.ps1 (the .inf/.cat/.dll + signing .cer + nefconc.exe). -Dir holds the staged payload (pf_vdisplay.inf/.cat/.dll + signing .cer + nefconc.exe). Steps:
Steps: 1. Trust the self-signed driver cert (machine Root + TrustedPublisher) so PnP installs it silently
1. Trust the self-signed driver cert (machine Root + TrustedPublisher) so PnP installs it silently. (the same punktfunk-ds-test cert the gamepad drivers ship).
2. Create the root device node IF ABSENT (gated a blind re-create spawns a phantom duplicate, and 2. Create the ROOT device node IF ABSENT (gated a blind re-create spawns a phantom duplicate, and
the host's open_device() binds interface index 0; crates/punktfunk-host/src/vdisplay/sudovda.rs). the host's open_device() binds interface index 0; crates/punktfunk-host/src/vdisplay/sudovda.rs).
ALWAYS via nefconc (a clean ROOT\DISPLAY node) NEVER devgen, which makes persistent SWD\DEVGEN
software devices that survive reboot + registry deletion and resurrect on every driver install.
3. Stage + bind the driver (pnputil /add-driver /install modern, in-box, idempotent). 3. Stage + bind the driver (pnputil /add-driver /install modern, in-box, idempotent).
Class/ClassGuid are read from the .inf so they always match the shipped driver. Class/ClassGuid are read from the .inf so they always match the shipped driver.
.EXAMPLE .EXAMPLE
powershell -ExecutionPolicy Bypass -File install-sudovda.ps1 -Dir C:\path\to\sudovda powershell -ExecutionPolicy Bypass -File install-pf-vdisplay.ps1 -Dir C:\path\to\pf-vdisplay
#> #>
[CmdletBinding()] [CmdletBinding()]
param( param(
[string]$Dir = $PSScriptRoot, [string]$Dir = $PSScriptRoot,
[string]$HardwareId = 'root\sudomaker\sudovda' # verified live (docs/windows-host.md) [string]$HardwareId = 'root\pf_vdisplay' # matches pf_vdisplay.inf [Standard.NTamd64]
) )
# Never abort the installer on a driver failure. # Never abort the installer on a driver failure.
$ErrorActionPreference = 'Continue' $ErrorActionPreference = 'Continue'
trap { Write-Warning "SudoVDA install error: $_"; exit 0 } trap { Write-Warning "pf-vdisplay install error: $_"; exit 0 }
function Test-SudoVdaPresent { function Test-PfVdisplayPresent {
$devs = Get-PnpDevice -Class Display -PresentOnly -ErrorAction SilentlyContinue $devs = Get-PnpDevice -Class Display -PresentOnly -ErrorAction SilentlyContinue
foreach ($d in $devs) { foreach ($d in $devs) {
$hw = (Get-PnpDeviceProperty -InstanceId $d.InstanceId -KeyName 'DEVPKEY_Device_HardwareIds' ` $hw = (Get-PnpDeviceProperty -InstanceId $d.InstanceId -KeyName 'DEVPKEY_Device_HardwareIds' `
@@ -36,14 +39,14 @@ function Test-SudoVdaPresent {
return $false return $false
} }
$inf = Get-ChildItem -Path $Dir -Filter *.inf -Recurse -ErrorAction SilentlyContinue | Select-Object -First 1 $inf = Get-ChildItem -Path $Dir -Filter pf_vdisplay.inf -Recurse -ErrorAction SilentlyContinue | Select-Object -First 1
$cer = Get-ChildItem -Path $Dir -Filter *.cer -Recurse -ErrorAction SilentlyContinue | Select-Object -First 1 $cer = Get-ChildItem -Path $Dir -Filter *.cer -Recurse -ErrorAction SilentlyContinue | Select-Object -First 1
$nef = Get-ChildItem -Path $Dir -Filter 'nefconc.exe' -Recurse -ErrorAction SilentlyContinue | Select-Object -First 1 $nef = Get-ChildItem -Path $Dir -Filter 'nefconc.exe' -Recurse -ErrorAction SilentlyContinue | Select-Object -First 1
if (-not $inf) { Write-Warning "no SudoVDA .inf in $Dir; skipping driver install."; exit 0 } if (-not $inf) { Write-Warning "no pf_vdisplay.inf in $Dir; skipping driver install."; exit 0 }
Write-Host "SudoVDA inf: $($inf.FullName)" Write-Host "pf-vdisplay inf: $($inf.FullName)"
# 1) Trust the self-signed driver cert (a self-signed driver needs the cert in BOTH the machine # 1) Trust the self-signed driver cert (a self-signed driver needs the cert in BOTH the machine Root
# Root store, so the chain validates, and TrustedPublisher, so PnP installs without a prompt). # store, so the chain validates, and TrustedPublisher, so PnP installs without a prompt).
if ($cer) { if ($cer) {
Write-Host "==> importing $($cer.Name) to Root + TrustedPublisher" Write-Host "==> importing $($cer.Name) to Root + TrustedPublisher"
certutil.exe -addstore -f Root "$($cer.FullName)" | Out-Null certutil.exe -addstore -f Root "$($cer.FullName)" | Out-Null
@@ -51,9 +54,9 @@ if ($cer) {
} }
else { Write-Warning "no .cer in $Dir — driver may not install silently (untrusted publisher)" } else { Write-Warning "no .cer in $Dir — driver may not install silently (untrusted publisher)" }
# 2) Create the root device node only if it isn't already there. # 2) Create the root device node only if it isn't already there. nefconc, NEVER devgen.
if (Test-SudoVdaPresent) { if (Test-PfVdisplayPresent) {
Write-Host "SudoVDA device node already present — leaving it as-is." Write-Host "pf-vdisplay device node already present — leaving it as-is."
} }
elseif ($nef) { elseif ($nef) {
$infText = Get-Content -Raw $inf.FullName $infText = Get-Content -Raw $inf.FullName
@@ -66,7 +69,7 @@ elseif ($nef) {
Write-Warning "nefconc --create-device-node returned $LASTEXITCODE" Write-Warning "nefconc --create-device-node returned $LASTEXITCODE"
} }
} }
else { Write-Warning "nefconc.exe not found in $Dir — cannot create the SudoVDA device node." } else { Write-Warning "nefconc.exe not found in $Dir — cannot create the pf-vdisplay device node." }
# 3) Stage + bind the driver (idempotent; re-staging the same .inf is harmless). # 3) Stage + bind the driver (idempotent; re-staging the same .inf is harmless).
Write-Host "==> pnputil /add-driver $($inf.Name) /install" Write-Host "==> pnputil /add-driver $($inf.Name) /install"
+9 -7
View File
@@ -7,7 +7,7 @@
1. resolves a code-signing cert (supplied stable .pfx from CI secrets OR an ephemeral self-signed 1. resolves a code-signing cert (supplied stable .pfx from CI secrets OR an ephemeral self-signed
CN=unom — same scheme as the client's pack-msix.ps1) and exports the public .cer, CN=unom — same scheme as the client's pack-msix.ps1) and exports the public .cer,
2. signs the inner punktfunk-host.exe, 2. signs the inner punktfunk-host.exe,
3. fetches + stages the SudoVDA driver bundle (unless -NoDriver), 3. stages the pf-vdisplay virtual-display driver bundle (unless -NoDriver),
4. runs ISCC to build punktfunk-host-setup-<ver>.exe, 4. runs ISCC to build punktfunk-host-setup-<ver>.exe,
5. signs the setup.exe (timestamp best-effort), 5. signs the setup.exe (timestamp best-effort),
6. emits HOST_SETUP_PATH / HOST_CER_PATH to GITHUB_ENV for the publish step. 6. emits HOST_SETUP_PATH / HOST_CER_PATH to GITHUB_ENV for the publish step.
@@ -28,7 +28,7 @@ param(
[string]$FfmpegDir = $env:FFMPEG_DIR, # bundle its bin\*.dll (amf-qsv build) [string]$FfmpegDir = $env:FFMPEG_DIR, # bundle its bin\*.dll (amf-qsv build)
[string]$WebDir = $env:WEB_OUTPUT_DIR, # built web .output tree -> bundle the mgmt console [string]$WebDir = $env:WEB_OUTPUT_DIR, # built web .output tree -> bundle the mgmt console
[string]$BunExe = $env:BUN_EXE, # portable bun.exe runtime for the console [string]$BunExe = $env:BUN_EXE, # portable bun.exe runtime for the console
[switch]$NoDriver, # build without the bundled SudoVDA driver [switch]$NoDriver, # build without the bundled pf-vdisplay driver
[switch]$NoSign # skip signing (local debug) [switch]$NoSign # skip signing (local debug)
) )
$ErrorActionPreference = 'Stop' $ErrorActionPreference = 'Stop'
@@ -140,17 +140,19 @@ $defines = @(
"/DReadme=$readme" "/DReadme=$readme"
) )
# --- stage the SudoVDA driver bundle ---------------------------------------------------------- # --- stage the pf-vdisplay virtual-display driver bundle --------------------------------------
# pf-vdisplay is our all-Rust IddCx driver (packaging/windows/vdisplay-driver/), vendored signed under
# packaging/windows/pf-vdisplay/. It replaced the vendored SudoVDA C++ driver.
if (-not $NoDriver) { if (-not $NoDriver) {
$stage = Join-Path $OutDir 'stage' $stage = Join-Path $OutDir 'stage'
& (Join-Path $here 'stage-sudovda.ps1') -OutDir $stage & (Join-Path $here 'stage-pf-vdisplay.ps1') -OutDir $stage
Copy-Item (Join-Path $here 'install-sudovda.ps1') (Join-Path $stage 'install-sudovda.ps1') -Force Copy-Item (Join-Path $here 'install-pf-vdisplay.ps1') (Join-Path $stage 'install-pf-vdisplay.ps1') -Force
$defines += "/DStageDir=$stage" $defines += "/DStageDir=$stage"
} }
else { Write-Host "-NoDriver: building installer WITHOUT the bundled SudoVDA driver" } else { Write-Host "-NoDriver: building installer WITHOUT the bundled pf-vdisplay driver" }
# --- stage the punktfunk virtual-gamepad UMDF drivers (DualSense/DS4 + Xbox 360 XUSB) ---------- # --- stage the punktfunk virtual-gamepad UMDF drivers (DualSense/DS4 + Xbox 360 XUSB) ----------
# Vendored, pre-signed under packaging/windows/gamepad-drivers/ (like SudoVDA). Rebuild + re-vendor # Vendored, pre-signed under packaging/windows/gamepad-drivers/ (like pf-vdisplay). Rebuild + re-vendor
# from packaging/windows/{dualsense,xusb}-driver/ when the driver source changes (see their READMEs). # from packaging/windows/{dualsense,xusb}-driver/ when the driver source changes (see their READMEs).
if (-not $NoDriver) { if (-not $NoDriver) {
$gpVendor = Join-Path $here 'gamepad-drivers' $gpVendor = Join-Path $here 'gamepad-drivers'
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,78 @@
;/*++
; pf-vdisplay - punktfunk virtual display, UMDF2 IddCx driver INF (template; stampinf -> .inf).
; Adapted from MolotovCherry/virtual-display-rs (MIT) + SudoVDA's control-device security DACL.
;--*/
[Version]
PnpLockdown=1
Signature="$Windows NT$"
ClassGUID={4D36E968-E325-11CE-BFC1-08002BE10318}
Class=Display
ClassVer=2.0
Provider=%ManufacturerName%
CatalogFile=pf_vdisplay.cat
DriverVer = 06/22/2026,1.0.0622.2210
[Manufacturer]
%ManufacturerName%=Standard,NTamd64
[Standard.NTamd64]
%DeviceName%=pf_vdisplay_Install, Root\pf_vdisplay
[SourceDisksFiles]
pf_vdisplay.dll=1
[SourceDisksNames]
1=%DiskName%
; =================== UMDF IddCx device ====================
[pf_vdisplay_Install.NT]
CopyFiles=UMDriverCopy
[pf_vdisplay_Install.NT.hw]
AddReg=pf_vdisplay_HardwareDeviceSettings
[pf_vdisplay_HardwareDeviceSettings]
HKR, , "UpperFilters", %REG_MULTI_SZ%, "IndirectKmd"
HKR, "WUDF", "DeviceGroupId", %REG_SZ%, "pfVDisplayGroup"
; Let the host (LocalSystem service) + admins open the control device for the ADD/REMOVE/PING IOCTLs.
HKR, , "Security", , "D:P(A;;GA;;;SY)(A;;GA;;;BA)(A;;GRGW;;;WD)"
[pf_vdisplay_Install.NT.Services]
AddService=WUDFRd,0x000001fa,WUDFRD_ServiceInstall
[pf_vdisplay_Install.NT.Wdf]
UmdfService=pf_vdisplay, pf_vdisplay_Install
UmdfServiceOrder=pf_vdisplay
UmdfKernelModeClientPolicy=AllowKernelModeClients
UmdfHostProcessSharing=ProcessSharingDisabled
[pf_vdisplay_Install]
UmdfLibraryVersion=2.15.0
ServiceBinary=%12%\UMDF\pf_vdisplay.dll
UmdfExtensions=IddCx0102
[WUDFRD_ServiceInstall]
DisplayName=%WudfRdDisplayName%
ServiceType=1
StartType=3
ErrorControl=1
ServiceBinary=%12%\WUDFRd.sys
[DestinationDirs]
UMDriverCopy=12,UMDF
[UMDriverCopy]
pf_vdisplay.dll
[Strings]
ManufacturerName="punktfunk"
DiskName="punktfunk Virtual Display Installation Disk"
WudfRdDisplayName="Windows Driver Foundation - User-mode Driver Framework Reflector"
DeviceName="punktfunk Virtual Display"
REG_MULTI_SZ=0x00010000
REG_SZ=0x00000000
REG_EXPAND_SZ=0x00020000
REG_DWORD=0x00010001
Binary file not shown.
+7 -7
View File
@@ -1,7 +1,7 @@
; punktfunk host installer (Inno Setup 6). ; punktfunk host installer (Inno Setup 6).
; ;
; Produces a signed setup.exe that lays the host into Program Files, optionally installs the bundled ; Produces a signed setup.exe that lays the host into Program Files, optionally installs the bundled
; SudoVDA virtual-display driver, and DELEGATES service registration to `punktfunk-host service ; pf-vdisplay virtual-display driver, and DELEGATES service registration to `punktfunk-host service
; install`. The real, idempotent install logic (SCM registration, firewall rules, default host.env, ; install`. The real, idempotent install logic (SCM registration, firewall rules, default host.env,
; the SYSTEM->interactive-session CreateProcessAsUserW supervisor for secure-desktop capture) lives in ; the SYSTEM->interactive-session CreateProcessAsUserW supervisor for secure-desktop capture) lives in
; crates/punktfunk-host/src/service.rs - this script does NOT duplicate it. That SYSTEM service model ; crates/punktfunk-host/src/service.rs - this script does NOT duplicate it. That SYSTEM service model
@@ -36,7 +36,7 @@
#ifndef WebSetup #ifndef WebSetup
#define WebSetup "..\..\scripts\windows\web-setup.ps1" #define WebSetup "..\..\scripts\windows\web-setup.ps1"
#endif #endif
; StageDir (the staged SudoVDA payload + nefconc.exe + install-sudovda.ps1) is optional. ; StageDir (the staged pf-vdisplay payload + nefconc.exe + install-pf-vdisplay.ps1) is optional.
#ifdef StageDir #ifdef StageDir
#define WithDriver #define WithDriver
#endif #endif
@@ -84,7 +84,7 @@ Name: "english"; MessagesFile: "compiler:Default.isl"
[Tasks] [Tasks]
#ifdef WithDriver #ifdef WithDriver
Name: "installdriver"; Description: "Install the SudoVDA virtual display driver (required for native-resolution streaming)" Name: "installdriver"; Description: "Install the pf-vdisplay virtual display driver (required for native-resolution streaming)"
#endif #endif
#ifdef WithGamepad #ifdef WithGamepad
Name: "installgamepad"; Description: "Install the virtual gamepad drivers (DualSense / DualShock 4 / Xbox 360 — no ViGEmBus needed)" Name: "installgamepad"; Description: "Install the virtual gamepad drivers (DualSense / DualShock 4 / Xbox 360 — no ViGEmBus needed)"
@@ -112,8 +112,8 @@ Source: "{#WebRunCmd}"; DestDir: "{app}\web"; DestName: "web-run.cmd"; Flags: ig
Source: "{#WebSetup}"; DestDir: "{tmp}"; DestName: "web-setup.ps1"; Flags: deleteafterinstall Source: "{#WebSetup}"; DestDir: "{tmp}"; DestName: "web-setup.ps1"; Flags: deleteafterinstall
#endif #endif
#ifdef WithDriver #ifdef WithDriver
; The driver payload + nefconc.exe + install-sudovda.ps1, extracted to {tmp} and removed after install. ; The driver payload + nefconc.exe + install-pf-vdisplay.ps1, extracted to {tmp} and removed after install.
Source: "{#StageDir}\*"; DestDir: "{tmp}\sudovda"; Flags: deleteafterinstall recursesubdirs createallsubdirs; Tasks: installdriver Source: "{#StageDir}\*"; DestDir: "{tmp}\pfvdisplay"; Flags: deleteafterinstall recursesubdirs createallsubdirs; Tasks: installdriver
#endif #endif
#ifdef WithGamepad #ifdef WithGamepad
; The vendored UMDF gamepad drivers + install-gamepad-drivers.ps1, extracted to {tmp}, removed after. ; The vendored UMDF gamepad drivers + install-gamepad-drivers.ps1, extracted to {tmp}, removed after.
@@ -123,8 +123,8 @@ Source: "{#GamepadStageDir}\*"; DestDir: "{tmp}\gamepad"; Flags: deleteafterinst
[Run] [Run]
#ifdef WithDriver #ifdef WithDriver
Filename: "powershell.exe"; \ Filename: "powershell.exe"; \
Parameters: "-NoProfile -ExecutionPolicy Bypass -File ""{tmp}\sudovda\install-sudovda.ps1"" -Dir ""{tmp}\sudovda"""; \ Parameters: "-NoProfile -ExecutionPolicy Bypass -File ""{tmp}\pfvdisplay\install-pf-vdisplay.ps1"" -Dir ""{tmp}\pfvdisplay"""; \
StatusMsg: "Installing the SudoVDA virtual display driver..."; \ StatusMsg: "Installing the pf-vdisplay virtual display driver..."; \
Flags: runhidden waituntilterminated; Tasks: installdriver Flags: runhidden waituntilterminated; Tasks: installdriver
#endif #endif
#ifdef WithGamepad #ifdef WithGamepad
@@ -1,25 +1,28 @@
<# <#
.SYNOPSIS .SYNOPSIS
Stage the driver bundle the installer ships into -OutDir: the VENDORED SudoVDA driver + the Stage the pf-vdisplay driver bundle the installer ships into -OutDir: the VENDORED signed pf-vdisplay
fetched nefcon device tool. driver + the fetched nefcon device tool.
.DESCRIPTION .DESCRIPTION
SudoVDA has no upstream release (its repo is a source-only VS solution; Apollo embeds the driver in pf-vdisplay (our all-Rust IddCx virtual display) is built from packaging/windows/vdisplay-driver/, and
its single installer), so the prebuilt, signed driver is VENDORED in this repo under the SIGNED output (pf_vdisplay.dll/.inf/.cat + punktfunk-driver.cer) is VENDORED under
packaging/windows/sudovda/ (MIT/CC0; SudoVDA v1.10.9.289, signer CN=sudovda@su.mk, Class=Display, packaging/windows/pf-vdisplay/ (signer punktfunk-ds-test shared with the gamepad drivers Class=
HWID Root\SudoMaker\SudoVDA). nefcon DOES publish a pinned release, so we fetch + SHA-256-verify it Display, HWID root\pf_vdisplay). Rebuild + re-vendor with
(it provides nefconc.exe, used to create the root-enumerated device node pnputil can't). packaging/windows/vdisplay-driver/deploy-dev.ps1 when the driver source changes, then copy the staged
pf_vdisplay.{dll,inf,cat} over the vendored copies. nefcon publishes a pinned release, so we fetch +
SHA-256-verify it (it provides nefconc.exe, used to create the root-enumerated device node pnputil
can't).
Output (consumed by punktfunk-host.iss): -OutDir gets SudoVDA.inf/.cat/.dll + sudovda.cer and Output (consumed by punktfunk-host.iss): -OutDir gets pf_vdisplay.inf/.cat/.dll + punktfunk-driver.cer
nefconc.exe (x64). pack-host-installer.ps1 also drops install-sudovda.ps1 in. and nefconc.exe (x64). pack-host-installer.ps1 also drops install-pf-vdisplay.ps1 in.
.EXAMPLE .EXAMPLE
pwsh -File stage-sudovda.ps1 -OutDir C:\t\out\stage pwsh -File stage-pf-vdisplay.ps1 -OutDir C:\t\out\stage
#> #>
[CmdletBinding()] [CmdletBinding()]
param( param(
[Parameter(Mandatory = $true)][string]$OutDir, [Parameter(Mandatory = $true)][string]$OutDir,
[string]$VendorDir = (Join-Path $PSScriptRoot 'sudovda'), [string]$VendorDir = (Join-Path $PSScriptRoot 'pf-vdisplay'),
# PINNED nefcon release (https://github.com/nefarius/nefcon/releases). MIT-licensed. # PINNED nefcon release (https://github.com/nefarius/nefcon/releases). MIT-licensed.
[string]$NefconUrl = 'https://github.com/nefarius/nefcon/releases/download/v1.17.40/nefcon_v1.17.40.zip', [string]$NefconUrl = 'https://github.com/nefarius/nefcon/releases/download/v1.17.40/nefcon_v1.17.40.zip',
[string]$NefconSha256 = '812bae7ed7dfb7d6d2284bc7de2f8ccebc92ed2a0b1ae893c53b337096e50c1a' [string]$NefconSha256 = '812bae7ed7dfb7d6d2284bc7de2f8ccebc92ed2a0b1ae893c53b337096e50c1a'
@@ -31,11 +34,11 @@ $PSNativeCommandUseErrorActionPreference = $false
if (Test-Path $OutDir) { Remove-Item -Recurse -Force $OutDir } if (Test-Path $OutDir) { Remove-Item -Recurse -Force $OutDir }
New-Item -ItemType Directory -Force -Path $OutDir | Out-Null New-Item -ItemType Directory -Force -Path $OutDir | Out-Null
# --- vendored SudoVDA driver ------------------------------------------------------------------ # --- vendored pf-vdisplay driver --------------------------------------------------------------
$inf = Get-ChildItem -Path $VendorDir -Filter *.inf -ErrorAction SilentlyContinue | Select-Object -First 1 $inf = Get-ChildItem -Path $VendorDir -Filter pf_vdisplay.inf -ErrorAction SilentlyContinue | Select-Object -First 1
if (-not $inf) { throw "no vendored SudoVDA .inf under $VendorDirsee packaging/windows/README.md" } if (-not $inf) { throw "no vendored pf_vdisplay.inf under $VendorDirre-vendor via vdisplay-driver/deploy-dev.ps1" }
Copy-Item (Join-Path $VendorDir '*') $OutDir -Force Copy-Item (Join-Path $VendorDir '*') $OutDir -Force
Write-Host "==> vendored SudoVDA staged from $VendorDir" Write-Host "==> vendored pf-vdisplay staged from $VendorDir"
# --- nefcon (fetched + verified) -------------------------------------------------------------- # --- nefcon (fetched + verified) --------------------------------------------------------------
$work = Join-Path ([IO.Path]::GetTempPath()) ('nefcon-' + [IO.Path]::GetRandomFileName()) $work = Join-Path ([IO.Path]::GetTempPath()) ('nefcon-' + [IO.Path]::GetRandomFileName())
@@ -51,7 +54,7 @@ try {
} }
Write-Host " sha256 ok ($got)" Write-Host " sha256 ok ($got)"
} }
else { Write-Warning "no pinned nefcon SHA-256 — computed $got (PIN THIS in stage-sudovda.ps1)" } else { Write-Warning "no pinned nefcon SHA-256 — computed $got (PIN THIS in stage-pf-vdisplay.ps1)" }
Expand-Archive -Path $zip -DestinationPath $work -Force Expand-Archive -Path $zip -DestinationPath $work -Force
$nefc = Get-ChildItem -Path $work -Recurse -Filter 'nefconc.exe' | $nefc = Get-ChildItem -Path $work -Recurse -Filter 'nefconc.exe' |
Where-Object { $_.FullName -match '(?i)\\x64\\' } | Select-Object -First 1 Where-Object { $_.FullName -match '(?i)\\x64\\' } | Select-Object -First 1
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.