v0.5.0 ccce658

scene.go RawHistoryFolder

1package ports
2
3import (
4	"image"
5	"math"
6	"os"
7	"slices"
8)
9
10// Scene carries core → renderer immutable snapshots with fresh Windows slices,
11// one per output. Rects and the output size are logical and local to the
12// output; the renderer multiplies by Scale.
13type Scene struct {
14	// Security is the owner epoch captured when this scene was published.
15	// Output owners reject stale scenes rather than showing a queued desktop
16	// or accepting old locker buffers across acquisition/release.
17	Security                  SecurityState
18	Output                    string
19	Seq                       uint64
20	OutputWidth, OutputHeight int
21	Scale                     float64
22	// Transform is how the output target holds this scene
23	// (wl_output.transform); 0 for capture child scenes. OutputWidth and
24	// OutputHeight are already in the transformed (logical) orientation.
25	Transform BufferTransform
26	// Off turns the display off (output power management): nothing is
27	// drawn until a scene without it.
28	Off        bool
29	Background string
30	Border     Border
31	// WorkspaceClip bounds workspace windows, their popups and separators
32	// in logical output coordinates. Zero inherits the full output. Layers
33	// remain output-wide; overview previews do not use this clip.
34	WorkspaceClip Rect
35	// Dim darkens the background, bottom layers and tiles (tile lines
36	// included) inside WorkspaceClip when set, with black at this opacity,
37	// 0 to 1, under the first
38	// visible float. 0 draws nothing.
39	Dim float64
40	// DimBehind draws the Dim veil under every window instead, right over
41	// the background and bottom layers (the overview).
42	DimBehind bool
43	Windows   []SceneWindow
44	// Separators are the lines between windows, drawn in slice order with
45	// the Border colors: tile lines over the tiles, under the floats; a
46	// float's border right after the float.
47	Separators []Separator
48	// DropHints show where a dragged tile lands, filled with Border.Active
49	// over the windows of the workspace, under the top layers. Only the
50	// output under the pointer has them, during a drag.
51	DropHints []Rect
52	// Layers are the shown layer surfaces, drawn in slice order: background
53	// and bottom before windows, top and overlay after. Core leaves out
54	// those a fullscreen window hides (all but the background and a surface
55	// taking the keyboard exclusively); adapters draw every layer given.
56	// Window popups are drawn after the windows, layer popups
57	// (SceneWindow.OverLayers) last, over every layer.
58	Layers []SceneLayer
59	// Capture is the capture state of this output: exclusion and hidden
60	// workspace; nil while nothing is captured (capture.go).
61	Capture *SceneCapture
62	// CaptureScene is the workspace captured off screen (Capture.Workspace)
63	// drawn for capture only: a workspace that is not on screen. Only the
64	// root scene carries it; CaptureScene.Capture and .CaptureScene are nil.
65	CaptureScene *Scene
66	// CaptureIndicators are the marks of live captures of this output,
67	// drawn over everything; nil when none. Captures never hold them: the
68	// capture pipeline serves requests from a scene without them.
69	CaptureIndicators []CaptureIndicator
70}
71
72// SameAs reports whether o draws exactly what s draws: every field other
73// than Seq is equal. A scene carrying a CaptureScene is never the same
74// (conservative: the capture image is not compared). Add every new Scene
75// field here; TestSceneSameAsCoversEveryField fails until the field count
76// is updated.
77func (s Scene) SameAs(o Scene) bool {
78	if s.Security != o.Security || s.Output != o.Output ||
79		s.OutputWidth != o.OutputWidth || s.OutputHeight != o.OutputHeight ||
80		s.Scale != o.Scale || s.Transform != o.Transform || s.Off != o.Off ||
81		s.Background != o.Background || s.Border != o.Border ||
82		s.WorkspaceClip != o.WorkspaceClip || s.Dim != o.Dim || s.DimBehind != o.DimBehind {
83		return false
84	}
85	if s.CaptureScene != nil || o.CaptureScene != nil {
86		return false
87	}
88	if (s.Capture == nil) != (o.Capture == nil) {
89		return false
90	}
91	if s.Capture != nil {
92		a, b := *s.Capture, *o.Capture
93		if a.Shown != b.Shown || a.Session != b.Session || a.Revision != b.Revision ||
94			a.Workspace != b.Workspace || a.Window != b.Window || !slices.Equal(a.Excluded, b.Excluded) {
95			return false
96		}
97	}
98	return slices.Equal(s.Windows, o.Windows) && slices.Equal(s.Separators, o.Separators) &&
99		slices.Equal(s.DropHints, o.DropHints) && slices.Equal(s.Layers, o.Layers) &&
100		slices.Equal(s.CaptureIndicators, o.CaptureIndicators)
101}
102
103// Shows reports whether the scene draws the surface of id: only its
104// content changes need a new frame. A window scrolled off the output is
105// not drawn. WorkspaceClip also excludes off-viewport windows; OverLayers
106// popups remain output-wide.
107func (s Scene) Shows(id WindowID) bool {
108	for _, w := range s.Windows {
109		if w.ID == id && !w.Hidden && w.Rect.Overlaps(Rect{W: s.OutputWidth, H: s.OutputHeight}) &&
110			(w.OverLayers || s.WorkspaceClip == (Rect{}) || w.Rect.Overlaps(s.WorkspaceClip)) {
111			return true
112		}
113	}
114	for _, l := range s.Layers {
115		if l.ID == id {
116			return true
117		}
118	}
119	return false
120}
121
122// Border carries the window border style; colors are #rrggbb, "" skips
123// drawing. Width is in logical pixels.
124type Border struct {
125	Width            int
126	Active, Inactive string
127}
128
129// Separator is a line between windows, in logical pixels. Active lines
130// mark the focused window and use Border.Active, others Border.Inactive.
131// Window is the float whose border it is; 0 for lines between tiles.
132type Separator struct {
133	Rect   Rect
134	Active bool
135	Window WindowID
136}
137
138// SceneLayer carries core → renderer layer surface placement.
139type SceneLayer struct {
140	ID    WindowID
141	Layer Layer
142	Rect  Rect
143}
144
145// SurfaceColor is the committed encoding of a surface. Zero means sRGB.
146// PQ+BT.2020 and extended-linear sRGB are color-managed; zero is legacy
147// sRGB. MaxCLL/MaxFALL are in nits. YUV metadata is per surface commit.
148type SurfaceColor struct {
149	TF, Primaries   uint8
150	MaxCLL, MaxFALL uint16
151	// Coefficients/range follow wp_color_representation_v1 (zero =
152	// unspecified, for YUV interpreted as BT.709 limited). Chroma is a
153	// H.273 Chroma420SampleLocType + 1 (zero defaults to type 0).
154	Coefficients, Range, Chroma uint8
155}
156
157const (
158	ColorTFPQ             uint8 = 11 // wp_color_manager_v1 ST2084 PQ
159	ColorTFExtendedLinear uint8 = 5  // wp_color_manager_v1 ext_linear
160	ColorPrimariesBT2020  uint8 = 6  // wp_color_manager_v1 BT.2020
161	ColorPrimariesSRGB    uint8 = 1
162	ColorTFSRGB           uint8 = 9 // wp_color_manager_v1 sRGB
163)
164
165func (c SurfaceColor) IsPQ2020() bool {
166	return c.TF == ColorTFPQ && c.Primaries == ColorPrimariesBT2020
167}
168
169func (c SurfaceColor) IsExtendedLinear() bool {
170	return c.TF == ColorTFExtendedLinear && c.Primaries == ColorPrimariesSRGB
171}
172
173// ContentType is the committed wp_content_type_v1 hint.
174type ContentType uint8
175
176const (
177	ContentNone ContentType = iota
178	ContentPhoto
179	ContentVideo
180	ContentGame
181)
182
183// SurfaceContent carries wayland → output the latest committed content of a
184// window: SHM, a client shared-memory buffer, DMABuf, a GPU buffer, or
185// Solid, a single-pixel buffer. Renderers read them in place and never modify
186// them. Empty means the window
187// has no content. LogicalW and LogicalH are the surface size in logical
188// pixels (buffer scale and viewport applied). Subsurfaces come in Children;
189// Geometry is the part of the surface that is the window (xdg window
190// geometry), the rest being client shadows.
191type SurfaceContent struct {
192	ID          WindowID
193	ContentType ContentType
194	// Surface is a never-reused wl_surface identity; Version identifies its
195	// applied buffer content independently of the window publication Seq.
196	Surface, Version uint64
197	// Seq counts the window's contents (wayland sets it), for release.
198	Seq                uint64
199	Width, Height      int
200	LogicalW, LogicalH int
201	// Source is the committed crop in buffer pixels: x, y, width, height.
202	// Zero width means the entire buffer is used.
203	Source [4]float32
204	// Transform is the wl_output.transform the client applied to the
205	// buffer: readers apply its inverse. Width and Height stay the buffer's.
206	Transform BufferTransform
207	// Opaque ignores the alpha byte: an x format, or an opaque region that
208	// covers the whole surface. It is false while Fade is set.
209	Opaque bool
210	// OpaqueRect is the largest rect of the opaque region inside a surface
211	// that is not wholly Opaque, in logical pixels from the surface origin:
212	// the pixels there ignore alpha too, so the surfaces below it are hidden
213	// by it. Empty when there is none or while Fade is set.
214	OpaqueRect Rect
215	// Fade is how much wp_alpha_modifier_v1 fades the surface: 0 none,
216	// 1 invisible. Its opacity is multiplied by 1-Fade.
217	Fade   float32
218	Color  SurfaceColor
219	SHM    *SHMBuffer
220	DMABuf *DMABuf
221	// Solid is a single-pixel buffer (Width and Height are 1): the surface is
222	// one flat color.
223	Solid *SolidColor
224	// Children are the subsurfaces, bottom to top, flattened.
225	Children []Subsurface
226	// Geometry is in logical pixels from the surface origin; empty means
227	// the whole surface.
228	Geometry Rect
229	// Async asks for tearing presentation (wp_tearing_control_v1); outputs
230	// honour it only in direct scanout.
231	Async bool
232	// Acquire is the explicit-sync fence of this content's root buffer
233	// (wp_linux_drm_syncobj_v1): readers wait on it before reading the
234	// buffer, instead of the buffer's implicit fences. Wayland owns it and
235	// closes it when the buffer is released; a reader that keeps it past
236	// the call duplicates it under Acquire.SyscallConn. nil: implicit sync.
237	// Wayland closes it only after every output reported a later content of
238	// the window as seen, so a reader never gets it already closed.
239	Acquire *os.File
240	// DamageHistory is what changed in the root surface's buffer over the
241	// window's last contents, oldest first, ending with this one (Seq).
242	// A renderer holding an older content redraws the union of the
243	// entries after it, or everything when the history does not reach it.
244	DamageHistory []SeqDamage
245}
246
247// BufferTransform is a wl_output.transform value: bit 0 rotates 90°
248// counter-clockwise, bit 1 rotates 180°, bit 2 flips around the vertical
249// axis first.
250type BufferTransform uint8
251
252// Rotated reports whether width and height swap between buffer and surface.
253func (t BufferTransform) Rotated() bool { return t&1 != 0 }
254
255// Size returns the size of a w×h space seen through t: width and height swap
256// when t is Rotated.
257func (t BufferTransform) Size(w, h int) (int, int) {
258	if t.Rotated() {
259		return h, w
260	}
261	return w, h
262}
263
264// ToBuffer maps a point of a w×h surface (logical, before scale) to the
265// same point of its buffer, in the buffer's untransformed w'×h' axes, where
266// w' and h' swap when the transform is Rotated.
267func (t BufferTransform) ToBuffer(x, y, w, h float64) (float64, float64) {
268	if t&4 != 0 {
269		x = w - x
270	}
271	switch t & 3 {
272	case 1:
273		return y, w - x
274	case 2:
275		return w - x, h - y
276	case 3:
277		return h - y, x
278	}
279	return x, y
280}
281
282// Invert is the transform that maps target points back: for a w×h scene and
283// its w'×h' target, t.Invert().ToBuffer(t.ToBuffer(p, w, h), w', h') == p.
284func (t BufferTransform) Invert() BufferTransform {
285	if t&1 != 0 && t&4 == 0 {
286		return t ^ 2
287	}
288	return t
289}
290
291// RectToBuffer maps a rectangle of a w×h surface to the buffer. Integer
292// rectangles stay integer under these transforms; rounding absorbs float noise.
293func (t BufferTransform) RectToBuffer(r image.Rectangle, w, h int) image.Rectangle {
294	ax, ay := t.ToBuffer(float64(r.Min.X), float64(r.Min.Y), float64(w), float64(h))
295	bx, by := t.ToBuffer(float64(r.Max.X), float64(r.Max.Y), float64(w), float64(h))
296	return image.Rect(int(math.Round(ax)), int(math.Round(ay)), int(math.Round(bx)), int(math.Round(by)))
297}
298
299// SeqDamage is what content Seq changed from the one before: Rects in
300// root buffer pixels, or everything when Full.
301type SeqDamage struct {
302	Seq   uint64
303	Full  bool
304	Rects []Rect
305}
306
307// DamageSince is the union of changes after content seq up to this one,
308// and false when the history does not reach back to seq (redraw all).
309func (c SurfaceContent) DamageSince(seq uint64) ([]Rect, bool) {
310	out, ok := c.AppendDamageSince(nil, seq)
311	if !ok {
312		return nil, false
313	}
314	return out, true
315}
316
317// AppendDamageSince is DamageSince appending to dst, so a caller reusing
318// its slice allocates nothing. When the history does not reach back to seq
319// it returns dst unchanged and false.
320func (c SurfaceContent) AppendDamageSince(dst []Rect, seq uint64) ([]Rect, bool) {
321	if seq >= c.Seq {
322		return dst, true
323	}
324	h := c.DamageHistory
325	if len(h) == 0 || h[0].Seq > seq+1 || h[len(h)-1].Seq != c.Seq {
326		return dst, false
327	}
328	n := len(dst)
329	for _, d := range h {
330		if d.Seq <= seq {
331			continue
332		}
333		if d.Full {
334			return dst[:n], false
335		}
336		dst = append(dst, d.Rects...)
337	}
338	return dst, true
339}
340
341// Subsurface is a child surface at X, Y logical pixels from the root
342// surface origin. Below children are drawn under the root surface.
343type Subsurface struct {
344	X, Y  int
345	Below bool
346	SurfaceContent
347}
348
349// HasBuffer reports whether the surface itself carries a client buffer.
350func (c SurfaceContent) HasBuffer() bool {
351	return c.SHM != nil || c.DMABuf != nil || c.Solid != nil
352}
353
354// SolidColor is the color of a single-pixel buffer: premultiplied by alpha,
355// 0 to 1, sRGB-encoded like the pixels of client buffers.
356type SolidColor struct{ R, G, B, A float32 }
357
358// Empty reports whether the content has nothing to draw.
359func (c SurfaceContent) Empty() bool {
360	return !c.HasBuffer() && len(c.Children) == 0
361}
362
363// SHMBuffer is a client wl_shm buffer: B8G8R8A8 pixels (argb8888/xrgb8888
364// little-endian) at Offset in the pool File, Stride bytes per row. The
365// client keeps writing the file, so renderers copy from it when they draw.
366// Pool is unique per wl_shm pool for the session: renderers map a pool
367// once. File stays open while the client's buffer exists and follows the
368// DMABuf rules: duplicate it under File.SyscallConn before keeping it. A
369// client may shrink the file at any time, so readers must survive faults.
370type SHMBuffer struct {
371	Pool           uint64
372	File           *os.File
373	Offset, Stride int
374}
375
376// DMABuf is a client GPU buffer (linux-dmabuf). Its files stay open while
377// the client's buffer exists; a renderer that keeps it must duplicate the
378// descriptors under File.SyscallConn so a concurrent close cannot hand it a
379// reused one. ID is unique per buffer for the whole session: renderers key
380// their imports on it.
381type DMABuf struct {
382	ID            uint64
383	Width, Height int
384	Format        uint32 // DRM fourcc
385	Modifier      uint64
386	Planes        []DMABufPlane
387}
388
389// DMABufPlane is one plane of a DMABuf.
390type DMABufPlane struct {
391	File           *os.File
392	Offset, Stride uint32
393}
394
395// DMABufFormat is a DRM fourcc with a modifier the renderer can import.
396type DMABufFormat struct {
397	Format   uint32
398	Modifier uint64
399}
400
401// DMABufSupport is what the renderer imports: the render device (a dev_t,
402// 0 when unknown) and the formats. No formats means no dmabuf.
403type DMABufSupport struct {
404	Device  uint64
405	Formats []DMABufFormat
406}
407
408// SceneWindow carries core → renderer window placement.
409type SceneWindow struct {
410	ID                          WindowID
411	Rect                        Rect
412	Focused, Fullscreen, Hidden bool
413	// Inset sides carry a separator line inside Rect: the client is drawn
414	// inside it.
415	Inset Sides
416	// Floating windows draw at their placed size. Below marks a native
417	// covering float behind the column group.
418	Floating, Below bool
419	// Dim darkens the window, border included, with black at this
420	// opacity, 0 to 1: a stashed window peeking in. 0 draws nothing.
421	Dim float64
422	// Fade makes the whole window translucent, border and content: 0 is
423	// opaque, 1 invisible (like SurfaceContent.Fade). A window appearing
424	// or leaving animates it; a faded window is composed, never scanned
425	// out.
426	Fade float64
427	// FocusEffect, above 0, is the focus indicator's effect at this frame
428	// (focus.animation, focus.effect): the window's surfaces are lifted
429	// toward white by that fraction (screen blend). Renderers without the
430	// effect ignore it.
431	//
432	// Only focus.effect = screen exists, so the effect kind is not carried:
433	// core ignores Config.Focus.Effect and the shaders always apply screen
434	// (compose.frag, compose_hdr.frag, push constant mapy.z). A second effect
435	// needs its kind here and in the push constants (a misc flag).
436	FocusEffect float64
437	// Preview, above 0, draws the window's surfaces that much smaller in
438	// Rect (an overview thumbnail): the client keeps its size. A preview
439	// is a card: it never opens the floats.
440	Preview float64
441	// Zoom, above 0, is the content scale of a window whose frame is
442	// animating (appearing, leaving, an overview card in flight): it
443	// replaces Preview for drawing only; the window keeps its kind.
444	Zoom float64
445	// Popups are drawn from their content only: no border, no background.
446	Popup bool
447	// OverLayers popups hang from a layer surface: drawn over the top and
448	// overlay layers. Window popups stay with the windows, under them.
449	OverLayers bool
450}

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 23

Tags 10

↑↓ moveenter openesc close