detached 974d42e

refactor(ports): split client events and surface content into feature files

commit
974d42e5954c84cae3d08f54c11a4a9b519f535b
author
brice · · 2026-10-08 05:31 UTC
parent
b727aa4
signature
GPG VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · arch-gpg Verified for the committer email
7 files · +449 -443
M internal/ports/client_events.go +0 -174
@@ -110,30 +110,6 @@ type WindowActivate struct{ ID WindowID }
110110
111111 func (WindowActivate) clientEvent() {}
112112
113-// WorkspaceActivate requests showing workspaces by stable ID, in commit order.
114-// Core applies the entire batch before publishing a new state.
115-type WorkspaceActivate struct{ IDs []uint64 }
116-
117-func (WorkspaceActivate) clientEvent() {}
118-
119-// Workspaces is an immutable, latest-only inventory of output workspaces.
120-type Workspaces struct{ Outputs []WorkspaceOutput }
121-type WorkspaceOutput struct {
122- Name string
123- Workspaces []WorkspaceInfo
124-}
125-type WorkspaceInfo struct {
126- ID uint64
127- Configured string // configured name; empty for dynamic workspaces. ID, not this, identifies the workspace
128- Name string
129- Index int // zero-based vertical coordinate
130- Active, Hidden bool
131- // Frame is the workspace viewport in output-local logical pixels
132- // (Monitor.Frame geometry: the whole output unless the workspace has a
133- // size override). It is where a capture session of it lies.
134- Frame Rect
135-}
136-
137113 // WindowAppID carries wayland → core an app ID set after the window mapped.
138114 type WindowAppID struct {
139115 ID WindowID
@@ -157,153 +133,3 @@ type WindowResized struct {
157133 }
158134
159135 func (WindowResized) clientEvent() {}
160-
161-// Popup anchors and gravities, as xdg_positioner values.
162-const (
163- EdgeNone uint32 = iota
164- EdgeTop
165- EdgeBottom
166- EdgeLeft
167- EdgeRight
168- EdgeTopLeft
169- EdgeBottomLeft
170- EdgeTopRight
171- EdgeBottomRight
172-)
173-
174-// Popup constraint adjustments, as xdg_positioner values.
175-const (
176- AdjustSlideX uint32 = 1 << iota
177- AdjustSlideY
178- AdjustFlipX
179- AdjustFlipY
180- AdjustResizeX
181- AdjustResizeY
182-)
183-
184-// Positioner places a popup relative to its parent's window geometry:
185-// the Anchor point of AnchorRect, moved by Offset, with the popup extending
186-// towards Gravity. Adjust says how core may move it to stay on screen.
187-type Positioner struct {
188- Width, Height int
189- AnchorRect Rect
190- Anchor, Gravity uint32
191- OffsetX, OffsetY int
192- Adjust uint32
193-}
194-
195-// PopupRequest carries wayland → core a popup to place (xdg_popup), on
196-// its first commit and on reposition (Reposition, with the client's
197-// Token). Parent is a window or popup. Grab popups take the keyboard and
198-// close on a click elsewhere.
199-type PopupRequest struct {
200- ID, Parent WindowID
201- Positioner Positioner
202- Grab bool
203- Reposition bool
204- Token uint32
205-}
206-
207-func (PopupRequest) clientEvent() {}
208-
209-// PopupMapped carries wayland → core a placed popup that now has content.
210-// It ends with WindowUnmapped.
211-type PopupMapped struct{ ID WindowID }
212-
213-func (PopupMapped) clientEvent() {}
214-
215-// Layer is a wlr-layer-shell stacking layer.
216-type Layer uint32
217-
218-const (
219- LayerBackground Layer = iota
220- LayerBottom
221- LayerTop
222- LayerOverlay
223-)
224-
225-// Anchor edges, as in zwlr_layer_surface_v1.anchor.
226-const (
227- AnchorTop uint32 = 1
228- AnchorBottom uint32 = 2
229- AnchorLeft uint32 = 4
230- AnchorRight uint32 = 8
231-)
232-
233-// LayerSurface is the committed state of one mapped layer surface. Width and
234-// Height are the committed surface size in logical pixels. Margin is top, right, bottom, left.
235-// IDs share the WindowID space with windows and never collide.
236-type LayerSurface struct {
237- ID WindowID
238- Layer Layer
239- Anchor uint32
240- ExclusiveZone int32
241- Margin [4]int32
242- Width, Height int
243- Namespace string
244- // Keyboard is the requested interactivity: 0 none, 1 exclusive, 2 on-demand.
245- Keyboard uint32
246- // Output is the connector the surface is on.
247- Output string
248-}
249-
250-// LayerChanged carries wayland → core the full list of mapped layer surfaces,
251-// sent whenever a layer surface maps, unmaps or commits new layer state.
252-type LayerChanged struct{ Layers []LayerSurface }
253-
254-func (LayerChanged) clientEvent() {}
255-
256-// ConstraintMode is how a pointer constraint holds the pointer.
257-type ConstraintMode uint8
258-
259-const (
260- ConstraintNone ConstraintMode = iota
261- // ConstraintLock keeps the pointer still: only relative motion flows.
262- ConstraintLock
263- // ConstraintConfine keeps the pointer inside Rect.
264- ConstraintConfine
265-)
266-
267-// PointerConstraint is an active pointer constraint. Rect is logical: in
268-// PointerConstrained it is window-local and empty means the whole window;
269-// core sends input the resolved global rectangle, and in X, Y its cursor
270-// position, where input holds a locked pointer. Warp moves input's
271-// pointer to X, Y whatever the mode (wp_pointer_warp_v1).
272-type PointerConstraint struct {
273- Mode ConstraintMode
274- Rect Rect
275- X, Y float64
276- Warp bool
277-}
278-
279-// Clamp keeps a global point inside the rectangle of a lock or confine;
280-// no constraint and an empty rectangle leave it unchanged.
281-func (c PointerConstraint) Clamp(x, y float64) (float64, float64) {
282- if c.Mode == ConstraintNone || c.Rect.W <= 0 || c.Rect.H <= 0 {
283- return x, y
284- }
285- x = min(max(x, float64(c.Rect.X)), float64(c.Rect.X+c.Rect.W-1))
286- y = min(max(y, float64(c.Rect.Y)), float64(c.Rect.Y+c.Rect.H-1))
287- return x, y
288-}
289-
290-// PointerConstrained carries wayland → core the constraint active on a
291-// window (zwp_pointer_constraints_v1); ID 0 means none is active. A
292-// constraint is active only on the window with both pointer and keyboard
293-// focus.
294-type PointerConstrained struct {
295- ID WindowID
296- PointerConstraint
297-}
298-
299-func (PointerConstrained) clientEvent() {}
300-
301-// PointerWarp carries wayland → core a client's request to move the
302-// pointer to X, Y, window-local logical (wp_pointer_warp_v1). Wayland has
303-// checked that the window has the pointer and the point is on it.
304-type PointerWarp struct {
305- ID WindowID
306- X, Y float64
307-}
308-
309-func (PointerWarp) clientEvent() {}
@@ -110,30 +110,6 @@ type WindowActivate struct{ ID WindowID }
110 110
111 func (WindowActivate) clientEvent() {}111 func (WindowActivate) clientEvent() {}
112 112
113-// WorkspaceActivate requests showing workspaces by stable ID, in commit order.
114-// Core applies the entire batch before publishing a new state.
115-type WorkspaceActivate struct{ IDs []uint64 }
116-
117-func (WorkspaceActivate) clientEvent() {}
118-
119-// Workspaces is an immutable, latest-only inventory of output workspaces.
120-type Workspaces struct{ Outputs []WorkspaceOutput }
121-type WorkspaceOutput struct {
122- Name string
123- Workspaces []WorkspaceInfo
124-}
125-type WorkspaceInfo struct {
126- ID uint64
127- Configured string // configured name; empty for dynamic workspaces. ID, not this, identifies the workspace
128- Name string
129- Index int // zero-based vertical coordinate
130- Active, Hidden bool
131- // Frame is the workspace viewport in output-local logical pixels
132- // (Monitor.Frame geometry: the whole output unless the workspace has a
133- // size override). It is where a capture session of it lies.
134- Frame Rect
135-}
136-
137 // WindowAppID carries wayland → core an app ID set after the window mapped.113 // WindowAppID carries wayland → core an app ID set after the window mapped.
138 type WindowAppID struct {114 type WindowAppID struct {
139 ID WindowID115 ID WindowID
@@ -157,153 +133,3 @@ type WindowResized struct {
157 }133 }
158 134
159 func (WindowResized) clientEvent() {}135 func (WindowResized) clientEvent() {}
160-
161-// Popup anchors and gravities, as xdg_positioner values.
162-const (
163- EdgeNone uint32 = iota
164- EdgeTop
165- EdgeBottom
166- EdgeLeft
167- EdgeRight
168- EdgeTopLeft
169- EdgeBottomLeft
170- EdgeTopRight
171- EdgeBottomRight
172-)
173-
174-// Popup constraint adjustments, as xdg_positioner values.
175-const (
176- AdjustSlideX uint32 = 1 << iota
177- AdjustSlideY
178- AdjustFlipX
179- AdjustFlipY
180- AdjustResizeX
181- AdjustResizeY
182-)
183-
184-// Positioner places a popup relative to its parent's window geometry:
185-// the Anchor point of AnchorRect, moved by Offset, with the popup extending
186-// towards Gravity. Adjust says how core may move it to stay on screen.
187-type Positioner struct {
188- Width, Height int
189- AnchorRect Rect
190- Anchor, Gravity uint32
191- OffsetX, OffsetY int
192- Adjust uint32
193-}
194-
195-// PopupRequest carries wayland → core a popup to place (xdg_popup), on
196-// its first commit and on reposition (Reposition, with the client's
197-// Token). Parent is a window or popup. Grab popups take the keyboard and
198-// close on a click elsewhere.
199-type PopupRequest struct {
200- ID, Parent WindowID
201- Positioner Positioner
202- Grab bool
203- Reposition bool
204- Token uint32
205-}
206-
207-func (PopupRequest) clientEvent() {}
208-
209-// PopupMapped carries wayland → core a placed popup that now has content.
210-// It ends with WindowUnmapped.
211-type PopupMapped struct{ ID WindowID }
212-
213-func (PopupMapped) clientEvent() {}
214-
215-// Layer is a wlr-layer-shell stacking layer.
216-type Layer uint32
217-
218-const (
219- LayerBackground Layer = iota
220- LayerBottom
221- LayerTop
222- LayerOverlay
223-)
224-
225-// Anchor edges, as in zwlr_layer_surface_v1.anchor.
226-const (
227- AnchorTop uint32 = 1
228- AnchorBottom uint32 = 2
229- AnchorLeft uint32 = 4
230- AnchorRight uint32 = 8
231-)
232-
233-// LayerSurface is the committed state of one mapped layer surface. Width and
234-// Height are the committed surface size in logical pixels. Margin is top, right, bottom, left.
235-// IDs share the WindowID space with windows and never collide.
236-type LayerSurface struct {
237- ID WindowID
238- Layer Layer
239- Anchor uint32
240- ExclusiveZone int32
241- Margin [4]int32
242- Width, Height int
243- Namespace string
244- // Keyboard is the requested interactivity: 0 none, 1 exclusive, 2 on-demand.
245- Keyboard uint32
246- // Output is the connector the surface is on.
247- Output string
248-}
249-
250-// LayerChanged carries wayland → core the full list of mapped layer surfaces,
251-// sent whenever a layer surface maps, unmaps or commits new layer state.
252-type LayerChanged struct{ Layers []LayerSurface }
253-
254-func (LayerChanged) clientEvent() {}
255-
256-// ConstraintMode is how a pointer constraint holds the pointer.
257-type ConstraintMode uint8
258-
259-const (
260- ConstraintNone ConstraintMode = iota
261- // ConstraintLock keeps the pointer still: only relative motion flows.
262- ConstraintLock
263- // ConstraintConfine keeps the pointer inside Rect.
264- ConstraintConfine
265-)
266-
267-// PointerConstraint is an active pointer constraint. Rect is logical: in
268-// PointerConstrained it is window-local and empty means the whole window;
269-// core sends input the resolved global rectangle, and in X, Y its cursor
270-// position, where input holds a locked pointer. Warp moves input's
271-// pointer to X, Y whatever the mode (wp_pointer_warp_v1).
272-type PointerConstraint struct {
273- Mode ConstraintMode
274- Rect Rect
275- X, Y float64
276- Warp bool
277-}
278-
279-// Clamp keeps a global point inside the rectangle of a lock or confine;
280-// no constraint and an empty rectangle leave it unchanged.
281-func (c PointerConstraint) Clamp(x, y float64) (float64, float64) {
282- if c.Mode == ConstraintNone || c.Rect.W <= 0 || c.Rect.H <= 0 {
283- return x, y
284- }
285- x = min(max(x, float64(c.Rect.X)), float64(c.Rect.X+c.Rect.W-1))
286- y = min(max(y, float64(c.Rect.Y)), float64(c.Rect.Y+c.Rect.H-1))
287- return x, y
288-}
289-
290-// PointerConstrained carries wayland → core the constraint active on a
291-// window (zwp_pointer_constraints_v1); ID 0 means none is active. A
292-// constraint is active only on the window with both pointer and keyboard
293-// focus.
294-type PointerConstrained struct {
295- ID WindowID
296- PointerConstraint
297-}
298-
299-func (PointerConstrained) clientEvent() {}
300-
301-// PointerWarp carries wayland → core a client's request to move the
302-// pointer to X, Y, window-local logical (wp_pointer_warp_v1). Wayland has
303-// checked that the window has the pointer and the point is on it.
304-type PointerWarp struct {
305- ID WindowID
306- X, Y float64
307-}
308-
309-func (PointerWarp) clientEvent() {}
A internal/ports/layer_events.go +42 -0
@@ -0,0 +1,42 @@
1+package ports
2+
3+// Layer is a wlr-layer-shell stacking layer.
4+type Layer uint32
5+
6+const (
7+ LayerBackground Layer = iota
8+ LayerBottom
9+ LayerTop
10+ LayerOverlay
11+)
12+
13+// Anchor edges, as in zwlr_layer_surface_v1.anchor.
14+const (
15+ AnchorTop uint32 = 1
16+ AnchorBottom uint32 = 2
17+ AnchorLeft uint32 = 4
18+ AnchorRight uint32 = 8
19+)
20+
21+// LayerSurface is the committed state of one mapped layer surface. Width and
22+// Height are the committed surface size in logical pixels. Margin is top, right, bottom, left.
23+// IDs share the WindowID space with windows and never collide.
24+type LayerSurface struct {
25+ ID WindowID
26+ Layer Layer
27+ Anchor uint32
28+ ExclusiveZone int32
29+ Margin [4]int32
30+ Width, Height int
31+ Namespace string
32+ // Keyboard is the requested interactivity: 0 none, 1 exclusive, 2 on-demand.
33+ Keyboard uint32
34+ // Output is the connector the surface is on.
35+ Output string
36+}
37+
38+// LayerChanged carries wayland → core the full list of mapped layer surfaces,
39+// sent whenever a layer surface maps, unmaps or commits new layer state.
40+type LayerChanged struct{ Layers []LayerSurface }
41+
42+func (LayerChanged) clientEvent() {}
@@ -0,0 +1,42 @@
1+package ports
2+
3+// Layer is a wlr-layer-shell stacking layer.
4+type Layer uint32
5+
6+const (
7+ LayerBackground Layer = iota
8+ LayerBottom
9+ LayerTop
10+ LayerOverlay
11+)
12+
13+// Anchor edges, as in zwlr_layer_surface_v1.anchor.
14+const (
15+ AnchorTop uint32 = 1
16+ AnchorBottom uint32 = 2
17+ AnchorLeft uint32 = 4
18+ AnchorRight uint32 = 8
19+)
20+
21+// LayerSurface is the committed state of one mapped layer surface. Width and
22+// Height are the committed surface size in logical pixels. Margin is top, right, bottom, left.
23+// IDs share the WindowID space with windows and never collide.
24+type LayerSurface struct {
25+ ID WindowID
26+ Layer Layer
27+ Anchor uint32
28+ ExclusiveZone int32
29+ Margin [4]int32
30+ Width, Height int
31+ Namespace string
32+ // Keyboard is the requested interactivity: 0 none, 1 exclusive, 2 on-demand.
33+ Keyboard uint32
34+ // Output is the connector the surface is on.
35+ Output string
36+}
37+
38+// LayerChanged carries wayland → core the full list of mapped layer surfaces,
39+// sent whenever a layer surface maps, unmaps or commits new layer state.
40+type LayerChanged struct{ Layers []LayerSurface }
41+
42+func (LayerChanged) clientEvent() {}
A internal/ports/pointer_events.go +56 -0
@@ -0,0 +1,56 @@
1+package ports
2+
3+// ConstraintMode is how a pointer constraint holds the pointer.
4+type ConstraintMode uint8
5+
6+const (
7+ ConstraintNone ConstraintMode = iota
8+ // ConstraintLock keeps the pointer still: only relative motion flows.
9+ ConstraintLock
10+ // ConstraintConfine keeps the pointer inside Rect.
11+ ConstraintConfine
12+)
13+
14+// PointerConstraint is an active pointer constraint. Rect is logical: in
15+// PointerConstrained it is window-local and empty means the whole window;
16+// core sends input the resolved global rectangle, and in X, Y its cursor
17+// position, where input holds a locked pointer. Warp moves input's
18+// pointer to X, Y whatever the mode (wp_pointer_warp_v1).
19+type PointerConstraint struct {
20+ Mode ConstraintMode
21+ Rect Rect
22+ X, Y float64
23+ Warp bool
24+}
25+
26+// Clamp keeps a global point inside the rectangle of a lock or confine;
27+// no constraint and an empty rectangle leave it unchanged.
28+func (c PointerConstraint) Clamp(x, y float64) (float64, float64) {
29+ if c.Mode == ConstraintNone || c.Rect.W <= 0 || c.Rect.H <= 0 {
30+ return x, y
31+ }
32+ x = min(max(x, float64(c.Rect.X)), float64(c.Rect.X+c.Rect.W-1))
33+ y = min(max(y, float64(c.Rect.Y)), float64(c.Rect.Y+c.Rect.H-1))
34+ return x, y
35+}
36+
37+// PointerConstrained carries wayland → core the constraint active on a
38+// window (zwp_pointer_constraints_v1); ID 0 means none is active. A
39+// constraint is active only on the window with both pointer and keyboard
40+// focus.
41+type PointerConstrained struct {
42+ ID WindowID
43+ PointerConstraint
44+}
45+
46+func (PointerConstrained) clientEvent() {}
47+
48+// PointerWarp carries wayland → core a client's request to move the
49+// pointer to X, Y, window-local logical (wp_pointer_warp_v1). Wayland has
50+// checked that the window has the pointer and the point is on it.
51+type PointerWarp struct {
52+ ID WindowID
53+ X, Y float64
54+}
55+
56+func (PointerWarp) clientEvent() {}
@@ -0,0 +1,56 @@
1+package ports
2+
3+// ConstraintMode is how a pointer constraint holds the pointer.
4+type ConstraintMode uint8
5+
6+const (
7+ ConstraintNone ConstraintMode = iota
8+ // ConstraintLock keeps the pointer still: only relative motion flows.
9+ ConstraintLock
10+ // ConstraintConfine keeps the pointer inside Rect.
11+ ConstraintConfine
12+)
13+
14+// PointerConstraint is an active pointer constraint. Rect is logical: in
15+// PointerConstrained it is window-local and empty means the whole window;
16+// core sends input the resolved global rectangle, and in X, Y its cursor
17+// position, where input holds a locked pointer. Warp moves input's
18+// pointer to X, Y whatever the mode (wp_pointer_warp_v1).
19+type PointerConstraint struct {
20+ Mode ConstraintMode
21+ Rect Rect
22+ X, Y float64
23+ Warp bool
24+}
25+
26+// Clamp keeps a global point inside the rectangle of a lock or confine;
27+// no constraint and an empty rectangle leave it unchanged.
28+func (c PointerConstraint) Clamp(x, y float64) (float64, float64) {
29+ if c.Mode == ConstraintNone || c.Rect.W <= 0 || c.Rect.H <= 0 {
30+ return x, y
31+ }
32+ x = min(max(x, float64(c.Rect.X)), float64(c.Rect.X+c.Rect.W-1))
33+ y = min(max(y, float64(c.Rect.Y)), float64(c.Rect.Y+c.Rect.H-1))
34+ return x, y
35+}
36+
37+// PointerConstrained carries wayland → core the constraint active on a
38+// window (zwp_pointer_constraints_v1); ID 0 means none is active. A
39+// constraint is active only on the window with both pointer and keyboard
40+// focus.
41+type PointerConstrained struct {
42+ ID WindowID
43+ PointerConstraint
44+}
45+
46+func (PointerConstrained) clientEvent() {}
47+
48+// PointerWarp carries wayland → core a client's request to move the
49+// pointer to X, Y, window-local logical (wp_pointer_warp_v1). Wayland has
50+// checked that the window has the pointer and the point is on it.
51+type PointerWarp struct {
52+ ID WindowID
53+ X, Y float64
54+}
55+
56+func (PointerWarp) clientEvent() {}
A internal/ports/popup_events.go +55 -0
@@ -0,0 +1,55 @@
1+package ports
2+
3+// Popup anchors and gravities, as xdg_positioner values.
4+const (
5+ EdgeNone uint32 = iota
6+ EdgeTop
7+ EdgeBottom
8+ EdgeLeft
9+ EdgeRight
10+ EdgeTopLeft
11+ EdgeBottomLeft
12+ EdgeTopRight
13+ EdgeBottomRight
14+)
15+
16+// Popup constraint adjustments, as xdg_positioner values.
17+const (
18+ AdjustSlideX uint32 = 1 << iota
19+ AdjustSlideY
20+ AdjustFlipX
21+ AdjustFlipY
22+ AdjustResizeX
23+ AdjustResizeY
24+)
25+
26+// Positioner places a popup relative to its parent's window geometry:
27+// the Anchor point of AnchorRect, moved by Offset, with the popup extending
28+// towards Gravity. Adjust says how core may move it to stay on screen.
29+type Positioner struct {
30+ Width, Height int
31+ AnchorRect Rect
32+ Anchor, Gravity uint32
33+ OffsetX, OffsetY int
34+ Adjust uint32
35+}
36+
37+// PopupRequest carries wayland → core a popup to place (xdg_popup), on
38+// its first commit and on reposition (Reposition, with the client's
39+// Token). Parent is a window or popup. Grab popups take the keyboard and
40+// close on a click elsewhere.
41+type PopupRequest struct {
42+ ID, Parent WindowID
43+ Positioner Positioner
44+ Grab bool
45+ Reposition bool
46+ Token uint32
47+}
48+
49+func (PopupRequest) clientEvent() {}
50+
51+// PopupMapped carries wayland → core a placed popup that now has content.
52+// It ends with WindowUnmapped.
53+type PopupMapped struct{ ID WindowID }
54+
55+func (PopupMapped) clientEvent() {}
@@ -0,0 +1,55 @@
1+package ports
2+
3+// Popup anchors and gravities, as xdg_positioner values.
4+const (
5+ EdgeNone uint32 = iota
6+ EdgeTop
7+ EdgeBottom
8+ EdgeLeft
9+ EdgeRight
10+ EdgeTopLeft
11+ EdgeBottomLeft
12+ EdgeTopRight
13+ EdgeBottomRight
14+)
15+
16+// Popup constraint adjustments, as xdg_positioner values.
17+const (
18+ AdjustSlideX uint32 = 1 << iota
19+ AdjustSlideY
20+ AdjustFlipX
21+ AdjustFlipY
22+ AdjustResizeX
23+ AdjustResizeY
24+)
25+
26+// Positioner places a popup relative to its parent's window geometry:
27+// the Anchor point of AnchorRect, moved by Offset, with the popup extending
28+// towards Gravity. Adjust says how core may move it to stay on screen.
29+type Positioner struct {
30+ Width, Height int
31+ AnchorRect Rect
32+ Anchor, Gravity uint32
33+ OffsetX, OffsetY int
34+ Adjust uint32
35+}
36+
37+// PopupRequest carries wayland → core a popup to place (xdg_popup), on
38+// its first commit and on reposition (Reposition, with the client's
39+// Token). Parent is a window or popup. Grab popups take the keyboard and
40+// close on a click elsewhere.
41+type PopupRequest struct {
42+ ID, Parent WindowID
43+ Positioner Positioner
44+ Grab bool
45+ Reposition bool
46+ Token uint32
47+}
48+
49+func (PopupRequest) clientEvent() {}
50+
51+// PopupMapped carries wayland → core a placed popup that now has content.
52+// It ends with WindowUnmapped.
53+type PopupMapped struct{ ID WindowID }
54+
55+func (PopupMapped) clientEvent() {}
M internal/ports/scene.go +1 -269
@@ -1,11 +1,6 @@
11 package ports
22
3-import (
4- "image"
5- "math"
6- "os"
7- "slices"
8-)
3+import "slices"
94
105 // Scene carries core → renderer immutable snapshots with fresh Windows slices,
116 // one per output. Rects and the output size are logical and local to the
@@ -142,269 +137,6 @@ type SceneLayer struct {
142137 Rect Rect
143138 }
144139
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.
148-type 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-
157-const (
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-
165-func (c SurfaceColor) IsPQ2020() bool {
166- return c.TF == ColorTFPQ && c.Primaries == ColorPrimariesBT2020
167-}
168-
169-func (c SurfaceColor) IsExtendedLinear() bool {
170- return c.TF == ColorTFExtendedLinear && c.Primaries == ColorPrimariesSRGB
171-}
172-
173-// ContentType is the committed wp_content_type_v1 hint.
174-type ContentType uint8
175-
176-const (
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.
191-type 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.
250-type BufferTransform uint8
251-
252-// Rotated reports whether width and height swap between buffer and surface.
253-func (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.
257-func (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.
267-func (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.
284-func (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.
293-func (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.
301-type 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).
309-func (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.
320-func (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.
343-type Subsurface struct {
344- X, Y int
345- Below bool
346- SurfaceContent
347-}
348-
349-// HasBuffer reports whether the surface itself carries a client buffer.
350-func (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.
356-type SolidColor struct{ R, G, B, A float32 }
357-
358-// Empty reports whether the content has nothing to draw.
359-func (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.
370-type 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.
381-type 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.
390-type 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.
396-type 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.
403-type DMABufSupport struct {
404- Device uint64
405- Formats []DMABufFormat
406-}
407-
408140 // SceneWindow carries core → renderer window placement.
409141 type SceneWindow struct {
410142 ID WindowID
@@ -1,11 +1,6 @@
1 package ports1 package ports
2 2
3-import (3+import "slices"
4- "image"
5- "math"
6- "os"
7- "slices"
8-)
9 4
10 // Scene carries core → renderer immutable snapshots with fresh Windows slices,5 // Scene carries core → renderer immutable snapshots with fresh Windows slices,
11 // one per output. Rects and the output size are logical and local to the6 // one per output. Rects and the output size are logical and local to the
@@ -142,269 +137,6 @@ type SceneLayer struct {
142 Rect Rect137 Rect Rect
143 }138 }
144 139
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.
148-type 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-
157-const (
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-
165-func (c SurfaceColor) IsPQ2020() bool {
166- return c.TF == ColorTFPQ && c.Primaries == ColorPrimariesBT2020
167-}
168-
169-func (c SurfaceColor) IsExtendedLinear() bool {
170- return c.TF == ColorTFExtendedLinear && c.Primaries == ColorPrimariesSRGB
171-}
172-
173-// ContentType is the committed wp_content_type_v1 hint.
174-type ContentType uint8
175-
176-const (
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.
191-type 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.
250-type BufferTransform uint8
251-
252-// Rotated reports whether width and height swap between buffer and surface.
253-func (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.
257-func (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.
267-func (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.
284-func (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.
293-func (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.
301-type 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).
309-func (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.
320-func (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.
343-type Subsurface struct {
344- X, Y int
345- Below bool
346- SurfaceContent
347-}
348-
349-// HasBuffer reports whether the surface itself carries a client buffer.
350-func (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.
356-type SolidColor struct{ R, G, B, A float32 }
357-
358-// Empty reports whether the content has nothing to draw.
359-func (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.
370-type 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.
381-type 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.
390-type 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.
396-type 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.
403-type DMABufSupport struct {
404- Device uint64
405- Formats []DMABufFormat
406-}
407-
408 // SceneWindow carries core → renderer window placement.140 // SceneWindow carries core → renderer window placement.
409 type SceneWindow struct {141 type SceneWindow struct {
410 ID WindowID142 ID WindowID
A internal/ports/surface_content.go +270 -0
@@ -0,0 +1,270 @@
1+package ports
2+
3+import (
4+ "image"
5+ "math"
6+ "os"
7+)
8+
9+// SurfaceColor is the committed encoding of a surface. Zero means sRGB.
10+// PQ+BT.2020 and extended-linear sRGB are color-managed; zero is legacy
11+// sRGB. MaxCLL/MaxFALL are in nits. YUV metadata is per surface commit.
12+type SurfaceColor struct {
13+ TF, Primaries uint8
14+ MaxCLL, MaxFALL uint16
15+ // Coefficients/range follow wp_color_representation_v1 (zero =
16+ // unspecified, for YUV interpreted as BT.709 limited). Chroma is a
17+ // H.273 Chroma420SampleLocType + 1 (zero defaults to type 0).
18+ Coefficients, Range, Chroma uint8
19+}
20+
21+const (
22+ ColorTFPQ uint8 = 11 // wp_color_manager_v1 ST2084 PQ
23+ ColorTFExtendedLinear uint8 = 5 // wp_color_manager_v1 ext_linear
24+ ColorPrimariesBT2020 uint8 = 6 // wp_color_manager_v1 BT.2020
25+ ColorPrimariesSRGB uint8 = 1
26+ ColorTFSRGB uint8 = 9 // wp_color_manager_v1 sRGB
27+)
28+
29+func (c SurfaceColor) IsPQ2020() bool {
30+ return c.TF == ColorTFPQ && c.Primaries == ColorPrimariesBT2020
31+}
32+
33+func (c SurfaceColor) IsExtendedLinear() bool {
34+ return c.TF == ColorTFExtendedLinear && c.Primaries == ColorPrimariesSRGB
35+}
36+
37+// ContentType is the committed wp_content_type_v1 hint.
38+type ContentType uint8
39+
40+const (
41+ ContentNone ContentType = iota
42+ ContentPhoto
43+ ContentVideo
44+ ContentGame
45+)
46+
47+// SurfaceContent carries wayland → output the latest committed content of a
48+// window: SHM, a client shared-memory buffer, DMABuf, a GPU buffer, or
49+// Solid, a single-pixel buffer. Renderers read them in place and never modify
50+// them. Empty means the window
51+// has no content. LogicalW and LogicalH are the surface size in logical
52+// pixels (buffer scale and viewport applied). Subsurfaces come in Children;
53+// Geometry is the part of the surface that is the window (xdg window
54+// geometry), the rest being client shadows.
55+type SurfaceContent struct {
56+ ID WindowID
57+ ContentType ContentType
58+ // Surface is a never-reused wl_surface identity; Version identifies its
59+ // applied buffer content independently of the window publication Seq.
60+ Surface, Version uint64
61+ // Seq counts the window's contents (wayland sets it), for release.
62+ Seq uint64
63+ Width, Height int
64+ LogicalW, LogicalH int
65+ // Source is the committed crop in buffer pixels: x, y, width, height.
66+ // Zero width means the entire buffer is used.
67+ Source [4]float32
68+ // Transform is the wl_output.transform the client applied to the
69+ // buffer: readers apply its inverse. Width and Height stay the buffer's.
70+ Transform BufferTransform
71+ // Opaque ignores the alpha byte: an x format, or an opaque region that
72+ // covers the whole surface. It is false while Fade is set.
73+ Opaque bool
74+ // OpaqueRect is the largest rect of the opaque region inside a surface
75+ // that is not wholly Opaque, in logical pixels from the surface origin:
76+ // the pixels there ignore alpha too, so the surfaces below it are hidden
77+ // by it. Empty when there is none or while Fade is set.
78+ OpaqueRect Rect
79+ // Fade is how much wp_alpha_modifier_v1 fades the surface: 0 none,
80+ // 1 invisible. Its opacity is multiplied by 1-Fade.
81+ Fade float32
82+ Color SurfaceColor
83+ SHM *SHMBuffer
84+ DMABuf *DMABuf
85+ // Solid is a single-pixel buffer (Width and Height are 1): the surface is
86+ // one flat color.
87+ Solid *SolidColor
88+ // Children are the subsurfaces, bottom to top, flattened.
89+ Children []Subsurface
90+ // Geometry is in logical pixels from the surface origin; empty means
91+ // the whole surface.
92+ Geometry Rect
93+ // Async asks for tearing presentation (wp_tearing_control_v1); outputs
94+ // honour it only in direct scanout.
95+ Async bool
96+ // Acquire is the explicit-sync fence of this content's root buffer
97+ // (wp_linux_drm_syncobj_v1): readers wait on it before reading the
98+ // buffer, instead of the buffer's implicit fences. Wayland owns it and
99+ // closes it when the buffer is released; a reader that keeps it past
100+ // the call duplicates it under Acquire.SyscallConn. nil: implicit sync.
101+ // Wayland closes it only after every output reported a later content of
102+ // the window as seen, so a reader never gets it already closed.
103+ Acquire *os.File
104+ // DamageHistory is what changed in the root surface's buffer over the
105+ // window's last contents, oldest first, ending with this one (Seq).
106+ // A renderer holding an older content redraws the union of the
107+ // entries after it, or everything when the history does not reach it.
108+ DamageHistory []SeqDamage
109+}
110+
111+// BufferTransform is a wl_output.transform value: bit 0 rotates 90°
112+// counter-clockwise, bit 1 rotates 180°, bit 2 flips around the vertical
113+// axis first.
114+type BufferTransform uint8
115+
116+// Rotated reports whether width and height swap between buffer and surface.
117+func (t BufferTransform) Rotated() bool { return t&1 != 0 }
118+
119+// Size returns the size of a w×h space seen through t: width and height swap
120+// when t is Rotated.
121+func (t BufferTransform) Size(w, h int) (int, int) {
122+ if t.Rotated() {
123+ return h, w
124+ }
125+ return w, h
126+}
127+
128+// ToBuffer maps a point of a w×h surface (logical, before scale) to the
129+// same point of its buffer, in the buffer's untransformed w'×h' axes, where
130+// w' and h' swap when the transform is Rotated.
131+func (t BufferTransform) ToBuffer(x, y, w, h float64) (float64, float64) {
132+ if t&4 != 0 {
133+ x = w - x
134+ }
135+ switch t & 3 {
136+ case 1:
137+ return y, w - x
138+ case 2:
139+ return w - x, h - y
140+ case 3:
141+ return h - y, x
142+ }
143+ return x, y
144+}
145+
146+// Invert is the transform that maps target points back: for a w×h scene and
147+// its w'×h' target, t.Invert().ToBuffer(t.ToBuffer(p, w, h), w', h') == p.
148+func (t BufferTransform) Invert() BufferTransform {
149+ if t&1 != 0 && t&4 == 0 {
150+ return t ^ 2
151+ }
152+ return t
153+}
154+
155+// RectToBuffer maps a rectangle of a w×h surface to the buffer. Integer
156+// rectangles stay integer under these transforms; rounding absorbs float noise.
157+func (t BufferTransform) RectToBuffer(r image.Rectangle, w, h int) image.Rectangle {
158+ ax, ay := t.ToBuffer(float64(r.Min.X), float64(r.Min.Y), float64(w), float64(h))
159+ bx, by := t.ToBuffer(float64(r.Max.X), float64(r.Max.Y), float64(w), float64(h))
160+ return image.Rect(int(math.Round(ax)), int(math.Round(ay)), int(math.Round(bx)), int(math.Round(by)))
161+}
162+
163+// SeqDamage is what content Seq changed from the one before: Rects in
164+// root buffer pixels, or everything when Full.
165+type SeqDamage struct {
166+ Seq uint64
167+ Full bool
168+ Rects []Rect
169+}
170+
171+// DamageSince is the union of changes after content seq up to this one,
172+// and false when the history does not reach back to seq (redraw all).
173+func (c SurfaceContent) DamageSince(seq uint64) ([]Rect, bool) {
174+ out, ok := c.AppendDamageSince(nil, seq)
175+ if !ok {
176+ return nil, false
177+ }
178+ return out, true
179+}
180+
181+// AppendDamageSince is DamageSince appending to dst, so a caller reusing
182+// its slice allocates nothing. When the history does not reach back to seq
183+// it returns dst unchanged and false.
184+func (c SurfaceContent) AppendDamageSince(dst []Rect, seq uint64) ([]Rect, bool) {
185+ if seq >= c.Seq {
186+ return dst, true
187+ }
188+ h := c.DamageHistory
189+ if len(h) == 0 || h[0].Seq > seq+1 || h[len(h)-1].Seq != c.Seq {
190+ return dst, false
191+ }
192+ n := len(dst)
193+ for _, d := range h {
194+ if d.Seq <= seq {
195+ continue
196+ }
197+ if d.Full {
198+ return dst[:n], false
199+ }
200+ dst = append(dst, d.Rects...)
201+ }
202+ return dst, true
203+}
204+
205+// Subsurface is a child surface at X, Y logical pixels from the root
206+// surface origin. Below children are drawn under the root surface.
207+type Subsurface struct {
208+ X, Y int
209+ Below bool
210+ SurfaceContent
211+}
212+
213+// HasBuffer reports whether the surface itself carries a client buffer.
214+func (c SurfaceContent) HasBuffer() bool {
215+ return c.SHM != nil || c.DMABuf != nil || c.Solid != nil
216+}
217+
218+// SolidColor is the color of a single-pixel buffer: premultiplied by alpha,
219+// 0 to 1, sRGB-encoded like the pixels of client buffers.
220+type SolidColor struct{ R, G, B, A float32 }
221+
222+// Empty reports whether the content has nothing to draw.
223+func (c SurfaceContent) Empty() bool {
224+ return !c.HasBuffer() && len(c.Children) == 0
225+}
226+
227+// SHMBuffer is a client wl_shm buffer: B8G8R8A8 pixels (argb8888/xrgb8888
228+// little-endian) at Offset in the pool File, Stride bytes per row. The
229+// client keeps writing the file, so renderers copy from it when they draw.
230+// Pool is unique per wl_shm pool for the session: renderers map a pool
231+// once. File stays open while the client's buffer exists and follows the
232+// DMABuf rules: duplicate it under File.SyscallConn before keeping it. A
233+// client may shrink the file at any time, so readers must survive faults.
234+type SHMBuffer struct {
235+ Pool uint64
236+ File *os.File
237+ Offset, Stride int
238+}
239+
240+// DMABuf is a client GPU buffer (linux-dmabuf). Its files stay open while
241+// the client's buffer exists; a renderer that keeps it must duplicate the
242+// descriptors under File.SyscallConn so a concurrent close cannot hand it a
243+// reused one. ID is unique per buffer for the whole session: renderers key
244+// their imports on it.
245+type DMABuf struct {
246+ ID uint64
247+ Width, Height int
248+ Format uint32 // DRM fourcc
249+ Modifier uint64
250+ Planes []DMABufPlane
251+}
252+
253+// DMABufPlane is one plane of a DMABuf.
254+type DMABufPlane struct {
255+ File *os.File
256+ Offset, Stride uint32
257+}
258+
259+// DMABufFormat is a DRM fourcc with a modifier the renderer can import.
260+type DMABufFormat struct {
261+ Format uint32
262+ Modifier uint64
263+}
264+
265+// DMABufSupport is what the renderer imports: the render device (a dev_t,
266+// 0 when unknown) and the formats. No formats means no dmabuf.
267+type DMABufSupport struct {
268+ Device uint64
269+ Formats []DMABufFormat
270+}
@@ -0,0 +1,270 @@
1+package ports
2+
3+import (
4+ "image"
5+ "math"
6+ "os"
7+)
8+
9+// SurfaceColor is the committed encoding of a surface. Zero means sRGB.
10+// PQ+BT.2020 and extended-linear sRGB are color-managed; zero is legacy
11+// sRGB. MaxCLL/MaxFALL are in nits. YUV metadata is per surface commit.
12+type SurfaceColor struct {
13+ TF, Primaries uint8
14+ MaxCLL, MaxFALL uint16
15+ // Coefficients/range follow wp_color_representation_v1 (zero =
16+ // unspecified, for YUV interpreted as BT.709 limited). Chroma is a
17+ // H.273 Chroma420SampleLocType + 1 (zero defaults to type 0).
18+ Coefficients, Range, Chroma uint8
19+}
20+
21+const (
22+ ColorTFPQ uint8 = 11 // wp_color_manager_v1 ST2084 PQ
23+ ColorTFExtendedLinear uint8 = 5 // wp_color_manager_v1 ext_linear
24+ ColorPrimariesBT2020 uint8 = 6 // wp_color_manager_v1 BT.2020
25+ ColorPrimariesSRGB uint8 = 1
26+ ColorTFSRGB uint8 = 9 // wp_color_manager_v1 sRGB
27+)
28+
29+func (c SurfaceColor) IsPQ2020() bool {
30+ return c.TF == ColorTFPQ && c.Primaries == ColorPrimariesBT2020
31+}
32+
33+func (c SurfaceColor) IsExtendedLinear() bool {
34+ return c.TF == ColorTFExtendedLinear && c.Primaries == ColorPrimariesSRGB
35+}
36+
37+// ContentType is the committed wp_content_type_v1 hint.
38+type ContentType uint8
39+
40+const (
41+ ContentNone ContentType = iota
42+ ContentPhoto
43+ ContentVideo
44+ ContentGame
45+)
46+
47+// SurfaceContent carries wayland → output the latest committed content of a
48+// window: SHM, a client shared-memory buffer, DMABuf, a GPU buffer, or
49+// Solid, a single-pixel buffer. Renderers read them in place and never modify
50+// them. Empty means the window
51+// has no content. LogicalW and LogicalH are the surface size in logical
52+// pixels (buffer scale and viewport applied). Subsurfaces come in Children;
53+// Geometry is the part of the surface that is the window (xdg window
54+// geometry), the rest being client shadows.
55+type SurfaceContent struct {
56+ ID WindowID
57+ ContentType ContentType
58+ // Surface is a never-reused wl_surface identity; Version identifies its
59+ // applied buffer content independently of the window publication Seq.
60+ Surface, Version uint64
61+ // Seq counts the window's contents (wayland sets it), for release.
62+ Seq uint64
63+ Width, Height int
64+ LogicalW, LogicalH int
65+ // Source is the committed crop in buffer pixels: x, y, width, height.
66+ // Zero width means the entire buffer is used.
67+ Source [4]float32
68+ // Transform is the wl_output.transform the client applied to the
69+ // buffer: readers apply its inverse. Width and Height stay the buffer's.
70+ Transform BufferTransform
71+ // Opaque ignores the alpha byte: an x format, or an opaque region that
72+ // covers the whole surface. It is false while Fade is set.
73+ Opaque bool
74+ // OpaqueRect is the largest rect of the opaque region inside a surface
75+ // that is not wholly Opaque, in logical pixels from the surface origin:
76+ // the pixels there ignore alpha too, so the surfaces below it are hidden
77+ // by it. Empty when there is none or while Fade is set.
78+ OpaqueRect Rect
79+ // Fade is how much wp_alpha_modifier_v1 fades the surface: 0 none,
80+ // 1 invisible. Its opacity is multiplied by 1-Fade.
81+ Fade float32
82+ Color SurfaceColor
83+ SHM *SHMBuffer
84+ DMABuf *DMABuf
85+ // Solid is a single-pixel buffer (Width and Height are 1): the surface is
86+ // one flat color.
87+ Solid *SolidColor
88+ // Children are the subsurfaces, bottom to top, flattened.
89+ Children []Subsurface
90+ // Geometry is in logical pixels from the surface origin; empty means
91+ // the whole surface.
92+ Geometry Rect
93+ // Async asks for tearing presentation (wp_tearing_control_v1); outputs
94+ // honour it only in direct scanout.
95+ Async bool
96+ // Acquire is the explicit-sync fence of this content's root buffer
97+ // (wp_linux_drm_syncobj_v1): readers wait on it before reading the
98+ // buffer, instead of the buffer's implicit fences. Wayland owns it and
99+ // closes it when the buffer is released; a reader that keeps it past
100+ // the call duplicates it under Acquire.SyscallConn. nil: implicit sync.
101+ // Wayland closes it only after every output reported a later content of
102+ // the window as seen, so a reader never gets it already closed.
103+ Acquire *os.File
104+ // DamageHistory is what changed in the root surface's buffer over the
105+ // window's last contents, oldest first, ending with this one (Seq).
106+ // A renderer holding an older content redraws the union of the
107+ // entries after it, or everything when the history does not reach it.
108+ DamageHistory []SeqDamage
109+}
110+
111+// BufferTransform is a wl_output.transform value: bit 0 rotates 90°
112+// counter-clockwise, bit 1 rotates 180°, bit 2 flips around the vertical
113+// axis first.
114+type BufferTransform uint8
115+
116+// Rotated reports whether width and height swap between buffer and surface.
117+func (t BufferTransform) Rotated() bool { return t&1 != 0 }
118+
119+// Size returns the size of a w×h space seen through t: width and height swap
120+// when t is Rotated.
121+func (t BufferTransform) Size(w, h int) (int, int) {
122+ if t.Rotated() {
123+ return h, w
124+ }
125+ return w, h
126+}
127+
128+// ToBuffer maps a point of a w×h surface (logical, before scale) to the
129+// same point of its buffer, in the buffer's untransformed w'×h' axes, where
130+// w' and h' swap when the transform is Rotated.
131+func (t BufferTransform) ToBuffer(x, y, w, h float64) (float64, float64) {
132+ if t&4 != 0 {
133+ x = w - x
134+ }
135+ switch t & 3 {
136+ case 1:
137+ return y, w - x
138+ case 2:
139+ return w - x, h - y
140+ case 3:
141+ return h - y, x
142+ }
143+ return x, y
144+}
145+
146+// Invert is the transform that maps target points back: for a w×h scene and
147+// its w'×h' target, t.Invert().ToBuffer(t.ToBuffer(p, w, h), w', h') == p.
148+func (t BufferTransform) Invert() BufferTransform {
149+ if t&1 != 0 && t&4 == 0 {
150+ return t ^ 2
151+ }
152+ return t
153+}
154+
155+// RectToBuffer maps a rectangle of a w×h surface to the buffer. Integer
156+// rectangles stay integer under these transforms; rounding absorbs float noise.
157+func (t BufferTransform) RectToBuffer(r image.Rectangle, w, h int) image.Rectangle {
158+ ax, ay := t.ToBuffer(float64(r.Min.X), float64(r.Min.Y), float64(w), float64(h))
159+ bx, by := t.ToBuffer(float64(r.Max.X), float64(r.Max.Y), float64(w), float64(h))
160+ return image.Rect(int(math.Round(ax)), int(math.Round(ay)), int(math.Round(bx)), int(math.Round(by)))
161+}
162+
163+// SeqDamage is what content Seq changed from the one before: Rects in
164+// root buffer pixels, or everything when Full.
165+type SeqDamage struct {
166+ Seq uint64
167+ Full bool
168+ Rects []Rect
169+}
170+
171+// DamageSince is the union of changes after content seq up to this one,
172+// and false when the history does not reach back to seq (redraw all).
173+func (c SurfaceContent) DamageSince(seq uint64) ([]Rect, bool) {
174+ out, ok := c.AppendDamageSince(nil, seq)
175+ if !ok {
176+ return nil, false
177+ }
178+ return out, true
179+}
180+
181+// AppendDamageSince is DamageSince appending to dst, so a caller reusing
182+// its slice allocates nothing. When the history does not reach back to seq
183+// it returns dst unchanged and false.
184+func (c SurfaceContent) AppendDamageSince(dst []Rect, seq uint64) ([]Rect, bool) {
185+ if seq >= c.Seq {
186+ return dst, true
187+ }
188+ h := c.DamageHistory
189+ if len(h) == 0 || h[0].Seq > seq+1 || h[len(h)-1].Seq != c.Seq {
190+ return dst, false
191+ }
192+ n := len(dst)
193+ for _, d := range h {
194+ if d.Seq <= seq {
195+ continue
196+ }
197+ if d.Full {
198+ return dst[:n], false
199+ }
200+ dst = append(dst, d.Rects...)
201+ }
202+ return dst, true
203+}
204+
205+// Subsurface is a child surface at X, Y logical pixels from the root
206+// surface origin. Below children are drawn under the root surface.
207+type Subsurface struct {
208+ X, Y int
209+ Below bool
210+ SurfaceContent
211+}
212+
213+// HasBuffer reports whether the surface itself carries a client buffer.
214+func (c SurfaceContent) HasBuffer() bool {
215+ return c.SHM != nil || c.DMABuf != nil || c.Solid != nil
216+}
217+
218+// SolidColor is the color of a single-pixel buffer: premultiplied by alpha,
219+// 0 to 1, sRGB-encoded like the pixels of client buffers.
220+type SolidColor struct{ R, G, B, A float32 }
221+
222+// Empty reports whether the content has nothing to draw.
223+func (c SurfaceContent) Empty() bool {
224+ return !c.HasBuffer() && len(c.Children) == 0
225+}
226+
227+// SHMBuffer is a client wl_shm buffer: B8G8R8A8 pixels (argb8888/xrgb8888
228+// little-endian) at Offset in the pool File, Stride bytes per row. The
229+// client keeps writing the file, so renderers copy from it when they draw.
230+// Pool is unique per wl_shm pool for the session: renderers map a pool
231+// once. File stays open while the client's buffer exists and follows the
232+// DMABuf rules: duplicate it under File.SyscallConn before keeping it. A
233+// client may shrink the file at any time, so readers must survive faults.
234+type SHMBuffer struct {
235+ Pool uint64
236+ File *os.File
237+ Offset, Stride int
238+}
239+
240+// DMABuf is a client GPU buffer (linux-dmabuf). Its files stay open while
241+// the client's buffer exists; a renderer that keeps it must duplicate the
242+// descriptors under File.SyscallConn so a concurrent close cannot hand it a
243+// reused one. ID is unique per buffer for the whole session: renderers key
244+// their imports on it.
245+type DMABuf struct {
246+ ID uint64
247+ Width, Height int
248+ Format uint32 // DRM fourcc
249+ Modifier uint64
250+ Planes []DMABufPlane
251+}
252+
253+// DMABufPlane is one plane of a DMABuf.
254+type DMABufPlane struct {
255+ File *os.File
256+ Offset, Stride uint32
257+}
258+
259+// DMABufFormat is a DRM fourcc with a modifier the renderer can import.
260+type DMABufFormat struct {
261+ Format uint32
262+ Modifier uint64
263+}
264+
265+// DMABufSupport is what the renderer imports: the render device (a dev_t,
266+// 0 when unknown) and the formats. No formats means no dmabuf.
267+type DMABufSupport struct {
268+ Device uint64
269+ Formats []DMABufFormat
270+}
A internal/ports/workspace_events.go +25 -0
@@ -0,0 +1,25 @@
1+package ports
2+
3+// WorkspaceActivate requests showing workspaces by stable ID, in commit order.
4+// Core applies the entire batch before publishing a new state.
5+type WorkspaceActivate struct{ IDs []uint64 }
6+
7+func (WorkspaceActivate) clientEvent() {}
8+
9+// Workspaces is an immutable, latest-only inventory of output workspaces.
10+type Workspaces struct{ Outputs []WorkspaceOutput }
11+type WorkspaceOutput struct {
12+ Name string
13+ Workspaces []WorkspaceInfo
14+}
15+type WorkspaceInfo struct {
16+ ID uint64
17+ Configured string // configured name; empty for dynamic workspaces. ID, not this, identifies the workspace
18+ Name string
19+ Index int // zero-based vertical coordinate
20+ Active, Hidden bool
21+ // Frame is the workspace viewport in output-local logical pixels
22+ // (Monitor.Frame geometry: the whole output unless the workspace has a
23+ // size override). It is where a capture session of it lies.
24+ Frame Rect
25+}
@@ -0,0 +1,25 @@
1+package ports
2+
3+// WorkspaceActivate requests showing workspaces by stable ID, in commit order.
4+// Core applies the entire batch before publishing a new state.
5+type WorkspaceActivate struct{ IDs []uint64 }
6+
7+func (WorkspaceActivate) clientEvent() {}
8+
9+// Workspaces is an immutable, latest-only inventory of output workspaces.
10+type Workspaces struct{ Outputs []WorkspaceOutput }
11+type WorkspaceOutput struct {
12+ Name string
13+ Workspaces []WorkspaceInfo
14+}
15+type WorkspaceInfo struct {
16+ ID uint64
17+ Configured string // configured name; empty for dynamic workspaces. ID, not this, identifies the workspace
18+ Name string
19+ Index int // zero-based vertical coordinate
20+ Active, Hidden bool
21+ // Frame is the workspace viewport in output-local logical pixels
22+ // (Monitor.Frame geometry: the whole output unless the workspace has a
23+ // size override). It is where a capture session of it lies.
24+ Frame Rect
25+}

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 26

Tags 10

↑↓ moveenter openesc close