Files
punktfunk/include/punktfunk_core.h
T
enricobuehler bfd64ce871
ci / rust (push) Has been cancelled
rename: lumen → punktfunk, everywhere
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>
2026-06-10 13:11:59 +00:00

417 lines
15 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* punktfunk-core C ABI — see crates/punktfunk-core/src/abi.rs */
#ifndef PUNKTFUNK_CORE_H
#define PUNKTFUNK_CORE_H
#pragma once
/* Generated by cbindgen from punktfunk-core. Do not edit by hand. */
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
// Bump on any breaking change to the [C ABI](crate::abi). Mirrors
// `punktfunk_abi_version()` and is checked by clients before use.
#define ABI_VERSION 1
// 16-byte AEAD authentication tag appended by GCM.
#define TAG_LEN 16
// Wire tag distinguishing an input datagram from a video packet.
#define INPUT_MAGIC 200
// Fixed serialized size of an [`InputEvent`] on the wire (tag + fields).
#define INPUT_WIRE_LEN (((((1 + 1) + 4) + 4) + 4) + 4)
#define PUNKTFUNK_BTN_DPAD_UP 1
#define PUNKTFUNK_BTN_DPAD_DOWN 2
#define PUNKTFUNK_BTN_DPAD_LEFT 4
#define PUNKTFUNK_BTN_DPAD_RIGHT 8
#define PUNKTFUNK_BTN_START 16
#define PUNKTFUNK_BTN_BACK 32
#define PUNKTFUNK_BTN_LS_CLICK 64
#define PUNKTFUNK_BTN_RS_CLICK 128
#define PUNKTFUNK_BTN_LB 256
#define PUNKTFUNK_BTN_RB 512
#define PUNKTFUNK_BTN_GUIDE 1024
#define PUNKTFUNK_BTN_A 4096
#define PUNKTFUNK_BTN_B 8192
#define PUNKTFUNK_BTN_X 16384
#define PUNKTFUNK_BTN_Y 32768
// Axis ids for `InputKind::GamepadAxis`.
#define PUNKTFUNK_AXIS_LS_X 0
#define PUNKTFUNK_AXIS_LS_Y 1
#define PUNKTFUNK_AXIS_RS_X 2
#define PUNKTFUNK_AXIS_RS_Y 3
// Triggers: value range 0..255.
#define PUNKTFUNK_AXIS_LT 4
#define PUNKTFUNK_AXIS_RT 5
// Identifies a punktfunk video packet (vs. an input datagram, see [`crate::input`]).
#define PUNKTFUNK_MAGIC 201
#define FLAG_PIC 1
#define FLAG_EOF 2
#define FLAG_SOF 4
// Largest UDP datagram the core will send or accept. `Config::validate` bounds
// `shard_payload` so `HEADER_LEN + shard_payload + CRYPTO_OVERHEAD ≤ MAX_DATAGRAM_BYTES`.
#define MAX_DATAGRAM_BYTES 2048
#if defined(PUNKTFUNK_FEATURE_QUIC)
// Datagram wire tags. Video rides UDP; everything low-rate rides QUIC datagrams,
// demultiplexed by the first byte: input = [`crate::input::INPUT_MAGIC`] (0xC8),
// audio = [`AUDIO_MAGIC`], rumble = [`RUMBLE_MAGIC`].
#define PUNKTFUNK_AUDIO_MAGIC 201
#endif
#if defined(PUNKTFUNK_FEATURE_QUIC)
#define PUNKTFUNK_RUMBLE_MAGIC 202
#endif
// Stable C ABI status codes. `Ok` is 0; all errors are negative so callers can
// test `rc < 0`. Do not renumber existing variants — only append.
enum PunktfunkStatus
#if defined(__cplusplus) || __STDC_VERSION__ >= 202311L
: int32_t
#endif // defined(__cplusplus) || __STDC_VERSION__ >= 202311L
{
PUNKTFUNK_STATUS_OK = 0,
PUNKTFUNK_STATUS_INVALID_ARG = -1,
PUNKTFUNK_STATUS_FEC = -2,
PUNKTFUNK_STATUS_CRYPTO = -3,
PUNKTFUNK_STATUS_BAD_PACKET = -4,
PUNKTFUNK_STATUS_NO_FRAME = -5,
PUNKTFUNK_STATUS_UNSUPPORTED = -6,
PUNKTFUNK_STATUS_IO = -7,
PUNKTFUNK_STATUS_NULL_POINTER = -8,
PUNKTFUNK_STATUS_TIMEOUT = -9,
PUNKTFUNK_STATUS_CLOSED = -10,
PUNKTFUNK_STATUS_PANIC = -99,
};
#ifndef __cplusplus
#if __STDC_VERSION__ >= 202311L
typedef enum PunktfunkStatus PunktfunkStatus;
#else
typedef int32_t PunktfunkStatus;
#endif // __STDC_VERSION__ >= 202311L
#endif // __cplusplus
// Kinds of input event. `#[repr(u8)]` so it crosses the C ABI as a byte tag.
enum PunktfunkInputKind
#if defined(__cplusplus) || __STDC_VERSION__ >= 202311L
: uint8_t
#endif // defined(__cplusplus) || __STDC_VERSION__ >= 202311L
{
PUNKTFUNK_INPUT_KIND_KEY_DOWN = 0,
PUNKTFUNK_INPUT_KIND_KEY_UP = 1,
// Relative motion: `x`/`y` carry `dx`/`dy`.
PUNKTFUNK_INPUT_KIND_MOUSE_MOVE = 2,
// Absolute motion: `x`/`y` carry pixel coordinates.
PUNKTFUNK_INPUT_KIND_MOUSE_MOVE_ABS = 3,
PUNKTFUNK_INPUT_KIND_MOUSE_BUTTON_DOWN = 4,
PUNKTFUNK_INPUT_KIND_MOUSE_BUTTON_UP = 5,
// `x` carries the (signed) scroll delta.
PUNKTFUNK_INPUT_KIND_MOUSE_SCROLL = 6,
// `code` = button bit ([`gamepad`] `BTN_*`), `x` ≠ 0 = pressed, `flags` = pad index.
PUNKTFUNK_INPUT_KIND_GAMEPAD_BUTTON = 7,
// `code` = axis id ([`gamepad`] `AXIS_*`), `x` = axis value, `flags` = pad index.
// Sticks are i16 range (32768..32767) in the XInput/Moonlight convention — **+y =
// up** (unlike mouse coordinates); triggers 0..255.
PUNKTFUNK_INPUT_KIND_GAMEPAD_AXIS = 8,
};
#ifndef __cplusplus
#if __STDC_VERSION__ >= 202311L
typedef enum PunktfunkInputKind PunktfunkInputKind;
#else
typedef uint8_t PunktfunkInputKind;
#endif // __STDC_VERSION__ >= 202311L
#endif // __cplusplus
#if defined(PUNKTFUNK_FEATURE_QUIC)
// Opaque handle to a live `punktfunk/1` connection (QUIC control plane + UDP data plane, all
// pumped on internal threads).
//
// Thread contract: each plane (video `next_au`, audio `next_audio`, rumble `next_rumble`)
// may be pulled from its own thread, at most one thread per plane. The accessors only
// take shared references internally (per-plane mutexed borrow slots), so cross-plane
// concurrency is sound — never two threads on the *same* plane.
typedef struct PunktfunkConnection PunktfunkConnection;
#endif
// Opaque session handle. Pointer-only from C.
typedef struct PunktfunkSession PunktfunkSession;
// Forward-compatible session configuration. The caller MUST set `struct_size` to
// `sizeof(PunktfunkConfig)`; the core uses it to detect ABI skew.
typedef struct {
uint32_t struct_size;
// 0 = host, 1 = client.
uint32_t role;
// 1 = P1 (GameStream-compatible), 2 = P2 (`punktfunk/1`).
uint32_t phase;
// 0 = GF(2⁸), 1 = GF(2¹⁶).
uint32_t fec_scheme;
uint32_t fec_percent;
uint32_t max_data_per_block;
uint32_t shard_payload;
// Non-zero enables AES-128-GCM.
uint32_t encrypt;
uint8_t key[16];
uint8_t salt[4];
// Test hook for the loopback transport; 0 in production.
uint32_t loopback_drop_period;
// Largest encoded access unit the receiver will accept (bounds reassembler memory).
uint64_t max_frame_bytes;
} PunktfunkConfig;
// A reassembled access unit. `data`/`len` borrow session-owned memory valid until the
// next `punktfunk_client_poll_frame`/`punktfunk_session_free` on the same session.
typedef struct {
const uint8_t *data;
uintptr_t len;
uint32_t frame_index;
uint64_t pts_ns;
uint32_t flags;
} PunktfunkFrame;
// A single input event. `#[repr(C)]` — shared verbatim with the C ABI as
// `PunktfunkInputEvent`.
typedef struct {
PunktfunkInputKind kind;
uint8_t _pad[3];
// keycode / button id / axis id, depending on `kind`.
uint32_t code;
// x / dx / abs-x / axis-value / scroll-delta, depending on `kind`.
int32_t x;
// y / dy / abs-y, depending on `kind`.
int32_t y;
// modifier bitmask or gamepad index.
uint32_t flags;
} PunktfunkInputEvent;
// Snapshot of session counters.
typedef struct {
uint64_t frames_submitted;
uint64_t frames_completed;
uint64_t frames_dropped;
uint64_t packets_sent;
uint64_t packets_received;
uint64_t packets_dropped;
uint64_t fec_recovered_shards;
uint64_t bytes_sent;
uint64_t bytes_received;
} PunktfunkStats;
#if defined(PUNKTFUNK_FEATURE_QUIC)
// One Opus audio packet pulled off a `punktfunk/1` connection (48 kHz stereo, 5 ms frames).
// `data` borrows connection memory until the next `punktfunk_connection_next_audio` call.
typedef struct {
const uint8_t *data;
uintptr_t len;
uint32_t seq;
uint64_t pts_ns;
} PunktfunkAudioPacket;
#endif
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
// Current ABI version. Mismatch with [`crate::ABI_VERSION`] means incompatible core.
uint32_t punktfunk_abi_version(void);
// Create a session over a real UDP transport (`local`/`peer` are `host:port` strings).
// Returns NULL on error.
//
// # Safety
// `cfg`, `local`, `peer` must be valid pointers; the strings must be NUL-terminated.
PunktfunkSession *punktfunk_session_new(const PunktfunkConfig *cfg,
const char *local,
const char *peer);
// Create a connected host+client session pair sharing an in-process loopback
// transport. Test/dev only — exercises the full FEC + framing path without a network.
//
// # Safety
// All four pointers must be valid; the two out-params receive owned handles.
PunktfunkStatus punktfunk_test_loopback_pair(const PunktfunkConfig *host_cfg,
const PunktfunkConfig *client_cfg,
PunktfunkSession **out_host,
PunktfunkSession **out_client);
// Free a session handle. Safe to call with NULL.
//
// # Safety
// `s` must be a handle from `punktfunk_session_new`/`punktfunk_test_loopback_pair`, freed once.
void punktfunk_session_free(PunktfunkSession *s);
// Host: FEC-protect, packetize, seal and send one encoded access unit.
//
// # Safety
// `s` is a valid host handle; `data` points to `len` readable bytes (or `len == 0`).
PunktfunkStatus punktfunk_host_submit_frame(PunktfunkSession *s,
const uint8_t *data,
uintptr_t len,
uint64_t pts_ns,
uint32_t flags);
// Client: poll for the next reassembled access unit. Returns [`PunktfunkStatus::NoFrame`]
// when nothing is ready yet. On `Ok`, `*out` borrows session memory until the next poll.
//
// # Safety
// `s` is a valid client handle; `out` points to a writable `PunktfunkFrame`.
PunktfunkStatus punktfunk_client_poll_frame(PunktfunkSession *s, PunktfunkFrame *out);
// Client: serialize and send one input event to the host.
//
// # Safety
// `s` is a valid client handle; `ev` points to a valid [`InputEvent`].
PunktfunkStatus punktfunk_send_input(PunktfunkSession *s, const PunktfunkInputEvent *ev);
// Register the host-side input callback (pass a NULL fn pointer to clear). The callback
// fires from within [`punktfunk_host_poll_input`], on the calling thread.
//
// # Safety
// `s` is a valid host handle; `user` is passed back verbatim to `cb`.
PunktfunkStatus punktfunk_set_input_callback(PunktfunkSession *s,
void (*cb)(const PunktfunkInputEvent *event, void *user),
void *user);
// Host: drain all pending input events, invoking the registered callback for each.
// Returns the count dispatched (≥ 0), or a negative [`PunktfunkStatus`] on error.
//
// # Safety
// `s` is a valid host handle.
int32_t punktfunk_host_poll_input(PunktfunkSession *s);
// Copy session counters into `*out`.
//
// # Safety
// `s` is a valid handle; `out` points to a writable `PunktfunkStats`.
PunktfunkStatus punktfunk_get_stats(PunktfunkSession *s, PunktfunkStats *out);
#if defined(PUNKTFUNK_FEATURE_QUIC)
// Connect to a `punktfunk/1` host and start a session at `width`x`height`@`refresh_hz`.
// Blocks up to `timeout_ms` for the handshake. Returns NULL on failure.
//
// Trust: `pin_sha256` (NULL or 32 bytes) is the expected SHA-256 fingerprint of the host's
// certificate — a mismatching host is rejected. NULL = trust on first use; persist the
// fingerprint written to `observed_sha256_out` (NULL or 32 bytes, filled on success) and
// pass it as the pin on every later connect.
//
// # Safety
// `host` is a NUL-terminated UTF-8 string (IP or hostname resolvable by the platform);
// `pin_sha256`/`observed_sha256_out` are each NULL or valid for 32 bytes.
PunktfunkConnection *punktfunk_connect(const char *host,
uint16_t port,
uint32_t width,
uint32_t height,
uint32_t refresh_hz,
const uint8_t *pin_sha256,
uint8_t *observed_sha256_out,
uint32_t timeout_ms);
#endif
#if defined(PUNKTFUNK_FEATURE_QUIC)
// Pull the next reassembled access unit, waiting up to `timeout_ms`. Returns
// [`PunktfunkStatus::NoFrame`] on timeout and [`PunktfunkStatus::Closed`] once the session ended.
// On `Ok`, `*out` borrows connection memory **until the next `next_au` call** on this
// handle (the audio/rumble planes do not invalidate it).
//
// # Safety
// `c` is a valid connection handle; `out` is writable. At most one thread pulls video —
// it may run concurrently with one audio-pulling and one rumble-pulling thread.
PunktfunkStatus punktfunk_connection_next_au(PunktfunkConnection *c,
PunktfunkFrame *out,
uint32_t timeout_ms);
#endif
#if defined(PUNKTFUNK_FEATURE_QUIC)
// Pull the next Opus audio packet, waiting up to `timeout_ms`. Returns
// [`PunktfunkStatus::NoFrame`] on timeout and [`PunktfunkStatus::Closed`] once the session ended.
// On `Ok`, `out->data` borrows connection memory **until the next audio call** on this
// handle (independent of the video slot). Drain from a dedicated audio thread — packets
// arrive every 5 ms and the internal queue holds 320 ms.
//
// # Safety
// `c` is a valid connection handle; `out` is writable. At most one thread pulls audio —
// it may run concurrently with the video/rumble pullers.
PunktfunkStatus punktfunk_connection_next_audio(PunktfunkConnection *c,
PunktfunkAudioPacket *out,
uint32_t timeout_ms);
#endif
#if defined(PUNKTFUNK_FEATURE_QUIC)
// Pull the next rumble (force-feedback) update, waiting up to `timeout_ms`. Amplitudes
// are 0..0xFFFF (`low` = low-frequency motor, `high` = high-frequency), `(0, 0)` = stop.
// Same timeout/closed semantics as [`punktfunk_connection_next_audio`].
//
// # Safety
// `c` is a valid connection handle; out pointers are writable (NULLs are skipped). At
// most one thread pulls rumble — it may run concurrently with the video/audio pullers.
PunktfunkStatus punktfunk_connection_next_rumble(PunktfunkConnection *c,
uint16_t *pad,
uint16_t *low,
uint16_t *high,
uint32_t timeout_ms);
#endif
#if defined(PUNKTFUNK_FEATURE_QUIC)
// Send one input event to the host as a QUIC datagram (non-blocking enqueue).
//
// # Safety
// `c` is a valid connection handle; `ev` points to a valid [`InputEvent`].
PunktfunkStatus punktfunk_connection_send_input(PunktfunkConnection *c,
const PunktfunkInputEvent *ev);
#endif
#if defined(PUNKTFUNK_FEATURE_QUIC)
// The host-confirmed session mode (from the Welcome). Safe any time after connect.
//
// # Safety
// `c` is a valid connection handle; out pointers are writable (NULLs are skipped).
PunktfunkStatus punktfunk_connection_mode(const PunktfunkConnection *c,
uint32_t *width,
uint32_t *height,
uint32_t *refresh_hz);
#endif
#if defined(PUNKTFUNK_FEATURE_QUIC)
// Close the connection and free the handle (joins the internal threads). NULL is a no-op.
//
// # Safety
// `c` was returned by [`punktfunk_connect`] and is not used after this call.
void punktfunk_connection_close(PunktfunkConnection *c);
#endif
#ifdef __cplusplus
} // extern "C"
#endif // __cplusplus
#endif /* PUNKTFUNK_CORE_H */