Full project rename, decided 2026-06-10: - Crates/binaries: punktfunk-core / punktfunk-host / punktfunk-client-rs. - C ABI: punktfunk_* symbols, Punktfunk* types, include/punktfunk_core.h, PUNKTFUNK_FEATURE_QUIC guard (header regenerated; cbindgen renames updated, incl. PUNKTFUNK_BTN_*/PUNKTFUNK_AXIS_* wire constants). - Protocol: punktfunk/1 — control-plane magic LMN1 → PKF1, nonce salt lmn1 → pkf1. WIRE BREAK: clients must be rebuilt from this revision. - Env knobs: PUNKTFUNK_VIDEO_SOURCE / PUNKTFUNK_COMPOSITOR / PUNKTFUNK_ZEROCOPY / …. - Host config dir: ~/.config/punktfunk (the box's dir was migrated in place — the persistent identity is unchanged, pinned fingerprints stay valid). - Swift package: PunktfunkKit + PunktfunkCore.xcframework + PunktfunkConnection (Sources/PunktfunkClient app + tests renamed with it); build-xcframework.sh updated. - scripts/: 60-punktfunk.rules, punktfunk-host.service; OpenAPI doc regenerated. Also: scripts/headless/run-headless-kde.sh — full headless Plasma bringup. Root cause of "desktop but no apps/settings" over the stream: plasmashell launched without XDG_MENU_PREFIX=plasma-, so the launcher resolved a nonexistent applications.menu and rendered an empty menu. The script sets the complete KDE session env (menu prefix, KDE_FULL_SESSION, session version) and rebuilds ksycoca before starting plasmashell. Gate: 97/97 tests, clippy -D warnings (both feature sets), fmt, C-ABI harness PASS, zero lumen references left outside .git. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,52 @@
|
||||
//! Real UDP datagram transport — native sockets, no async runtime.
|
||||
//!
|
||||
//! M1 uses one `recv` syscall per packet; the latency budget (§7) calls for
|
||||
//! `sendmmsg`/UDP-GSO batching to cut syscalls, which is a P2 optimization layered on
|
||||
//! this same [`Transport`] seam.
|
||||
|
||||
use super::Transport;
|
||||
use crate::packet::MAX_DATAGRAM_BYTES;
|
||||
use std::net::UdpSocket;
|
||||
|
||||
/// Receive buffer size. `Config::validate` bounds `shard_payload` so a well-formed
|
||||
/// datagram (header + shard + crypto overhead) always fits in [`MAX_DATAGRAM_BYTES`];
|
||||
/// the `+ 1` byte lets us detect an oversized datagram (a full read) instead of
|
||||
/// silently truncating it.
|
||||
const RECV_BUF: usize = MAX_DATAGRAM_BYTES + 1;
|
||||
|
||||
pub struct UdpTransport {
|
||||
socket: UdpSocket,
|
||||
}
|
||||
|
||||
impl UdpTransport {
|
||||
/// Bind `local` and `connect` to `peer`, so `send`/`recv` need no address and the
|
||||
/// kernel filters to this peer. Non-blocking, matching the [`Transport`] contract.
|
||||
pub fn connect(local: &str, peer: &str) -> std::io::Result<Self> {
|
||||
let socket = UdpSocket::bind(local)?;
|
||||
socket.connect(peer)?;
|
||||
socket.set_nonblocking(true)?;
|
||||
Ok(UdpTransport { socket })
|
||||
}
|
||||
}
|
||||
|
||||
impl Transport for UdpTransport {
|
||||
fn send(&self, packet: &[u8]) -> std::io::Result<()> {
|
||||
self.socket.send(packet)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn recv(&self) -> std::io::Result<Option<Vec<u8>>> {
|
||||
let mut buf = vec![0u8; RECV_BUF];
|
||||
match self.socket.recv(&mut buf) {
|
||||
// A read that fills the whole buffer means the datagram was larger than any
|
||||
// valid packet — drop it rather than hand a truncated, corrupt packet up.
|
||||
Ok(n) if n >= RECV_BUF => Ok(None),
|
||||
Ok(n) => {
|
||||
buf.truncate(n);
|
||||
Ok(Some(buf))
|
||||
}
|
||||
Err(e) if e.kind() == std::io::ErrorKind::WouldBlock => Ok(None),
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user