//! The scanout wire protocol — what the compositor says to a native scanout driver (e.g.
//! virtio-gpu) over `/protocol/scanout` to put a composited frame on screen. The driver owns
//! the panel and the shared scanout surface it handed the compositor (via the display
//! service's `attach_scanout`); the compositor composites into that surface, then asks the
//! driver to present a damaged rectangle. Tiny by design — one present request. Separate from
//! the display protocol because the directions differ: clients call the compositor over
//! `/protocol/display`; the compositor calls the driver over `/protocol/scanout`. See
//! docs/display-v2.md.
//!
//! One scanout per driver instance, so `Header.target` is always 0.

const envelope = @import("envelope");

/// `present(rect)`: put the given rectangle of the shared scanout surface on the panel (on
/// virtio-gpu: transfer-to-host of the region, then a fenced resource flush).
pub const Present = extern struct {
    x: u32 = 0,
    y: u32 = 0,
    width: u32 = 0,
    height: u32 = 0,
};

/// `set_mode(width, height)`: change the scanout resolution — the shared surface is sized to
/// the largest mode, so this just re-points the scanout rectangle; the surface is unchanged.
pub const SetMode = extern struct { width: u32, height: u32 };

/// One offered display mode.
pub const Mode = extern struct { width: u32, height: u32 };
pub const max_modes = 4;

/// The answer to `get_modes`: a small fixed list of modes. The success/failure verdict is
/// the reply's `Status`, so this carries only the modes.
pub const Modes = extern struct {
    count: u32 = 0,
    _padding: u32 = 0,
    modes: [max_modes]Mode = @splat(.{ .width = 0, .height = 0 }),
};

pub const Protocol = envelope.Define(.{
    .name = "scanout",
    .version = 1,
    .operations = &.{
        .{ .name = "present", .request = Present },
        .{ .name = "get_modes", .reply = Modes },
        .{ .name = "set_mode", .request = SetMode },
    },
});

pub const Operation = Protocol.Operation;

/// The call floor, like every synchronous protocol. This module used to declare
/// 64 — the *push* floor — which was simply wrong: nothing here is pushed, and a
/// provider sizing its receive buffer to 64 refuses (`-E2BIG`) any caller that
/// sends up to the floor it is entitled to.
pub const message_maximum: usize = Protocol.message_maximum;
