3 Commits

Author SHA1 Message Date
enricobuehler 5b5ec15ead fix(client-linux): GL presenter — eglCreateImageKHR takes EGLint attribs, not EGLAttrib
docker / deploy-docs (push) Blocked by required conditions
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Waiting to run
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Waiting to run
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Waiting to run
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Waiting to run
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Waiting to run
flatpak / build-publish (push) Waiting to run
rpm / build-publish (fedora-44, punktfunk-fedora44-rpm) (push) Waiting to run
decky / build-publish (push) Waiting to run
rpm / build-publish (bazzite, punktfunk-fedora-rpm) (push) Waiting to run
apple / swift (push) Successful in 1m12s
apple / screenshots (push) Successful in 5m47s
android / android (push) Successful in 3m18s
ci / rust (push) Successful in 1m35s
ci / web (push) Successful in 55s
ci / bench (push) Has started running
ci / docs-site (push) Successful in 1m35s
deb / build-publish (push) Has started running
The KHR variant reads 32-bit attrib pairs; the pointer-sized array fed it
garbage and every plane import came back rejected (observed on-Deck; the
new fallback ladder caught it and demoted to software exactly as designed).
Also print the real EGL error enum instead of its discriminant.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-04 14:32:06 +00:00
enricobuehler c9ff144492 Merge branch 'main' of git.unom.io:unom/punktfunk
ci / rust (push) Successful in 1m49s
apple / swift (push) Successful in 1m6s
ci / web (push) Successful in 51s
ci / docs-site (push) Successful in 1m6s
android / android (push) Successful in 3m51s
windows-host / package (push) Successful in 6m56s
windows-msix / package (arm64, C:\Users\Public\ffmpeg-arm64, aarch64-pc-windows-msvc, C:\t-a64) (push) Successful in 1m22s
deb / build-publish (push) Successful in 4m40s
ci / bench (push) Successful in 4m48s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 5s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 5s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 4s
decky / build-publish (push) Successful in 24s
release / apple (push) Successful in 7m51s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 4s
windows-msix / package (x64, C:\Users\Public\ffmpeg, x86_64-pc-windows-msvc, C:\t) (push) Successful in 1m19s
windows / build (aarch64-pc-windows-msvc) (push) Successful in 50s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 2m17s
docker / deploy-docs (push) Waiting to run
windows / build (x86_64-pc-windows-msvc) (push) Successful in 58s
flatpak / build-publish (push) Successful in 4m12s
apple / screenshots (push) Successful in 5m39s
rpm / build-publish (bazzite, punktfunk-fedora-rpm) (push) Successful in 9m50s
rpm / build-publish (fedora-44, punktfunk-fedora44-rpm) (push) Successful in 9m41s
2026-07-04 14:29:40 +00:00
enricobuehler 7930d2f0f4 fix(core): split WIRE_VERSION from ABI_VERSION — new clients locked out of every deployed host
ABI_VERSION was doing double duty: the embeddable C surface AND the punktfunk/1
Hello/Welcome version that hosts equality-check. The WoL feature's v3 bump added
a client-local FFI function without changing a single wire byte — and every new
client started refusing against every deployed host ("ABI mismatch: client 3
host 2", observed live Deck → Bazzite). The wire now carries its own
WIRE_VERSION (still 2); ABI_VERSION stays 3 for the C header and the mgmt API's
informational field. Bump WIRE_VERSION only when the handshake/planes actually
change incompatibly.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-04 14:29:33 +00:00
6 changed files with 47 additions and 29 deletions
+22 -20
View File
@@ -30,15 +30,17 @@ use std::sync::{Arc, Mutex};
// --- EGL_EXT_image_dma_buf_import(+_modifiers) constants (khronos-egl exposes none) ------
const EGL_LINUX_DMA_BUF_EXT: egl::Enum = 0x3270;
const EGL_LINUX_DRM_FOURCC_EXT: usize = 0x3271;
const EGL_DMA_BUF_PLANE0_FD_EXT: usize = 0x3272;
const EGL_DMA_BUF_PLANE0_OFFSET_EXT: usize = 0x3273;
const EGL_DMA_BUF_PLANE0_PITCH_EXT: usize = 0x3274;
const EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT: usize = 0x3443;
const EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT: usize = 0x3444;
const EGL_WIDTH: usize = 0x3057;
const EGL_HEIGHT: usize = 0x3056;
const EGL_NONE: usize = 0x3038;
// eglCreateImageKHR takes 32-bit EGLint attribs (the core-1.5 eglCreateImage variant is the
// one with pointer-sized EGLAttrib) — using the wrong width feeds the driver garbage pairs.
const EGL_LINUX_DRM_FOURCC_EXT: i32 = 0x3271;
const EGL_DMA_BUF_PLANE0_FD_EXT: i32 = 0x3272;
const EGL_DMA_BUF_PLANE0_OFFSET_EXT: i32 = 0x3273;
const EGL_DMA_BUF_PLANE0_PITCH_EXT: i32 = 0x3274;
const EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT: i32 = 0x3443;
const EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT: i32 = 0x3444;
const EGL_WIDTH: i32 = 0x3057;
const EGL_HEIGHT: i32 = 0x3056;
const EGL_NONE: i32 = 0x3038;
const DRM_FORMAT_MOD_INVALID: u64 = 0x00ff_ffff_ffff_ffff;
/// `fourcc('N','V','1','2')` — the only decoder output today (8-bit 4:2:0). P010 joins when
@@ -140,7 +142,7 @@ type EglCreateImageKhr = unsafe extern "C" fn(
*mut c_void, // EGLContext (EGL_NO_CONTEXT for dmabuf)
egl::Enum,
*mut c_void, // EGLClientBuffer (null for dmabuf)
*const usize,
*const i32, // EGLint attrib list (KHR variant — NOT pointer-sized EGLAttrib)
) -> *const c_void;
type EglDestroyImageKhr = unsafe extern "C" fn(*mut c_void, *const c_void) -> egl::Boolean;
@@ -464,24 +466,24 @@ impl GlConverter {
) -> Result<*const c_void> {
let mut attribs = vec![
EGL_WIDTH,
width as usize,
width as i32,
EGL_HEIGHT,
height as usize,
height as i32,
EGL_LINUX_DRM_FOURCC_EXT,
fourcc as usize,
fourcc as i32,
EGL_DMA_BUF_PLANE0_FD_EXT,
plane.fd as usize,
plane.fd,
EGL_DMA_BUF_PLANE0_OFFSET_EXT,
plane.offset as usize,
plane.offset as i32,
EGL_DMA_BUF_PLANE0_PITCH_EXT,
plane.stride as usize,
plane.stride as i32,
];
if modifier != DRM_FORMAT_MOD_INVALID && modifier != 0 {
attribs.extend_from_slice(&[
EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT,
(modifier & 0xffff_ffff) as usize,
(modifier & 0xffff_ffff) as u32 as i32,
EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT,
(modifier >> 32) as usize,
(modifier >> 32) as u32 as i32,
]);
}
attribs.push(EGL_NONE);
@@ -497,12 +499,12 @@ impl GlConverter {
};
if img.is_null() {
bail!(
"eglCreateImageKHR rejected plane ({}x{} {:#x} mod {:#018x}): {:#x}",
"eglCreateImageKHR rejected plane ({}x{} {:#x} mod {:#018x}): {:?}",
width,
height,
fourcc,
modifier,
self.egl.get_error().map(|e| e as u32).unwrap_or(0)
self.egl.get_error()
);
}
Ok(img)
+1 -1
View File
@@ -412,7 +412,7 @@ async fn session(args: Args) -> Result<()> {
io::write_msg(
&mut send,
&Hello {
abi_version: punktfunk_core::ABI_VERSION,
abi_version: punktfunk_core::WIRE_VERSION,
mode: args.mode,
compositor: args.compositor,
gamepad: args.gamepad,
+1 -1
View File
@@ -876,7 +876,7 @@ async fn worker_main(args: WorkerArgs) {
io::write_msg(
&mut send,
&Hello {
abi_version: crate::ABI_VERSION,
abi_version: crate::WIRE_VERSION,
mode,
compositor,
gamepad,
+8
View File
@@ -54,3 +54,11 @@ pub use stats::Stats;
/// v3: added `punktfunk_wake_on_lan` (Wake-on-LAN magic packet; the host's wake MAC(s) reach
/// clients out-of-band via the mDNS `mac` TXT record, so no connection is required to wake).
pub const ABI_VERSION: u32 = 3;
/// The punktfunk/1 **wire** version — what `Hello`/`Welcome` carry and hosts equality-check.
/// Deliberately its own constant: [`ABI_VERSION`] tracks the embeddable **C surface**
/// (functions a client links), which can grow without changing a single wire byte — v3's
/// `punktfunk_wake_on_lan` is client-local, and riding the C-ABI bump onto the wire locked
/// every new client out of every deployed host ("ABI mismatch: client 3 host 2", observed
/// live). Bump this ONLY when the handshake/planes actually change incompatibly.
pub const WIRE_VERSION: u32 = 2;
+7 -7
View File
@@ -585,10 +585,10 @@ async fn serve_session(
// the `handshake` future re-decodes for the real session — a few dozen bytes, negligible.
let gate_hello = Hello::decode(&first).map_err(|e| anyhow!("Hello decode: {e:?}"))?;
anyhow::ensure!(
gate_hello.abi_version == punktfunk_core::ABI_VERSION,
"ABI mismatch: client {} host {}",
gate_hello.abi_version == punktfunk_core::WIRE_VERSION,
"wire version mismatch: client {} host {}",
gate_hello.abi_version,
punktfunk_core::ABI_VERSION
punktfunk_core::WIRE_VERSION
);
let fp = endpoint::peer_fingerprint(&conn);
let known = fp
@@ -654,10 +654,10 @@ async fn serve_session(
let handshake = async {
let hello = Hello::decode(&first).map_err(|e| anyhow!("Hello decode: {e:?}"))?;
anyhow::ensure!(
hello.abi_version == punktfunk_core::ABI_VERSION,
"ABI mismatch: client {} host {}",
hello.abi_version == punktfunk_core::WIRE_VERSION,
"wire version mismatch: client {} host {}",
hello.abi_version,
punktfunk_core::ABI_VERSION
punktfunk_core::WIRE_VERSION
);
// The pairing gate (require_pairing → paired? else park for delegated approval) ran above,
// before this future, so a client reaching here is paired (or the host is `--open`).
@@ -805,7 +805,7 @@ async fn serve_session(
let mut key = [0u8; 16];
rand::thread_rng().fill_bytes(&mut key);
let welcome = Welcome {
abi_version: punktfunk_core::ABI_VERSION,
abi_version: punktfunk_core::WIRE_VERSION,
udp_port,
mode: hello.mode,
// The post-GameStream point of punktfunk/1: Leopard GF(2¹⁶) FEC + real encryption.
+8
View File
@@ -21,6 +21,14 @@
// clients out-of-band via the mDNS `mac` TXT record, so no connection is required to wake).
#define ABI_VERSION 3
// The punktfunk/1 **wire** version — what `Hello`/`Welcome` carry and hosts equality-check.
// Deliberately its own constant: [`ABI_VERSION`] tracks the embeddable **C surface**
// (functions a client links), which can grow without changing a single wire byte — v3's
// `punktfunk_wake_on_lan` is client-local, and riding the C-ABI bump onto the wire locked
// every new client out of every deployed host ("ABI mismatch: client 3 host 2", observed
// live). Bump this ONLY when the handshake/planes actually change incompatibly.
#define WIRE_VERSION 2
// `PunktfunkHidOutput::kind` — lightbar RGB (`r`/`g`/`b` valid).
#define PUNKTFUNK_HIDOUT_LED 1