diff --git a/Cargo.lock b/Cargo.lock index 605793d9..f3b3be26 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2740,6 +2740,25 @@ version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" +[[package]] +name = "pf-client-core" +version = "0.8.2" +dependencies = [ + "anyhow", + "async-channel", + "ffmpeg-next", + "mdns-sd", + "opus", + "pipewire", + "punktfunk-core", + "rustls", + "sdl3", + "serde", + "serde_json", + "tracing", + "ureq", +] + [[package]] name = "pf-driver-proto" version = "0.0.1" @@ -2927,21 +2946,14 @@ version = "0.8.2" dependencies = [ "anyhow", "async-channel", - "ffmpeg-next", "gtk4", "khronos-egl", "libadwaita", - "mdns-sd", - "opus", - "pipewire", + "pf-client-core", "punktfunk-core", - "rustls", - "sdl3", - "serde", "serde_json", "tracing", "tracing-subscriber", - "ureq", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 42a1f8e4..1971c5e4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,6 +5,7 @@ members = [ "crates/punktfunk-host", "crates/punktfunk-host/vendor/usbip-sim", "crates/punktfunk-tray", + "crates/pf-client-core", "crates/pf-driver-proto", "clients/probe", "clients/linux", diff --git a/clients/android/native/Cargo.toml b/clients/android/native/Cargo.toml index 24ada0dd..0b0a4db5 100644 --- a/clients/android/native/Cargo.toml +++ b/clients/android/native/Cargo.toml @@ -20,7 +20,7 @@ punktfunk-core = { path = "../../../crates/punktfunk-core", features = ["quic"] jni = "0.21" log = "0.4" # LAN host discovery: browse the host's `_punktfunk._udp` mDNS advert — the SAME crate + service the -# Linux/Windows clients use (`clients/linux/src/discovery.rs`), replacing Android's per-OEM +# Linux/Windows clients use (`crates/pf-client-core/src/discovery.rs`), replacing Android's per-OEM # `NsdManager` system daemon with one tested browse path. Pure Rust (socket2/if-addrs/mio), so it # cross-compiles to the Android targets AND builds on the host (the JNI seam links into # `cargo build --workspace`). Kotlin keeps only the Wi-Fi `MulticastLock` + permission UX. diff --git a/clients/android/native/src/discovery.rs b/clients/android/native/src/discovery.rs index 1eec536d..c046a6bd 100644 --- a/clients/android/native/src/discovery.rs +++ b/clients/android/native/src/discovery.rs @@ -1,5 +1,5 @@ //! LAN host discovery over mDNS, in Rust via `mdns-sd` — the same crate + service type the -//! Linux/Windows clients use (`clients/linux/src/discovery.rs`), exposed to Kotlin over JNI. +//! Linux/Windows clients use (`crates/pf-client-core/src/discovery.rs`), exposed to Kotlin over JNI. //! //! Why not `NsdManager`: that API delegates to a per-OEM system mDNS daemon whose reliability //! varies wildly (the Android client's discovery was "mostly broken"). Browsing in our own Rust diff --git a/clients/linux/Cargo.toml b/clients/linux/Cargo.toml index 86898145..a4a72b3a 100644 --- a/clients/linux/Cargo.toml +++ b/clients/linux/Cargo.toml @@ -15,6 +15,10 @@ path = "src/main.rs" # Everything is Linux-gated so `cargo build --workspace` stays green on macOS (the Mac # client lives in clients/apple); on other platforms this builds as a stub binary. [target.'cfg(target_os = "linux")'.dependencies] +# Session pump, decode, audio, gamepads, trust, discovery, keymap — the UI-agnostic +# plumbing, shared with the upcoming Vulkan session binary (design: punktfunk-planning +# linux-client-rearchitecture.md). This crate keeps only the GTK shell + GL presenter. +pf-client-core = { path = "../../crates/pf-client-core" } punktfunk-core = { path = "../../crates/punktfunk-core", features = ["quic"] } # UI shell. GraphicsOffload needs GTK ≥ 4.14; black-background ≥ 4.16. AlertDialog/ @@ -23,25 +27,10 @@ gtk = { package = "gtk4", version = "0.11", features = ["v4_16"] } adw = { package = "libadwaita", version = "0.9", features = ["v1_5"] } async-channel = "2" -# Video decode (same FFmpeg pin as the host) and audio. -ffmpeg-next = "8" -opus = "0.3" -pipewire = "0.9" - -# Gamepads: capture + feedback (full DualSense fidelity — touchpad/motion/triggers/LEDs -# need the hidapi driver). -sdl3 = { version = "0.18", features = ["hidapi"] } # The VAAPI GL presenter (video_gl.rs): EGL dmabuf import into a GDK-shared context, dlopened # at runtime (`dynamic`) so GPU-less boxes and the software path never touch libEGL. khronos-egl = { version = "6", features = ["dynamic"] } -mdns-sd = "0.20" -# Game-library fetch from the host's management API over mTLS + fingerprint pinning. -# `ureq` is small + sync (the host uses it too) and its rustls unifies with the -# workspace's (quinn's) 0.23; the pinning verifier mirrors core's private `PinVerify`. -ureq = "2" -rustls = { version = "0.23", features = ["ring"] } -serde = { version = "1", features = ["derive"] } serde_json = "1" anyhow = "1" tracing = "0.1" diff --git a/clients/linux/README.md b/clients/linux/README.md index 24368c5a..8d98ac9d 100644 --- a/clients/linux/README.md +++ b/clients/linux/README.md @@ -78,15 +78,15 @@ src/ ui_trust.rs TOFU / PIN-pairing / request-access dialogs ui_settings.rs resolution · refresh · decoder · bitrate · compositor · mic ui_stream.rs the stream window (GtkGraphicsOffload present) + input capture - launch.rs · session.rs session launch/UI glue; lifecycle over the NativeClient connector - video.rs FFmpeg VAAPI / software decode → dmabuf / texture - audio.rs PipeWire playback + mic uplink - gamepad.rs · keymap.rs SDL3 controllers + feedback; keyboard VK mapping - trust.rs · discovery.rs persistent identity, known hosts + settings, mDNS browse - library.rs mgmt-API library client (mTLS + pinned fingerprint, art proxy) + launch.rs session launch/UI glue over the shared session pump + video_gl.rs VAAPI dmabuf → RGBA GL presenter (EGL import, CICP-driven CSC) tools/screenshots.sh store screenshot capture (app self-capture; Xvfb fallback) ``` +The UI-agnostic plumbing — session pump, FFmpeg decode, PipeWire audio, SDL3 gamepads + +keymap, trust store, mDNS discovery, library client, Wake-on-LAN — lives in +`crates/pf-client-core`, shared with the Vulkan session binary. + ## Related - **[Documentation](https://docs.punktfunk.unom.io)** — quick start, pairing, troubleshooting diff --git a/clients/linux/src/main.rs b/clients/linux/src/main.rs index 4f4fd021..aa6adf38 100644 --- a/clients/linux/src/main.rs +++ b/clients/linux/src/main.rs @@ -5,27 +5,19 @@ //! mirrors the Apple client: persistent identity, TOFU prompt with the host fingerprint, //! SPAKE2 PIN pairing. +// The UI-agnostic plumbing lives in `pf-client-core`, shared with the upcoming Vulkan +// session binary (design: punktfunk-planning linux-client-rearchitecture.md, Phase 0). +// Root re-exports keep every existing `crate::video`-style path resolving unchanged. +#[cfg(target_os = "linux")] +pub use pf_client_core::{audio, discovery, gamepad, keymap, library, session, trust, video, wol}; + #[cfg(target_os = "linux")] mod app; #[cfg(target_os = "linux")] -mod audio; -#[cfg(target_os = "linux")] mod cli; #[cfg(target_os = "linux")] -mod discovery; -#[cfg(target_os = "linux")] -mod gamepad; -#[cfg(target_os = "linux")] -mod keymap; -#[cfg(target_os = "linux")] mod launch; #[cfg(target_os = "linux")] -mod library; -#[cfg(target_os = "linux")] -mod session; -#[cfg(target_os = "linux")] -mod trust; -#[cfg(target_os = "linux")] mod ui_gamepad_library; #[cfg(target_os = "linux")] mod ui_hosts; @@ -38,12 +30,8 @@ mod ui_stream; #[cfg(target_os = "linux")] mod ui_trust; #[cfg(target_os = "linux")] -mod video; -#[cfg(target_os = "linux")] mod video_gl; -mod wol; - #[cfg(target_os = "linux")] fn main() -> gtk::glib::ExitCode { app::run() diff --git a/crates/pf-client-core/Cargo.toml b/crates/pf-client-core/Cargo.toml new file mode 100644 index 00000000..d9044e2a --- /dev/null +++ b/crates/pf-client-core/Cargo.toml @@ -0,0 +1,36 @@ +[package] +name = "pf-client-core" +description = "Shared Linux-client plumbing — session pump, FFmpeg decode, PipeWire audio, SDL3 gamepads, trust store, discovery — extracted from the GTK client so the shell and the Vulkan session binary build on one implementation" +version.workspace = true +edition.workspace = true +rust-version.workspace = true +license.workspace = true +authors.workspace = true +repository.workspace = true + +# Same Linux gating as clients/linux: `cargo build --workspace` stays green on macOS +# (the Mac client lives in clients/apple); elsewhere this crate is `wol` plus stubs-free +# emptiness. `wol` is pure std and stays cross-platform, matching the old main.rs. +[target.'cfg(target_os = "linux")'.dependencies] +punktfunk-core = { path = "../punktfunk-core", features = ["quic"] } +async-channel = "2" + +# Video decode (same FFmpeg pin as the host) and audio. +ffmpeg-next = "8" +opus = "0.3" +pipewire = "0.9" + +# Gamepads: capture + feedback (full DualSense fidelity — touchpad/motion/triggers/LEDs +# need the hidapi driver). +sdl3 = { version = "0.18", features = ["hidapi"] } + +mdns-sd = "0.20" +# Game-library fetch from the host's management API over mTLS + fingerprint pinning. +# `ureq` is small + sync (the host uses it too) and its rustls unifies with the +# workspace's (quinn's) 0.23; the pinning verifier mirrors core's private `PinVerify`. +ureq = "2" +rustls = { version = "0.23", features = ["ring"] } +serde = { version = "1", features = ["derive"] } +serde_json = "1" +anyhow = "1" +tracing = "0.1" diff --git a/clients/linux/src/audio.rs b/crates/pf-client-core/src/audio.rs similarity index 100% rename from clients/linux/src/audio.rs rename to crates/pf-client-core/src/audio.rs diff --git a/clients/linux/src/discovery.rs b/crates/pf-client-core/src/discovery.rs similarity index 100% rename from clients/linux/src/discovery.rs rename to crates/pf-client-core/src/discovery.rs diff --git a/clients/linux/src/gamepad.rs b/crates/pf-client-core/src/gamepad.rs similarity index 100% rename from clients/linux/src/gamepad.rs rename to crates/pf-client-core/src/gamepad.rs diff --git a/clients/linux/src/keymap.rs b/crates/pf-client-core/src/keymap.rs similarity index 100% rename from clients/linux/src/keymap.rs rename to crates/pf-client-core/src/keymap.rs diff --git a/crates/pf-client-core/src/lib.rs b/crates/pf-client-core/src/lib.rs new file mode 100644 index 00000000..9b399bbc --- /dev/null +++ b/crates/pf-client-core/src/lib.rs @@ -0,0 +1,26 @@ +//! Shared, UI-agnostic Linux-client plumbing, extracted verbatim from the GTK client +//! (design: punktfunk-planning `linux-client-rearchitecture.md`, Phase 0) so the desktop +//! shell and the Vulkan session binary build on one implementation. +//! +//! Nothing here may depend on a UI toolkit: the presenter contract is `session`'s +//! channels (`SessionHandle`) and `video`'s `DecodedImage` (RGBA bytes or dmabuf fds + +//! plane layout) — how frames reach the screen is the consumer's business. + +#[cfg(target_os = "linux")] +pub mod audio; +#[cfg(target_os = "linux")] +pub mod discovery; +#[cfg(target_os = "linux")] +pub mod gamepad; +#[cfg(target_os = "linux")] +pub mod keymap; +#[cfg(target_os = "linux")] +pub mod library; +#[cfg(target_os = "linux")] +pub mod session; +#[cfg(target_os = "linux")] +pub mod trust; +#[cfg(target_os = "linux")] +pub mod video; + +pub mod wol; diff --git a/clients/linux/src/library.rs b/crates/pf-client-core/src/library.rs similarity index 100% rename from clients/linux/src/library.rs rename to crates/pf-client-core/src/library.rs diff --git a/clients/linux/src/session.rs b/crates/pf-client-core/src/session.rs similarity index 100% rename from clients/linux/src/session.rs rename to crates/pf-client-core/src/session.rs diff --git a/clients/linux/src/trust.rs b/crates/pf-client-core/src/trust.rs similarity index 100% rename from clients/linux/src/trust.rs rename to crates/pf-client-core/src/trust.rs diff --git a/clients/linux/src/video.rs b/crates/pf-client-core/src/video.rs similarity index 100% rename from clients/linux/src/video.rs rename to crates/pf-client-core/src/video.rs diff --git a/clients/linux/src/wol.rs b/crates/pf-client-core/src/wol.rs similarity index 100% rename from clients/linux/src/wol.rs rename to crates/pf-client-core/src/wol.rs diff --git a/clients/linux/tests/pq-frame.h265 b/crates/pf-client-core/tests/pq-frame.h265 similarity index 100% rename from clients/linux/tests/pq-frame.h265 rename to crates/pf-client-core/tests/pq-frame.h265 diff --git a/crates/punktfunk-tray/Cargo.toml b/crates/punktfunk-tray/Cargo.toml index 34d3ace2..758b903c 100644 --- a/crates/punktfunk-tray/Cargo.toml +++ b/crates/punktfunk-tray/Cargo.toml @@ -23,7 +23,7 @@ anyhow = "1" serde = { version = "1", features = ["derive"] } serde_json = "1" # Loopback HTTPS poll of GET /api/v1/local/summary. Same sync ureq + rustls(ring) stack and -# custom-verifier pattern as the Linux client's library fetch (clients/linux/src/library.rs) — +# custom-verifier pattern as the Linux client's library fetch (crates/pf-client-core/src/library.rs) — # but ring-only (no default aws-lc-rs provider: it needs a C toolchain per target and the agent # pins the ring provider explicitly anyway). ureq = { version = "2", default-features = false, features = ["tls"] }