visual.go RawHistoryFolder
1package ports
2
3import (
4 "context"
5 "time"
6)
7
8// Visual is the policy daemon's only handle on the separate visual process
9// (fade animation and ext-session-lock surfaces). The daemon never initialises
10// Vulkan or fonts; an implementation spawns the visual lazily on first use.
11//
12// There is deliberately NO unlock method: only a successful authentication in
13// the visual+auth-worker pair releases the lock, and the visual reports that
14// as a LockReleased event. Activity, reload, logind Unlock and control-socket
15// requests can never reach an unlock path through this port.
16//
17// Methods are called from the single policy owner goroutine only. Results and
18// child lifecycle come back asynchronously as VisualEvent values on the events
19// channel the implementation was constructed with (channel port; tested by
20// sending messages).
21type Visual interface {
22 // Fade starts (Black) or reverses (!Black) the fade. A reveal with no
23 // running visual is a no-op and never spawns a process. Idempotent. A
24 // reveal never drops lock surfaces: it only undoes the fade animation.
25 Fade(ctx context.Context, req VisualFade) error
26 // Lock makes the visual acquire ext-session-lock for req.Generation,
27 // spawning it if needed. Idempotent for a live visual already asked for
28 // the same generation, and safe to call again after VisualExited to
29 // re-spawn: a crashed lock visual leaves the compositor locked and the
30 // respawn re-acquires. req.Auth.EnvPIN ownership transfers to the
31 // implementation, which wipes it once written to the private pipe.
32 Lock(ctx context.Context, req VisualLock) error
33 // Close kills and reaps any visual process. Killing a lock visual leaves
34 // the compositor locked; it never unlocks.
35 Close() error
36 // Detach closes only the daemon's pipe ends and leaves the child running.
37 // The daemon uses it instead of Close when exiting while a lock is live:
38 // the visual treats command-pipe EOF as "daemon gone", keeps its lock and
39 // authentication UI, and exits only after a successful unlock.
40 Detach() error
41}
42
43// VisualCommand is daemon -> visual traffic on the private pipe.
44type VisualCommand interface{ visualCommand() }
45
46// VisualFade: Duration is ports.Config.FadeDuration.
47type VisualFade struct {
48 Black bool
49 Duration time.Duration
50 Spawn VisualSpawn
51}
52
53// VisualSpawn applies only when the request has to launch a process. OmitEnv
54// names environment variables the child must not inherit (the configured PIN
55// variable: its value travels solely in VisualLock.Auth.EnvPIN).
56type VisualSpawn struct{ OmitEnv []string }
57
58// VisualLock carries auth metadata only (config + the privately captured
59// startup PIN value when the env source is selected). Auth.Generation equals
60// Generation and is the nonzero epoch scoping every auth frame. The visual,
61// not the daemon, spawns the auth worker and forwards Auth to it.
62type VisualLock struct {
63 Generation Generation
64 Auth AuthBootstrap
65 Spawn VisualSpawn
66}
67
68func (VisualFade) visualCommand() {}
69func (VisualLock) visualCommand() {}
70
71// VisualEvent is visual -> daemon traffic (lifecycle results, never secrets).
72// LockConfirmed and LockReleased are the authoritative lock lifecycle results
73// and double as PolicyEvents; VisualExited is the final event of a process.
74type VisualEvent interface{ visualEvent() }
75
76// VisualExited reports that the visual process is gone (crash, kill or normal
77// exit after unlock/reveal). Err is nil for a clean zero exit. It is never an
78// unlock: a lock without a prior LockReleased is still held by the compositor.
79type VisualExited struct{ Err error }
80
81func (LockConfirmed) visualEvent() {}
82func (LockReleased) visualEvent() {}
83func (VisualExited) visualEvent() {}
84
85// VisualLauncher is the os/exec adapter seam: it starts one visual process
86// with a private anonymous pipe pair (child fd 3 = commands in, fd 4 = events
87// out) and returns its session, honouring spawn.OmitEnv.
88type VisualLauncher interface {
89 Launch(ctx context.Context, spawn VisualSpawn) (VisualSession, error)
90}
91
92// VisualSession is one running visual process. Send is called from the owner
93// goroutine while Events is read from another goroutine.
94type VisualSession interface {
95 // Send writes a command; it fails once the process is gone.
96 Send(cmd VisualCommand) error
97 // Events yields child events; the final one is VisualExited, then the
98 // channel closes.
99 Events() <-chan VisualEvent
100 // Close kills (SIGKILL) and reaps the process. Idempotent.
101 Close() error
102 // Detach closes the daemon-side pipe ends and stops observing the child.
103 Detach() error
104}