/* 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.
//
// v2: `punktfunk_connect` gained `client_cert_pem`/`client_key_pem` (pairing identities);
// added `punktfunk_pair` / `punktfunk_generate_identity` / `punktfunk_connection_request_mode`.
#define ABI_VERSION 2

// `PunktfunkHidOutput::kind` — lightbar RGB (`r`/`g`/`b` valid).
#define PUNKTFUNK_HIDOUT_LED 1

// `PunktfunkHidOutput::kind` — player-indicator LEDs (`player_bits` valid, low 5 bits).
#define PUNKTFUNK_HIDOUT_PLAYER_LEDS 2

// `PunktfunkHidOutput::kind` — one adaptive-trigger effect (`which` + `effect`/`effect_len` valid).
#define PUNKTFUNK_HIDOUT_TRIGGER 3

// `PunktfunkHidOutput::kind` — a trackpad haptic pulse (Steam Controller voice-coils). `which` =
// side (0 = right pad, 1 = left pad); `effect[0..6]` packs `amplitude` / `period` / `count` as
// little-endian `u16`s with `effect_len = 6`. Clients without trackpad coils drop it.
#define PUNKTFUNK_HIDOUT_TRACKPAD_HAPTIC 4

// Capacity of `PunktfunkHidOutput::effect` (the DualSense trigger parameter block).
#define PUNKTFUNK_HID_EFFECT_MAX 11

// `PunktfunkRichInput::kind` — a touchpad contact (`finger`/`active`/`x`/`y` valid).
#define PUNKTFUNK_RICH_TOUCHPAD 1

// `PunktfunkRichInput::kind` — a motion sample (`gyro`/`accel` valid).
#define PUNKTFUNK_RICH_MOTION 2

// `RichInput::TouchpadEx` kind on the wire — an extended trackpad contact that identifies the
// surface (0 single / 1 Steam-left / 2 Steam-right) and carries click + pressure. The host decodes
// it today; *sending* it from a C client needs the size-prefixed `PunktfunkRichInputEx` +
// `punktfunk_connection_send_rich_input2` (added with client capture).
#define PUNKTFUNK_RICH_TOUCHPAD_EX 3

// Compositor preference for [`punktfunk_connect_ex`] (`compositor` arg). `AUTO` lets the host
// pick (auto-detect from its running desktop); a concrete value is honored only if that backend
// is available on the host right now, else the host falls back to auto-detect. The resolved
// choice is reported back over the protocol (see `punktfunk/1` `Welcome`).
#define PUNKTFUNK_COMPOSITOR_AUTO 0

// KWin / KDE Plasma.
#define PUNKTFUNK_COMPOSITOR_KWIN 1

// wlroots (Sway / Hyprland).
#define PUNKTFUNK_COMPOSITOR_WLROOTS 2

// Mutter / GNOME.
#define PUNKTFUNK_COMPOSITOR_MUTTER 3

// gamescope (spawned nested).
#define PUNKTFUNK_COMPOSITOR_GAMESCOPE 4

// Gamepad-backend preference for [`punktfunk_connect_ex2`] (`gamepad` arg): which virtual pad
// the host creates for this session's controllers. Precedence host-side: an explicit client
// choice > the host's `PUNKTFUNK_GAMEPAD` env var > X-Box 360. `AUTO` (or any unrecognized
// value) = host decides. The resolved choice is echoed over the protocol (`Welcome`) and
// readable via [`punktfunk_connection_gamepad`].
#define PUNKTFUNK_GAMEPAD_AUTO 0

// uinput X-Box 360 pad (the universal default — every game speaks XInput).
#define PUNKTFUNK_GAMEPAD_XBOX360 1

// UHID DualSense (kernel `hid-playstation`): adaptive triggers, lightbar, touchpad, motion —
// feedback arrives on the HID-output plane ([`punktfunk_connection_next_hidout`]). Honored
// only where available (Linux hosts); otherwise the host falls back to X-Box 360.
#define PUNKTFUNK_GAMEPAD_DUALSENSE 2

// uinput X-Box One / Series pad — the X-Box 360 backend with the One/Series USB identity, so
// games show One/Series glyphs. XInput-identical to `XBOX360` otherwise (no game-visible gain;
// impulse-trigger rumble is unreachable through a virtual pad). Useful for glyph-matching a
// physical X-Box One/Series controller on the client.
#define PUNKTFUNK_GAMEPAD_XBOXONE 3

// UHID DualShock 4 (kernel `hid-playstation` ≥ 6.2): lightbar, touchpad, motion, rumble — the
// touchpad/motion arrive over the rich-input plane and lightbar over the HID-output plane, like
// DualSense (minus adaptive triggers / player LEDs / mute). Honored only where available (Linux
// hosts); otherwise the host falls back to X-Box 360.
#define PUNKTFUNK_GAMEPAD_DUALSHOCK4 4

// UHID classic Steam Controller (Valve `28DE:1102`, kernel `hid-steam`): dual trackpads, gyro,
// two grip paddles. Reserved — currently folds to `XBOX360` until its backend lands.
#define PUNKTFUNK_GAMEPAD_STEAMCONTROLLER 5

// UHID Steam Deck controller (Valve `28DE:1205`, kernel `hid-steam`): full Deck gamepad incl. the
// four back grips, a right trackpad, and the IMU; re-grabbed by Steam Input with native glyphs when
// Steam runs on the host. Honored only where available (Linux hosts); else folds to X-Box 360.
#define PUNKTFUNK_GAMEPAD_STEAMDECK 6

// Extended `InputEvent` gamepad button bits for embedders building raw events: the four back grips
// (Steam L4/L5/R4/R5 ≙ Xbox-Elite P1–P4) + the misc/capture button, in Moonlight's
// `buttonFlags2 << 16` namespace. Mirror `input::gamepad::BTN_PADDLE1..4` / `BTN_MISC1`.
#define PUNKTFUNK_GAMEPAD_BTN_PADDLE1 65536

#define PUNKTFUNK_GAMEPAD_BTN_PADDLE2 131072

#define PUNKTFUNK_GAMEPAD_BTN_PADDLE3 262144

#define PUNKTFUNK_GAMEPAD_BTN_PADDLE4 524288

#define PUNKTFUNK_GAMEPAD_BTN_MISC1 2097152

// 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. Equivalent to
// [`punktfunk_connect_ex`] with `compositor = PUNKTFUNK_COMPOSITOR_AUTO`.
//
// Video-capability bit for [`punktfunk_connect_ex5`] (`video_caps`): the client can decode a
// 10-bit (Main10) HEVC stream. (Mirrors `quic::VIDEO_CAP_10BIT`.)
#define PUNKTFUNK_VIDEO_CAP_10BIT 1

// Video-capability bit for [`punktfunk_connect_ex5`] (`video_caps`): the client can present
// BT.2020 PQ HDR10 (implies 10-bit). (Mirrors `quic::VIDEO_CAP_HDR`.)
#define PUNKTFUNK_VIDEO_CAP_HDR 2

// Video-capability bit for [`punktfunk_connect_ex5`] (`video_caps`): the client can decode a
// full-chroma 4:4:4 HEVC stream (Range Extensions). The host emits 4:4:4 only when this is set,
// the host opted in, the codec is HEVC, and the GPU supports it — else the stream stays 4:2:0 and
// [`punktfunk_connection_chroma_format`] reports the real value. (Mirrors `quic::VIDEO_CAP_444`.)
#define PUNKTFUNK_VIDEO_CAP_444 4

// 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

// Back grip R4 — SDL `RightPaddle1` / GameStream `PADDLE1`.
#define BTN_PADDLE1 65536

// Back grip L4 — SDL `LeftPaddle1` / GameStream `PADDLE2`.
#define BTN_PADDLE2 131072

// Back grip R5 — SDL `RightPaddle2` / GameStream `PADDLE3`.
#define BTN_PADDLE3 262144

// Back grip L5 — SDL `LeftPaddle2` / GameStream `PADDLE4`.
#define BTN_PADDLE4 524288

// DualSense touchpad click. Moonlight's extended-button position (`buttonFlags2`
// merges in at `<< 16`, see `gamestream/gamepad.rs`), so GameStream clients land on
// the same bit. Only the DualSense backend renders it; the xpad has no such button.
#define PUNKTFUNK_BTN_TOUCHPAD 1048576

// Misc / capture button — the Deck `…`/quick-access, Share/Capture / GameStream `MISC`.
#define BTN_MISC1 2097152

// 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

// Bandwidth-probe filler, not decodable video: a [`crate::quic::ProbeRequest`] speed test makes
// the host burst access units carrying this flag so the client measures throughput/loss without
// feeding them to the decoder. Punktfunk/1 only (GameStream never sets it).
#define FLAG_PROBE 8

// 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)
// [`Hello::video_caps`] bit: the client can decode a 10-bit (Main10) HEVC stream.
#define VIDEO_CAP_10BIT 1
#endif

#if defined(PUNKTFUNK_FEATURE_QUIC)
// [`Hello::video_caps`] bit: the client can present BT.2020 PQ HDR10 (implies 10-bit).
#define VIDEO_CAP_HDR 2
#endif

#if defined(PUNKTFUNK_FEATURE_QUIC)
// [`Hello::video_caps`] bit: the client can decode a full-chroma **4:4:4** HEVC stream (HEVC
// Range Extensions / Rec.ITU-T H.265 `chroma_format_idc = 3`). The host emits 4:4:4 ONLY when this
// bit is set, the host opted in (`PUNKTFUNK_444`), the codec is HEVC, **and** the GPU/driver
// actually supports a 4:4:4 encode (probed) — otherwise the session stays 4:2:0 and
// [`Welcome::chroma_format`] reflects the real resolved value. Independent of 10-bit/HDR (4:4:4 is a
// chroma decision, bit depth is a depth decision; the two may combine where the hardware allows).
#define VIDEO_CAP_444 4
#endif

#if defined(PUNKTFUNK_FEATURE_QUIC)
// HEVC `chroma_format_idc` for 4:2:0 — what every pre-4:4:4 build produced and the back-compat
// default when a peer omits [`Welcome::chroma_format`].
#define CHROMA_IDC_420 1
#endif

#if defined(PUNKTFUNK_FEATURE_QUIC)
// HEVC `chroma_format_idc` for full-chroma 4:4:4 (Range Extensions).
#define CHROMA_IDC_444 3
#endif

#if defined(PUNKTFUNK_FEATURE_QUIC)
// Longest device name carried in a [`Hello`] (bytes of UTF-8; longer names are truncated on
// encode, rejected on decode — a one-byte length prefix caps it at 255 anyway).
#define HELLO_NAME_MAX 64
#endif

#if defined(PUNKTFUNK_FEATURE_QUIC)
// Longest library id carried in a [`Hello::launch`] (bytes of UTF-8). Ids are short
// (`steam:<appid>` / `custom:<12 hex>`); the cap just bounds an attacker-controlled field.
#define HELLO_LAUNCH_MAX 128
#endif

#if defined(PUNKTFUNK_FEATURE_QUIC)
// Type byte of [`Reconfigure`] (first byte after the magic).
#define MSG_RECONFIGURE 1
#endif

#if defined(PUNKTFUNK_FEATURE_QUIC)
// Type byte of [`Reconfigured`].
#define MSG_RECONFIGURED 2
#endif

#if defined(PUNKTFUNK_FEATURE_QUIC)
// Type byte of [`RequestKeyframe`].
#define MSG_REQUEST_KEYFRAME 3
#endif

#if defined(PUNKTFUNK_FEATURE_QUIC)
// Type byte of [`LossReport`].
#define MSG_LOSS_REPORT 4
#endif

#if defined(PUNKTFUNK_FEATURE_QUIC)
// Type byte of [`ProbeRequest`].
#define MSG_PROBE_REQUEST 32
#endif

#if defined(PUNKTFUNK_FEATURE_QUIC)
// Type byte of [`ProbeResult`].
#define MSG_PROBE_RESULT 33
#endif

#if defined(PUNKTFUNK_FEATURE_QUIC)
// Type byte of [`ClockProbe`].
#define MSG_CLOCK_PROBE 48
#endif

#if defined(PUNKTFUNK_FEATURE_QUIC)
// Type byte of [`ClockEcho`].
#define MSG_CLOCK_ECHO 49
#endif

#if defined(PUNKTFUNK_FEATURE_QUIC)
// Type byte of [`PairRequest`].
#define MSG_PAIR_REQUEST 16
#endif

#if defined(PUNKTFUNK_FEATURE_QUIC)
// Type byte of [`PairChallenge`].
#define MSG_PAIR_CHALLENGE 17
#endif

#if defined(PUNKTFUNK_FEATURE_QUIC)
// Type byte of [`PairProof`].
#define MSG_PAIR_PROOF 18
#endif

#if defined(PUNKTFUNK_FEATURE_QUIC)
// Type byte of [`PairResult`].
#define MSG_PAIR_RESULT 19
#endif

#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, client→host),
// audio = [`AUDIO_MAGIC`] (0xC9, host→client), rumble = [`RUMBLE_MAGIC`] (0xCA, host→client),
// mic = [`MIC_MAGIC`] (0xCB, client→host), rich-input = [`RICH_INPUT_MAGIC`] (0xCC, client→host),
// HID-output = [`HIDOUT_MAGIC`] (0xCD, host→client), HDR metadata = [`HDR_META_MAGIC`]
// (0xCE, host→client).
#define PUNKTFUNK_AUDIO_MAGIC 201
#endif

#if defined(PUNKTFUNK_FEATURE_QUIC)
#define PUNKTFUNK_RUMBLE_MAGIC 202
#endif

#if defined(PUNKTFUNK_FEATURE_QUIC)
// Microphone uplink: the client's mic, Opus-encoded, client → host (the inverse of
// [`AUDIO_MAGIC`]). The host feeds it into a virtual PipeWire source so its apps can record it.
#define MIC_MAGIC 203
#endif

#if defined(PUNKTFUNK_FEATURE_QUIC)
// Rich client→host input: events too big for the fixed 18-byte [`InputEvent`]
// (crate::input::InputEvent) — the DualSense touchpad and motion sensors. Variable-length,
// kind-tagged (see [`RichInput`]).
#define RICH_INPUT_MAGIC 204
#endif

#if defined(PUNKTFUNK_FEATURE_QUIC)
// HID output, host → client: DualSense feedback a game wrote to the host's virtual controller
// (lightbar, player LEDs, adaptive triggers) — the rich analog of [`RUMBLE_MAGIC`]. See
// [`HidOutput`].
#define HIDOUT_MAGIC 205
#endif

#if defined(PUNKTFUNK_FEATURE_QUIC)
// HDR static-metadata datagram tag, host → client (the static analog of the per-frame VUI;
// see [`HdrMeta`]). Next tag after [`HIDOUT_MAGIC`].
#define HDR_META_MAGIC 206
#endif

#if defined(PUNKTFUNK_FEATURE_QUIC)
// CICP colour-primaries code point: BT.709.
#define ColorInfo_CP_BT709 1
#endif

#if defined(PUNKTFUNK_FEATURE_QUIC)
// CICP colour-primaries code point: BT.2020.
#define ColorInfo_CP_BT2020 9
#endif

#if defined(PUNKTFUNK_FEATURE_QUIC)
// CICP transfer code point: BT.709.
#define ColorInfo_TRC_BT709 1
#endif

#if defined(PUNKTFUNK_FEATURE_QUIC)
// CICP transfer code point: PQ (SMPTE ST.2084).
#define ColorInfo_TRC_PQ 16
#endif

#if defined(PUNKTFUNK_FEATURE_QUIC)
// CICP transfer code point: HLG (ARIB STD-B67 / BT.2100).
#define ColorInfo_TRC_HLG 18
#endif

#if defined(PUNKTFUNK_FEATURE_QUIC)
// CICP matrix code point: BT.709.
#define ColorInfo_MC_BT709 1
#endif

#if defined(PUNKTFUNK_FEATURE_QUIC)
// CICP matrix code point: BT.2020 non-constant-luminance. (Never emit 10 / constant-luminance —
// no client decodes it.)
#define ColorInfo_MC_BT2020_NCL 9
#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 and `flags` packs the client's
    // coordinate-space size as `(width << 16) | height` (the same contract as
    // [`TouchDown`](Self::TouchDown)) — injectors normalize against it before mapping
    // into the output region and **drop the event when it is zero**.
    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,
    // Touch begins. `code` = touch id (which finger; reusable after `TouchUp`), `x`/`y` =
    // pixel coordinates and `flags` = `(width << 16) | height` of the client's touch surface
    // — the same absolute mapping as [`MouseMoveAbs`](Self::MouseMoveAbs).
    PUNKTFUNK_INPUT_KIND_TOUCH_DOWN = 9,
    // Touch moves. Same field meaning as [`TouchDown`](Self::TouchDown).
    PUNKTFUNK_INPUT_KIND_TOUCH_MOVE = 10,
    // Touch ends. Only `code` (the touch id) is used.
    PUNKTFUNK_INPUT_KIND_TOUCH_UP = 11,
};
#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)
// Per-session colour signalling (CICP / ITU-T H.273 code points) the host resolved for the
// encoded video, carried on [`Welcome`]. A client configures its decoder/presenter from these
// instead of inferring them from the bitstream VUI. An older host omits the bytes on the wire →
// [`ColorInfo::SDR_BT709`] (the 8-bit BT.709 limited stream every pre-HDR build produced).
//
// The *static* HDR mastering metadata (ST.2086 + content light level) is larger and can change
// mid-stream, so it rides the [`HDR_META_MAGIC`] datagram rather than this fixed struct.
typedef struct ColorInfo ColorInfo;
#endif

#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;
    // Packets dropped on the host send path because the kernel buffer was full (WouldBlock) — the
    // dominant loss mode at very high bitrate; distinct from `packets_dropped` (recv-side).
    uint64_t packets_send_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

#if defined(PUNKTFUNK_FEATURE_QUIC)
// One decoded audio frame from [`punktfunk_connection_next_audio_pcm`]: interleaved 32-bit
// float PCM at 48 kHz, in the canonical wire channel order `FL FR FC LFE RL RR SL SR` (the
// first `channels` of it). `samples` points at `frame_count * channels` floats and borrows
// connection memory **until the next PCM call** on this handle.
typedef struct {
    // Interleaved f32 samples (wire channel order), `frame_count * channels` long.
    const float *samples;
    // Samples per channel in this frame.
    uint32_t frame_count;
    // Channel count (2/6/8) — the negotiated [`punktfunk_connection_audio_channels`].
    uint8_t channels;
    // Source packet sequence number.
    uint32_t seq;
    // Capture presentation timestamp (ns).
    uint64_t pts_ns;
} PunktfunkAudioPcm;
#endif

#if defined(PUNKTFUNK_FEATURE_QUIC)
// One DualSense HID-output feedback event a game wrote to the host's virtual pad
// ([`punktfunk_connection_next_hidout`]). `kind` selects which fields are meaningful — replay it
// on a real DualSense (lightbar color, player LEDs, or an adaptive-trigger effect via the
// platform's `GCDualSenseAdaptiveTrigger`-style API).
typedef struct {
    // One of `PUNKTFUNK_HIDOUT_*`.
    uint8_t kind;
    // Gamepad index.
    uint8_t pad;
    // LED: lightbar red.
    uint8_t r;
    // LED: lightbar green.
    uint8_t g;
    // LED: lightbar blue.
    uint8_t b;
    // PlayerLeds: lit player indicators (low 5 bits).
    uint8_t player_bits;
    // Trigger: 0 = L2, 1 = R2.
    uint8_t which;
    // Trigger: number of valid bytes in `effect` (≤ `PUNKTFUNK_HID_EFFECT_MAX`).
    uint8_t effect_len;
    // Trigger: the raw DualSense trigger parameter block (mode + params).
    uint8_t effect[11];
} PunktfunkHidOutput;
#endif

#if defined(PUNKTFUNK_FEATURE_QUIC)
// Static HDR metadata for an HDR session ([`punktfunk_connection_next_hdr_meta`]): SMPTE ST.2086
// mastering display colour volume + CEA-861.3 content light level. All fields are in the standard
// HDR10 SEI fixed-point units (primaries/white in 1/50000, luminance in 0.0001 cd/m²), ready for
// DXGI `DXGI_HDR_METADATA_HDR10` / Apple `CAEDRMetadata` / Android `KEY_HDR_STATIC_INFO`.
typedef struct {
    // Display-primaries x-chromaticities in 1/50000 units, ST.2086 order [green, blue, red].
    uint16_t display_primaries_x[3];
    // Display-primaries y-chromaticities in 1/50000 units, ST.2086 order [green, blue, red].
    uint16_t display_primaries_y[3];
    // White-point x-chromaticity, 1/50000 units.
    uint16_t white_point_x;
    // White-point y-chromaticity, 1/50000 units.
    uint16_t white_point_y;
    // Max display mastering luminance, 0.0001 cd/m² units.
    uint32_t max_display_mastering_luminance;
    // Min display mastering luminance, 0.0001 cd/m² units.
    uint32_t min_display_mastering_luminance;
    // Maximum content light level (MaxCLL), nits. 0 = unknown.
    uint16_t max_cll;
    // Maximum frame-average light level (MaxFALL), nits. 0 = unknown.
    uint16_t max_fall;
} PunktfunkHdrMeta;
#endif

#if defined(PUNKTFUNK_FEATURE_QUIC)
// One rich client→host input for the host's virtual DualSense
// ([`punktfunk_connection_send_rich_input`]): a touchpad contact or a motion sample. Set `kind`
// and the matching fields; the others are ignored.
typedef struct {
    // One of `PUNKTFUNK_RICH_*`.
    uint8_t kind;
    // Gamepad index.
    uint8_t pad;
    // Touchpad: contact id (0 or 1).
    uint8_t finger;
    // Touchpad: 1 = finger down, 0 = lifted.
    uint8_t active;
    // Touchpad: normalized x, 0..=65535 across the touchpad.
    uint16_t x;
    // Touchpad: normalized y, 0..=65535 across the touchpad.
    uint16_t y;
    // Motion: gyro (pitch, yaw, roll), raw signed-16.
    int16_t gyro[3];
    // Motion: accelerometer (x, y, z), raw signed-16.
    int16_t accel[3];
} PunktfunkRichInput;
#endif

#if defined(PUNKTFUNK_FEATURE_QUIC)
// Forward-compatible superset of [`PunktfunkRichInput`] that can also express the rich Steam
// surfaces: a *second* trackpad (`surface`), a distinct `click` vs touch, signed coordinates, and
// pressure. Sent via [`punktfunk_connection_send_rich_input2`] — the only way a C client can emit a
// `TouchpadEx`. The caller MUST set `struct_size = sizeof(PunktfunkRichInputEx)` (the ABI-skew
// guard, like [`PunktfunkConfig`]); the legacy [`PunktfunkRichInput`] +
// [`punktfunk_connection_send_rich_input`] stay byte-for-byte for existing callers.
typedef struct {
    // MUST equal `sizeof(PunktfunkRichInputEx)`.
    uint32_t struct_size;
    // One of `PUNKTFUNK_RICH_*` (`TOUCHPAD` / `MOTION` / `TOUCHPAD_EX`).
    uint8_t kind;
    // Gamepad index.
    uint8_t pad;
    // Touchpad/TouchpadEx: contact id.
    uint8_t finger;
    // Touchpad/TouchpadEx: 1 = finger down / touching, 0 = lifted.
    uint8_t active;
    // TouchpadEx: which surface — 0 = single/DualSense, 1 = Steam left pad, 2 = Steam right pad.
    uint8_t surface;
    // TouchpadEx: 1 = the pad is physically clicked (depressed), distinct from a touch contact.
    uint8_t click;
    // Reserved for alignment; set to 0.
    uint8_t _reserved[2];
    // TouchpadEx: x coordinate — **signed**, centred at 0 (the real Steam report convention). For a
    // legacy `TOUCHPAD` kind sent through this struct, store the unsigned `0..=65535` value's bits.
    int16_t x;
    // TouchpadEx: y coordinate — signed, centred at 0.
    int16_t y;
    // TouchpadEx: contact pressure (`0` if the surface has no force sensor).
    uint16_t pressure;
    // Motion: gyro (pitch, yaw, roll), raw signed-16.
    int16_t gyro[3];
    // Motion: accelerometer (x, y, z), raw signed-16.
    int16_t accel[3];
} PunktfunkRichInputEx;
#endif

// A speed-test measurement, filled by [`punktfunk_connection_probe_result`]. `done` is 0 until
// the host's end-of-burst report lands, then 1 (the numbers are final). `throughput_kbps` is the
// delivered wire throughput to drive a bitrate choice from; `loss_pct` is the link loss and
// `host_drop_pct` the host-side send-buffer drop (raise `net.core.wmem_max`) — they're measured
// separately so a host that can't keep up reads differently from a lossy link.
typedef struct {
    // 1 once the host's end-of-burst report arrived (measurement final); else 0 (partial).
    uint8_t done;
    // Delivered wire bytes (header + shard) / packets the client received during the burst.
    uint64_t recv_bytes;
    uint32_t recv_packets;
    // Application goodput bytes / access units the host offered.
    uint64_t host_bytes;
    uint32_t host_packets;
    // The host's measured burst duration, milliseconds (the throughput denominator).
    uint32_t elapsed_ms;
    // Delivered wire throughput = `recv_bytes * 8 / elapsed_ms` (kilobits/second).
    uint32_t throughput_kbps;
    // Link loss `(wire_packets_sent − recv_packets) / wire_packets_sent` as a percentage.
    float loss_pct;
    // Host-side send-buffer drop `send_dropped / (wire_packets_sent + send_dropped)`, percent.
    float host_drop_pct;
    // Wire packets the host put on the link, and the ones its send buffer dropped (raw counts).
    uint32_t wire_packets_sent;
    uint32_t send_dropped;
} PunktfunkProbeResult;





#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)
// 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.
//
// Identity: `client_cert_pem`/`client_key_pem` (both NULL, or both NUL-terminated PEM
// strings — see [`punktfunk_generate_identity`]) are presented via TLS client auth so a
// host can recognize this client once paired ([`punktfunk_pair`]). NULL = anonymous;
// hosts running `--require-pairing` reject anonymous sessions.
//
// # 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;
// `client_cert_pem`/`client_key_pem` are each NULL or NUL-terminated UTF-8.
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,
                                       const char *client_cert_pem,
                                       const char *client_key_pem,
                                       uint32_t timeout_ms);
#endif

#if defined(PUNKTFUNK_FEATURE_QUIC)
// Like [`punktfunk_connect`], but requests a specific `compositor` backend on the host (one of
// the `PUNKTFUNK_COMPOSITOR_*` values). `PUNKTFUNK_COMPOSITOR_AUTO` (or any unrecognized value)
// lets the host decide; a concrete value is honored only if available, else the host falls back
// to auto-detect. The resolved choice is logged host-side and returned over the protocol.
// Equivalent to [`punktfunk_connect_ex2`] with `gamepad = PUNKTFUNK_GAMEPAD_AUTO`.
//
// # Safety
// Same as [`punktfunk_connect`].
PunktfunkConnection *punktfunk_connect_ex(const char *host,
                                          uint16_t port,
                                          uint32_t width,
                                          uint32_t height,
                                          uint32_t refresh_hz,
                                          uint32_t compositor,
                                          const uint8_t *pin_sha256,
                                          uint8_t *observed_sha256_out,
                                          const char *client_cert_pem,
                                          const char *client_key_pem,
                                          uint32_t timeout_ms);
#endif

#if defined(PUNKTFUNK_FEATURE_QUIC)
// Like [`punktfunk_connect_ex`], but additionally requests which virtual `gamepad` backend the
// host creates for this session's pads (one of the `PUNKTFUNK_GAMEPAD_*` values).
// `PUNKTFUNK_GAMEPAD_AUTO` (or any unrecognized value) lets the host decide (its
// `PUNKTFUNK_GAMEPAD` env var, else X-Box 360); a concrete value is honored only if that
// backend is available on the host. The resolved choice is readable via
// [`punktfunk_connection_gamepad`] — only a DualSense session emits HID-output feedback.
//
// # Safety
// Same as [`punktfunk_connect`].
PunktfunkConnection *punktfunk_connect_ex2(const char *host,
                                           uint16_t port,
                                           uint32_t width,
                                           uint32_t height,
                                           uint32_t refresh_hz,
                                           uint32_t compositor,
                                           uint32_t gamepad,
                                           const uint8_t *pin_sha256,
                                           uint8_t *observed_sha256_out,
                                           const char *client_cert_pem,
                                           const char *client_key_pem,
                                           uint32_t timeout_ms);
#endif

#if defined(PUNKTFUNK_FEATURE_QUIC)
// Like [`punktfunk_connect_ex2`], but additionally requests the video encoder `bitrate_kbps`
// (kilobits per second). `0` lets the host pick its default; any other value is clamped to the
// host's supported range. After a speed test ([`punktfunk_connection_speed_test`]) a client can
// reconnect (or pick at connect time) with the measured rate. The value the host actually
// configured is readable via [`punktfunk_connection_bitrate`].
//
// # Safety
// Same as [`punktfunk_connect`].
PunktfunkConnection *punktfunk_connect_ex3(const char *host,
                                           uint16_t port,
                                           uint32_t width,
                                           uint32_t height,
                                           uint32_t refresh_hz,
                                           uint32_t compositor,
                                           uint32_t gamepad,
                                           uint32_t bitrate_kbps,
                                           const uint8_t *pin_sha256,
                                           uint8_t *observed_sha256_out,
                                           const char *client_cert_pem,
                                           const char *client_key_pem,
                                           uint32_t timeout_ms);
#endif

#if defined(PUNKTFUNK_FEATURE_QUIC)
// Like [`punktfunk_connect_ex3`], but additionally asks the host to launch a library title in
// this session. `launch_id` is a store-qualified [`crate::library`-style] id as returned by the
// host's `GET /api/v1/library` (`steam:<appid>` / `custom:<id>`); the host resolves it against
// its OWN library and runs the matching recipe — the client never sends a raw command. `NULL`
// (or an empty / unknown id) ⇒ the host's default session, no game launched.
//
// # Safety
// Same as [`punktfunk_connect`]; `launch_id`, when non-NULL, must be a NUL-terminated C string.
PunktfunkConnection *punktfunk_connect_ex4(const char *host,
                                           uint16_t port,
                                           uint32_t width,
                                           uint32_t height,
                                           uint32_t refresh_hz,
                                           uint32_t compositor,
                                           uint32_t gamepad,
                                           uint32_t bitrate_kbps,
                                           const char *launch_id,
                                           const uint8_t *pin_sha256,
                                           uint8_t *observed_sha256_out,
                                           const char *client_cert_pem,
                                           const char *client_key_pem,
                                           uint32_t timeout_ms);
#endif

#if defined(PUNKTFUNK_FEATURE_QUIC)
// Like [`punktfunk_connect_ex4`], but additionally advertises the embedder's video decode/present
// capabilities as `video_caps` — a bitfield of `PUNKTFUNK_VIDEO_CAP_10BIT` (can decode 10-bit
// Main10) and `PUNKTFUNK_VIDEO_CAP_HDR` (can present BT.2020 PQ HDR10). The host upgrades to a
// 10-bit / HDR encode ONLY when the matching bit is set (and the host opted in); `0` keeps the
// 8-bit BT.709 SDR stream. After connecting, read the resolved colour via
// [`punktfunk_connection_color_info`] and drain the mastering metadata via
// [`punktfunk_connection_next_hdr_meta`].
//
// # Safety
// Same as [`punktfunk_connect`]; `launch_id`, when non-NULL, must be a NUL-terminated C string.
PunktfunkConnection *punktfunk_connect_ex5(const char *host,
                                           uint16_t port,
                                           uint32_t width,
                                           uint32_t height,
                                           uint32_t refresh_hz,
                                           uint32_t compositor,
                                           uint32_t gamepad,
                                           uint32_t bitrate_kbps,
                                           uint8_t video_caps,
                                           const char *launch_id,
                                           const uint8_t *pin_sha256,
                                           uint8_t *observed_sha256_out,
                                           const char *client_cert_pem,
                                           const char *client_key_pem,
                                           uint32_t timeout_ms);
#endif

#if defined(PUNKTFUNK_FEATURE_QUIC)
// Like [`punktfunk_connect_ex5`], but additionally requests the audio channel count:
// `2` (stereo, the default behaviour of every earlier variant), `6` (5.1) or `8` (7.1). The host
// clamps the request to what it can actually capture and echoes the resolved count via
// [`punktfunk_connection_audio_channels`]; the `0xC9` audio frames are Opus-(multi)stream encoded
// for that layout. A client that wants surround calls this; everything else inherits stereo.
//
// # Safety
// Same as [`punktfunk_connect`].
PunktfunkConnection *punktfunk_connect_ex6(const char *host,
                                           uint16_t port,
                                           uint32_t width,
                                           uint32_t height,
                                           uint32_t refresh_hz,
                                           uint32_t compositor,
                                           uint32_t gamepad,
                                           uint32_t bitrate_kbps,
                                           uint8_t video_caps,
                                           uint8_t audio_channels,
                                           const char *launch_id,
                                           const uint8_t *pin_sha256,
                                           uint8_t *observed_sha256_out,
                                           const char *client_cert_pem,
                                           const char *client_key_pem,
                                           uint32_t timeout_ms);
#endif

#if defined(PUNKTFUNK_FEATURE_QUIC)
// Generate a persistent client identity: a self-signed certificate + private key, both
// PEM, NUL-terminated, written into the caller's buffers. Generate ONCE, store both
// strings (Keychain etc.), pass them to [`punktfunk_pair`] and every
// [`punktfunk_connect`] — the certificate's fingerprint is how hosts recognize this
// client. 4096-byte buffers are ample.
//
// # Safety
// `cert_pem_out` is writable for `cert_cap` bytes; `key_pem_out` for `key_cap`.
PunktfunkStatus punktfunk_generate_identity(char *cert_pem_out,
                                            uintptr_t cert_cap,
                                            char *key_pem_out,
                                            uintptr_t key_cap);
#endif

#if defined(PUNKTFUNK_FEATURE_QUIC)
// Run the PIN pairing ceremony against a host (see the protocol docs in punktfunk-core):
// the host displays a short PIN; the user types it into the client app, which passes it
// here. On success the host has stored this client's identity, the now-verified host
// fingerprint is written to `host_sha256_out` (32 bytes) — persist it and pass it as
// `pin_sha256` to [`punktfunk_connect`] from then on. Returns
// [`PunktfunkStatus::Crypto`] for a wrong PIN.
//
// # Safety
// `host`/`client_cert_pem`/`client_key_pem`/`pin`/`name` are NUL-terminated UTF-8;
// `host_sha256_out` is writable for 32 bytes.
PunktfunkStatus punktfunk_pair(const char *host,
                               uint16_t port,
                               const char *client_cert_pem,
                               const char *client_key_pem,
                               const char *pin,
                               const char *name,
                               uint8_t *host_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)
// Read the audio channel count the host resolved for this session (from its Welcome): `2`
// (stereo), `6` (5.1) or `8` (7.1). `*out` is filled when non-NULL. The `0xC9` Opus frames are
// (multistream-)encoded for this layout; an embedder decoding raw frames itself must build its
// decoder from THIS value (see [`crate::audio::layout_for`]) — or use
// [`punktfunk_connection_next_audio_pcm`], which decodes in-core. Available immediately after a
// successful connect (it doesn't change without a reconfigure).
//
// # Safety
// `c` is a valid connection handle; `out` is NULL or writable for one `u8`.
PunktfunkStatus punktfunk_connection_audio_channels(PunktfunkConnection *c, uint8_t *out);
#endif

#if defined(PUNKTFUNK_FEATURE_QUIC)
// Pull the next audio frame and **decode it in-core** to interleaved f32 PCM — for embedders
// without a multistream-capable Opus decoder (e.g. Apple, whose AudioToolbox Opus path is
// stereo-only). The decoder is built once from the negotiated channel count and handles 2/6/8
// channels (a 1-coupled-stream multistream decoder is exactly a stereo decoder). Same
// timeout/closed semantics as [`punktfunk_connection_next_audio`]; `out->samples` borrows
// connection memory until the next PCM call on this handle. Use EITHER this or
// [`punktfunk_connection_next_audio`] on a given connection, from one dedicated audio thread —
// not both (they share the underlying queue).
//
// # Safety
// `c` is a valid connection handle; `out` is writable. At most one thread pulls audio.
PunktfunkStatus punktfunk_connection_next_audio_pcm(PunktfunkConnection *c,
                                                    PunktfunkAudioPcm *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)
// Pull the next DualSense HID-output feedback event (lightbar / player LEDs / adaptive trigger)
// the host's virtual pad received from a game, into `*out`. [`PunktfunkStatus::NoFrame`] on
// timeout, [`PunktfunkStatus::Closed`] once the session ended. Only the DualSense host backend
// emits these. Same threading rules as [`punktfunk_connection_next_rumble`] (one puller, may run
// alongside the other planes).
//
// # Safety
// `c` is a valid connection handle; `out` is writable for one `PunktfunkHidOutput`.
PunktfunkStatus punktfunk_connection_next_hidout(PunktfunkConnection *c,
                                                 PunktfunkHidOutput *out,
                                                 uint32_t timeout_ms);
#endif

#if defined(PUNKTFUNK_FEATURE_QUIC)
// Pull the next static HDR metadata update (ST.2086 mastering display + content light level) for
// an HDR session, into `*out`. [`PunktfunkStatus::NoFrame`] on timeout, [`PunktfunkStatus::Closed`]
// once the session ended. The host sends one near session start and re-sends it on mastering
// changes / keyframes; apply the latest to the display (`SetHDRMetaData` / `CAEDRMetadata` /
// `KEY_HDR_STATIC_INFO`). Only an HDR session (`punktfunk_connection_color_info` reports a PQ
// transfer) ever emits these. Same threading rules as [`punktfunk_connection_next_rumble`] (one
// puller, may run alongside the other planes).
//
// # Safety
// `c` is a valid connection handle; `out` is writable for one `PunktfunkHdrMeta`.
PunktfunkStatus punktfunk_connection_next_hdr_meta(PunktfunkConnection *c,
                                                   PunktfunkHdrMeta *out,
                                                   uint32_t timeout_ms);
#endif

#if defined(PUNKTFUNK_FEATURE_QUIC)
// Read the session's resolved colour signalling + encode bit depth (from the host's Welcome).
// Each out pointer is filled when non-NULL: `primaries`/`transfer`/`matrix` are CICP code points
// (BT.709 = 1; BT.2020 = 9; PQ transfer = 16, HLG = 18; BT.2020-NCL matrix = 9), `full_range` is
// 0 (limited) or 1 (full), `bit_depth` is 8 or 10. A `transfer` of 16/18 means HDR — configure an
// HDR present path and drain [`punktfunk_connection_next_hdr_meta`]. Available immediately after a
// successful connect (these don't change without a reconfigure).
//
// # Safety
// `c` is a valid connection handle; each out pointer is NULL or writable for its scalar.
PunktfunkStatus punktfunk_connection_color_info(PunktfunkConnection *c,
                                                uint8_t *primaries,
                                                uint8_t *transfer,
                                                uint8_t *matrix,
                                                uint8_t *full_range,
                                                uint8_t *bit_depth);
#endif

#if defined(PUNKTFUNK_FEATURE_QUIC)
// Read the session's resolved chroma subsampling (from the host's Welcome) as the HEVC
// `chroma_format_idc`: `1` = 4:2:0 (the default every pre-4:4:4 host produced), `3` = full-chroma
// 4:4:4. `*out` is filled when non-NULL. The in-band SPS is authoritative; this lets the embedder
// pre-size its decoder / pick a 4:4:4 pixel format up front. Available immediately after a
// successful connect (it doesn't change without a reconfigure).
//
// # Safety
// `c` is a valid connection handle; `out` is NULL or writable for one `u8`.
PunktfunkStatus punktfunk_connection_chroma_format(PunktfunkConnection *c, uint8_t *out);
#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)
// Send one Opus mic frame to the host as a QUIC datagram (48 kHz; the host decodes it into a
// virtual microphone source its apps can record). Non-blocking enqueue; the host uses `seq`/
// `pts_ns` (the caller's own counters) only for diagnostics. `opus_data`/`len` may be empty
// (a DTX silence frame). The data is copied; the caller may reuse the buffer after this returns.
//
// # Safety
// `c` is a valid connection handle; `opus_data` is valid for `len` bytes (or `len == 0`).
PunktfunkStatus punktfunk_connection_send_mic(PunktfunkConnection *c,
                                              const uint8_t *opus_data,
                                              uintptr_t len,
                                              uint32_t seq,
                                              uint64_t pts_ns);
#endif

#if defined(PUNKTFUNK_FEATURE_QUIC)
// Send one rich input event (DualSense touchpad contact or motion sample) to the host as a QUIC
// datagram (non-blocking enqueue). The host applies it to its virtual DualSense pad — a no-op
// unless the host runs the DualSense gamepad backend. [`PunktfunkStatus::InvalidArg`] on an
// unknown `kind`.
//
// # Safety
// `c` is a valid connection handle; `rich` points to a valid [`PunktfunkRichInput`].
PunktfunkStatus punktfunk_connection_send_rich_input(PunktfunkConnection *c,
                                                     const PunktfunkRichInput *rich);
#endif

#if defined(PUNKTFUNK_FEATURE_QUIC)
// Send a rich client→host input via the forward-compatible [`PunktfunkRichInputEx`] — the only way
// a C client can emit a `TouchpadEx` (a second trackpad / signed coords / pressure). Set
// `rich->struct_size = sizeof(PunktfunkRichInputEx)`; a smaller (older-layout) value is rejected.
//
// # Safety
// `c` is a valid connection handle; `rich` is null or points to at least its declared
// `struct_size` bytes.
PunktfunkStatus punktfunk_connection_send_rich_input2(PunktfunkConnection *c,
                                                      const PunktfunkRichInputEx *rich);
#endif

#if defined(PUNKTFUNK_FEATURE_QUIC)
// The currently active session mode — the Welcome's, until an accepted
// [`punktfunk_connection_request_mode`] switches it. 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)
// The virtual gamepad backend the host actually resolved for this session (one of the
// `PUNKTFUNK_GAMEPAD_*` values; the `Welcome`'s echo of the [`punktfunk_connect_ex2`]
// preference). `PUNKTFUNK_GAMEPAD_AUTO` = an older host that didn't say — assume X-Box 360,
// no HID-output feedback. Safe any time after connect.
//
// # Safety
// `c` is a valid connection handle; `gamepad` is writable (NULL is skipped).
PunktfunkStatus punktfunk_connection_gamepad(const PunktfunkConnection *c, uint32_t *gamepad);
#endif

#if defined(PUNKTFUNK_FEATURE_QUIC)
// The compositor backend the host actually resolved for this session (one of the
// `PUNKTFUNK_COMPOSITOR_*` values; the `Welcome`'s echo of the [`punktfunk_connect_ex`]
// preference). `PUNKTFUNK_COMPOSITOR_AUTO` = an older host that didn't say. Clients use it for
// compositor-specific behavior — e.g. a client-side cursor by default on
// `PUNKTFUNK_COMPOSITOR_GAMESCOPE`, whose PipeWire capture carries no cursor. Safe any time after
// connect.
//
// # Safety
// `c` is a valid connection handle; `compositor` is writable (NULL is skipped).
PunktfunkStatus punktfunk_connection_compositor(const PunktfunkConnection *c, uint32_t *compositor);
#endif

#if defined(PUNKTFUNK_FEATURE_QUIC)
// The video encoder bitrate (kilobits per second) the host actually configured for this session
// — the [`punktfunk_connect_ex3`] request clamped to the host's range, or its default when `0`
// was requested. `0` = an older host that didn't report it. Safe any time after connect.
//
// # Safety
// `c` is a valid connection handle; `bitrate_kbps` is writable (NULL is skipped).
PunktfunkStatus punktfunk_connection_bitrate(const PunktfunkConnection *c, uint32_t *bitrate_kbps);
#endif

#if defined(PUNKTFUNK_FEATURE_QUIC)
// The host↔client wall-clock offset (nanoseconds, **host minus client**) measured by the
// connect-time skew handshake. Add it to a local receive/present timestamp (same realtime clock,
// `CLOCK_REALTIME` / `gettimeofday`-epoch nanoseconds) to express that instant in the host's
// capture clock — the clock the per-access-unit `pts_ns` is stamped in — so glass-to-glass latency
// (e.g. present-time minus `pts_ns`) is valid across machines. `0` = no correction: either an older
// host that didn't answer the handshake, or genuinely synchronized clocks. Safe any time after
// connect.
//
// # Safety
// `c` is a valid connection handle; `offset_ns` is writable (NULL is skipped).
PunktfunkStatus punktfunk_connection_clock_offset_ns(const PunktfunkConnection *c,
                                                     int64_t *offset_ns);
#endif

#if defined(PUNKTFUNK_FEATURE_QUIC)
// Ask the host to switch the live session to `width`x`height`@`refresh_hz` without
// reconnecting (window resized, refresh changed). Non-blocking enqueue: on acceptance the
// stream continues at the new mode — the first new-mode access unit is an IDR with
// in-band parameter sets (rebuild the decoder from it) — and
// [`punktfunk_connection_mode`] reflects the switch. A rejected request leaves the
// session unchanged.
//
// # Safety
// `c` is a valid connection handle.
PunktfunkStatus punktfunk_connection_request_mode(const PunktfunkConnection *c,
                                                  uint32_t width,
                                                  uint32_t height,
                                                  uint32_t refresh_hz);
#endif

#if defined(PUNKTFUNK_FEATURE_QUIC)
// Ask the host's encoder to emit a fresh IDR keyframe now — client recovery when the
// decoder has stalled (the infinite-GOP stream sends one opening IDR then P-frames only, so
// a wedged decoder would otherwise freeze until the next loss-triggered recovery keyframe).
// Non-blocking, fire-and-forget; the recovered keyframe is the only ack. The caller should
// THROTTLE — the decode stays wedged for several frames until the IDR lands, so requesting
// every frame would flood the control stream.
//
// # Safety
// `c` is a valid connection handle.
PunktfunkStatus punktfunk_connection_request_keyframe(const PunktfunkConnection *c);
#endif

#if defined(PUNKTFUNK_FEATURE_QUIC)
// Cumulative access units the host→client reassembler dropped as unrecoverable (FEC couldn't
// rebuild them). A video loop polls this and calls [`punktfunk_connection_request_keyframe`]
// when it climbs — the correct loss trigger under the host's infinite GOP, where unrecoverable
// loss yields reference-missing delta frames the decoder *silently conceals* (frozen / garbage
// picture, no decode error), so a decode-error trigger rarely fires. Monotonic for the session;
// compare against the last observed value. Writes 0 to `out` on a NULL connection.
//
// # Safety
// `c` is a valid connection handle; `out` is writable (NULL is skipped).
PunktfunkStatus punktfunk_connection_frames_dropped(const PunktfunkConnection *c, uint64_t *out);
#endif

#if defined(PUNKTFUNK_FEATURE_QUIC)
// Start a bandwidth speed test: ask the host to burst filler over the data plane at
// `target_kbps` of goodput for `duration_ms` (each clamped host-side to ≤ 3 Gbps / ≤ 5 s),
// *briefly pausing video*. Non-blocking — poll [`punktfunk_connection_probe_result`] until its
// `done` field is 1. Starting a probe resets any prior measurement.
//
// # Safety
// `c` is a valid connection handle.
PunktfunkStatus punktfunk_connection_speed_test(const PunktfunkConnection *c,
                                                uint32_t target_kbps,
                                                uint32_t duration_ms);
#endif

#if defined(PUNKTFUNK_FEATURE_QUIC)
// Read the current speed-test measurement into `*out` (partial until `out->done == 1`). Safe to
// poll repeatedly after [`punktfunk_connection_speed_test`]; before any probe it reports zeros.
//
// # Safety
// `c` is a valid connection handle; `out` is writable for one `PunktfunkProbeResult` (NULL is an
// error).
PunktfunkStatus punktfunk_connection_probe_result(const PunktfunkConnection *c,
                                                  PunktfunkProbeResult *out);
#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 */
