capture.go RawHistoryFolder

1package ports
2
3import "time"
4
5// Capture sessions, exclusion and workspace sources.
6//
7// Core owns every decision; wayland relays requests and what core decided.
8// Registered sessions are the ext-image-copy-capture sessions
9// (CaptureSessionOpen). One of them at most has an exclusion
10// (neferwl_capture_exclusion_v1): the layer surfaces its HUD attached stay
11// visible over a fullscreen window and are left out of that session's frames.
12//
13// Every capture is shown on screen by the compositor, never by the client
14// (CaptureIndicator): a border on the target of a recording session for as
15// long as it lives, and on the target of every captured frame for
16// CaptureFlash after it. No capture image ever holds an indicator.
17
18const (
19	// MaxExclusionLayers bounds the layer surfaces attached to an exclusion.
20	MaxExclusionLayers = 4
21	// MaxCaptureExcluded bounds SceneCapture.Excluded: attached layers and
22	// their popup descendants.
23	MaxCaptureExcluded = 128
24	// CaptureBorderWidth and CaptureBorderColor are the indicator border, in
25	// logical pixels and #rrggbb. CapturePillSize, CapturePillInset and
26	// CapturePillRadius are the marker of a target that is not on screen
27	// (a workspace rendered for capture only): a rounded square in the
28	// top-right corner of its output, in logical pixels.
29	CaptureBorderWidth = 2
30	CaptureBorderColor = "#ff3b30"
31	CapturePillSize    = 12
32	CapturePillInset   = 8
33	CapturePillRadius  = 3
34	// CaptureFlash is how long the indicator of a captured frame stays on
35	// screen after that frame.
36	CaptureFlash = time.Second
37)
38
39// CaptureReason says why a registered session ends. The zero value is none.
40type CaptureReason string
41
42const (
43	CaptureReasonNone CaptureReason = ""
44	// Every reason ends the session: core forgets it and wayland stops it.
45	CaptureReasonOutputGone    CaptureReason = "output-gone"
46	CaptureReasonWorkspaceGone CaptureReason = "workspace-gone"
47	CaptureReasonInvalidRegion CaptureReason = "invalid-region" // nothing of the region is left on the output
48	CaptureReasonWindowGone    CaptureReason = "window-gone"
49	// TooManyExcluded: the attached layers and their popups exceed
50	// MaxCaptureExcluded; a capture could not leave them all out (fail closed).
51	CaptureReasonTooManyExcluded CaptureReason = "too-many-excluded"
52)
53
54// Terminal reports whether a state with this reason ends the session.
55func (r CaptureReason) Terminal() bool { return r != CaptureReasonNone }
56
57// CaptureSessionOpen carries wayland → core a new ext-image-copy-capture
58// session. Its target is an output (Workspace 0), whole when Region is zero,
59// else the part of it in Region (output-local logical pixels, clipped), or a
60// workspace (ID of WorkspaceInfo.ID): its frame, on whichever output owns it
61// now, or a window (Window not 0): its client area at its own size, wherever
62// it is and whether or not it is on screen, always rendered off screen. ID is
63// chosen by wayland and unique for the process lifetime.
64type CaptureSessionOpen struct {
65	ID        uint64
66	Output    string
67	Workspace uint64
68	Window    WindowID
69	Region    Rect
70}
71
72func (CaptureSessionOpen) clientEvent() {}
73
74// CaptureSessionClose carries wayland → core the end of a session, for any
75// reason. Unknown IDs are ignored.
76type CaptureSessionClose struct{ ID uint64 }
77
78func (CaptureSessionClose) clientEvent() {}
79
80// CaptureFrameTaken carries wayland → core a capture accepted for rendering,
81// from any protocol: a wlr-screencopy frame (Session 0) or a frame of an
82// ext-image-copy-capture session. Output and Region, or Workspace, are the
83// target as the client asked for it: an output (Region zero is the whole
84// output, else output-local logical pixels, clipped by core) or a workspace
85// (Workspace is its ID, Output and Region are empty). Core shows the
86// indicator on that target for CaptureFlash after it, and while Session
87// lives when it is not 0.
88type CaptureFrameTaken struct {
89	Session   uint64
90	Output    string
91	Workspace uint64
92	Window    WindowID
93	Region    Rect
94}
95
96func (CaptureFrameTaken) clientEvent() {}
97
98// CaptureExclusionBegin carries wayland → core the exclusion of a session.
99// Wayland has already refused it when another one is live.
100type CaptureExclusionBegin struct{ Session uint64 }
101
102func (CaptureExclusionBegin) clientEvent() {}
103
104// CaptureExclusionLayer carries wayland → core a layer surface the session's
105// authorised HUD attached (or detached, when it was destroyed). Layer is the
106// LayerSurface.ID, known before the surface maps. Wayland has already checked
107// the layer is top or overlay and that it never takes the keyboard
108// exclusively; at most MaxExclusionLayers are attached.
109type CaptureExclusionLayer struct {
110	Session  uint64
111	Layer    WindowID
112	Attached bool
113}
114
115func (CaptureExclusionLayer) clientEvent() {}
116
117// CaptureExclusionEnd carries wayland → core the end of an exclusion whose
118// session lives on (its object was destroyed). A session that closes ends
119// its exclusion with it. The layers of an ended exclusion that a screen still
120// lists stay excluded and fenced (CaptureSessionState.Exclusion, Revision)
121// until none is listed, so no frame of the session shows the HUD afterwards.
122type CaptureExclusionEnd struct{ Session uint64 }
123
124func (CaptureExclusionEnd) clientEvent() {}
125
126// CaptureSessionState carries core → wayland what core decided about a
127// registered session, on every change of it. Output is the output that owns
128// the target now, Rect the clipped target in output-local logical pixels (for
129// a workspace, its frame) and Workspace the workspace ID, 0 for an output.
130// Hidden means the workspace is not drawn as itself on screen: it is rendered
131// for capture only (Scene.CaptureScene) and frames must ask for it
132// (CaptureRequest.Workspace). Active means frames are served; a hidden
133// workspace is rendered for one session at a time in the compositor (its
134// Workspace): another hidden one is not Active until that one is gone. A
135// non-empty Reason ends the session (wayland stops it).
136//
137// Exclusion says core knows an exclusion of this session. Revision is its
138// revision fence, from 1, and Layers the attached layer surfaces core knows
139// (at most MaxExclusionLayers); both are zero without an exclusion. Core
140// bumps the revision whenever what an excluded frame must leave out changes:
141// the start, an attached or detached layer, an excluded popup, the target. The
142// scene carrying that change has the same SceneCapture.Revision. Wayland
143// confirms an attach to the client only once a state lists the layer, serves
144// the session's frames with CaptureRequest.Exclude and stamps
145// CaptureRevision with the last state's Revision; a renderer refuses the
146// request unless its scene's Capture.Revision is at least that, so a scene
147// published before the change is never used and no stale exclusion list can
148// leak a HUD.
149type CaptureSessionState struct {
150	ID        uint64
151	Output    string
152	Rect      Rect
153	Workspace uint64
154	Hidden    bool
155	Active    bool
156	Reason    CaptureReason
157	Exclusion bool
158	Revision  uint64
159	Layers    []WindowID
160}
161
162func (CaptureSessionState) clientCommand() {}
163
164// SceneCapture carries core → renderer the capture state of one output's
165// scene (Scene.Capture; nil when there is none, which is the whole cost of
166// the feature while no session exists).
167//
168// Session is the session that owns the exclusion, 0 without one; Excluded
169// lists its attached layer surfaces and their popup descendants (at most
170// MaxCaptureExcluded), which that session's frames leave out, and Revision is
171// the fence of CaptureSessionState. Workspace is the ID of the hidden
172// workspace CaptureScene draws, 0 when there is none on this output. Shown is
173// the ID of the workspace this scene draws as itself (0 in the overview): a
174// request for a workspace is served only when it is Shown here, or is the
175// hidden Workspace; else it fails, whatever wayland last believed. Window is
176// the window CaptureScene draws instead, alone at its client size, 0 when
177// none: one of Workspace and Window at most is set.
178type SceneCapture struct {
179	Shown     uint64
180	Session   uint64
181	Excluded  []WindowID
182	Revision  uint64
183	Workspace uint64
184	Window    WindowID
185}
186
187// CaptureIndicator carries core → renderer one mark of a capture on an
188// output's scene (Scene.CaptureIndicators; nil while nothing is captured,
189// which is the whole cost of the feature). The renderer draws it last, over
190// everything, in logical pixels; it is only on the scene of the physical
191// output and in no capture image (the capture pipeline draws captures
192// without it).
193//
194// Without Pill the indicator is the border of a target on screen: Rect is
195// the mark, output-local and clipped, and the border is drawn inside it, at
196// most half its smaller side wide (CaptureBorderOf). Core never lists a mark
197// thinner than MinCaptureMark unless the output itself is: a border of a
198// 1-pixel target would have nothing to draw. With Pill it marks a target
199// that is not on screen: Rect is the pill square, a rounded one.
200type CaptureIndicator struct {
201	Rect Rect
202	Pill bool
203}
204
205// MinCaptureMark is the smallest side of a border mark: the border on both
206// sides and one pixel between them.
207const MinCaptureMark = 2*CaptureBorderWidth + 1
208
209// CaptureBorderOf is the width of the border drawn inside a w×h mark: the
210// indicator width, at most half of each side. Zero means nothing is drawn
211// for a proper border; the renderer then fills the whole mark, and the fence
212// refuses such a mark (the thin mark of a capture it cannot vouch for).
213func CaptureBorderOf(w, h int) int { return min(CaptureBorderWidth, w/2, h/2) }
214
215// Drawable reports whether the indicator draws a visible mark, wherever the
216// output is: a non-empty rectangle whose border, for a border mark, has a
217// width. A pill must also lie in the output (DrawableIn).
218func (m CaptureIndicator) Drawable() bool {
219	if m.Rect.W <= 0 || m.Rect.H <= 0 {
220		return false
221	}
222	return m.Pill || CaptureBorderOf(m.Rect.W, m.Rect.H) > 0
223}
224
225// DrawableIn is Drawable on an output of w×h logical pixels: a pill must lie
226// fully inside it (an empty or partly outside pill is cut by the renderer and
227// may show nothing).
228func (m CaptureIndicator) DrawableIn(w, h int) bool {
229	if !m.Drawable() {
230		return false
231	}
232	if !m.Pill {
233		return true
234	}
235	r := m.Rect
236	return r.X >= 0 && r.Y >= 0 && r.X+r.W <= w && r.Y+r.H <= h
237}

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 25

Tags 10

↑↓ moveenter openesc close