v0.2.0 13c6a90

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

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