1// Package ports defines framework-independent capture contracts.
2package ports
3
4import (
5 "context"
6 "io"
7 "time"
8)
9
10// Output IDs are stable only within a Source connection. Name is wl_output.name.
11type Output struct {
12 ID uint32
13 Name string
14 Width, Height int // physical mode dimensions, informational
15 Scale int
16}
17
18// Region uses output-local logical coordinates. Zero value means the full output.
19type Region struct{ X, Y, Width, Height int }
20
21type Target struct {
22 OutputID uint32
23 Region Region
24 // WorkspaceID is a Source-local identity from Source.Workspaces, never an
25 // index or monitor alias. Nonzero requires a zero Region. A workspace
26 // that is not displayed can be captured only if the compositor supports
27 // it; otherwise it is captured as its output while Active.
28 WorkspaceID uint64
29}
30
31// Capabilities says what the compositor behind a Source offers beyond
32// capturing whole outputs. It is fixed once the Source is created.
33type Capabilities struct {
34 // Workspaces: Source.Workspaces lists the compositor's workspaces.
35 Workspaces bool
36 // Exclusion: a client's own layer surfaces can be left out of its frames,
37 // so a recording indicator can be shown without appearing in the video.
38 Exclusion bool
39}
40
41// Workspace is compositor workspace metadata. Region is output-local, logical
42// geometry (the whole output). Active means this workspace is displayed.
43type Workspace struct {
44 ID uint64
45 OutputID uint32
46 Name string
47 Region Region
48 Active bool
49}
50
51// PixelFormat names the native little-endian wl_shm 32-bit pixel layout.
52type PixelFormat uint32
53
54const (
55 ARGB8888 PixelFormat = 0
56 XRGB8888 PixelFormat = 1
57)
58
59// Frame borrows storage from Source until the next Capture or Close call.
60// Consumers must finish reading synchronously and must not retain Pixels.
61// Output is opaque SDR.
62type Frame struct {
63 Pixels []byte
64 Width, Height, Stride int
65 Format PixelFormat
66}
67
68// Source has one sequential owner; cancellation must interrupt blocked I/O.
69// Cancellation errors wrap ctx.Err(), not a custom cancellation cause.
70// A failed Capture invalidates its borrowed frame. Close is idempotent.
71type Source interface {
72 Capabilities() Capabilities
73 Outputs(context.Context) ([]Output, error)
74 // Workspaces returns nil without error when the compositor has none.
75 Workspaces(context.Context) ([]Workspace, error)
76 Capture(context.Context, Target) (Frame, error)
77 Close() error
78}
79
80// ScreenshotWriter writes an opaque PNG without overwriting an existing path.
81// Save completes all reads from borrowed frame storage before returning.
82type ScreenshotWriter interface {
83 Save(context.Context, Frame, string) error
84 // Encode consumes borrowed pixels synchronously without creating a file.
85 Encode(context.Context, io.Writer, Frame) error
86}
87
88// Clipboard accepts PNG bytes synchronously. Its owner may outlive this process.
89type Clipboard interface {
90 Copy(context.Context, io.Reader) error
91}
92
93// VideoSettings describes fixed-rate silent SDR video. Zero output dimensions
94// retain source resolution. Both dimensions must be specified for scaling.
95type VideoSettings struct {
96 FPS int
97 Width, Height int
98}
99
100// VideoWriter has one owner. Start fixes input geometry and reserves a new path.
101// Start does not encode its frame; pass the first frame explicitly to Write.
102// Start rejects input/output geometry that its encoder cannot use; it must not
103// silently crop when zero output dimensions request source resolution.
104// Write consumes borrowed pixels before returning; it must not queue frames.
105// Close drains and finalizes the encoder with a bounded shutdown deadline.
106// Abort as the first terminal operation interrupts writes and discards output.
107// Abort is safe concurrently with Write or a draining Close. During Close it
108// escalates shutdown unless Close already observed encoder exit; Close's result
109// is authoritative. Abort after completed Close is a no-op. Close after Abort
110// returns Abort's cleanup result. Close preserves frames on a clean exit.
111// Cancellation errors wrap ctx.Err(); unrelated failures must be preserved.
112// Write rejects width, height or format changes with ErrGeometryChanged.
113// Stride changes are allowed. Close and Abort are idempotent terminal operations;
114// Write after termination returns ErrClosed. Other methods have one owner.
115type VideoWriter interface {
116 Start(context.Context, Frame, string, VideoSettings) error
117 Write(context.Context, Frame) error
118 Close() error
119 Abort() error
120}
121
122type Mode string
123
124const (
125 Screenshot Mode = "screenshot"
126 Record Mode = "record"
127)
128
129// Selection is returned by a control panel before capture begins.
130type Selection struct {
131 Mode Mode
132 Target Target
133 Path string
134 Clipboard bool // screenshot only; permits an empty Path
135 Video VideoSettings
136 Duration time.Duration // zero records until context cancellation
137}
138
139// Selector must close its surface before returning an accepted selection.
140type Selector interface {
141 Select(context.Context, []Output) (Selection, bool, error)
142}