1package app
2
3import (
4 "github.com/bnema/neferwl/internal/adapters/wayland"
5 "github.com/bnema/neferwl/internal/adapters/xkb"
6 "github.com/bnema/neferwl/internal/core"
7 "github.com/bnema/neferwl/internal/ports"
8)
9
10// pipes are the channels between a session's goroutines. newPipes makes
11// them all, with their buffer sizes; core, wayland and outputs hand each
12// side the ends it uses.
13type pipes struct {
14 securityChanges chan ports.SecurityState
15 securityEvents chan ports.SecurityBackendEvent
16
17 client chan ports.ClientEvent
18 input chan ports.InputEvent
19 output chan ports.OutputEvent
20 commands chan ports.ClientCommand
21 spawn chan ports.SpawnRequest
22
23 // Capacity 1: each holds the latest value its sender published.
24 scenes chan []ports.Scene
25 renderScenes chan []ports.Scene
26 layouts chan ports.Layout
27 constraints chan ports.PointerConstraint
28 states chan ports.State
29 workspaces chan ports.Workspaces
30 cursorChanges chan ports.CursorChange
31 keymaps chan *xkb.Keymap
32 deviceConfigs chan ports.InputDevicesConfig
33
34 // Configuration: watched from the file, filtered by relayConfig, then
35 // configured (overrides included) by outputs for core.
36 watched chan ports.ConfigChanged
37 filtered chan ports.ConfigChanged
38 configured chan ports.ConfigChanged
39 configErrors chan error
40 // scales is nil outside a real session: headless runs never touch the
41 // config file.
42 scales chan ports.ScaleChanged
43
44 // Outputs report to wayland through its presentation sink and, for each
45 // flip of an animating scene (Scene.Animating), tell core (frames) to
46 // move running slides.
47 frames chan ports.OutputFrame
48
49 // Hint mode: core asks the hints adapter for a scan and gets the result.
50 hintScans chan ports.HintScanRequest
51 hintResults chan ports.HintScanResult
52
53 captures chan ports.CaptureRequest
54 // Every admitted capture owns one reply slot until routeCaptured takes
55 // it. Keep this capacity tied to admission so output routing never waits.
56 // With hint mode on, the outputs reply on capturedAll and routeCaptured splits the replies by
57 // ports.CaptureIDInternal into captured (the wayland consumer) and
58 // hintCaptured (internal consumers). With hint mode off, capturedAll is
59 // captured itself and there is no routeCaptured.
60 capturedAll chan ports.CaptureDone
61 captured chan ports.CaptureDone
62 // hintCaptured carries internal capture replies to the hints adapter.
63 hintCaptured chan ports.CaptureDone
64
65 outputFormats chan ports.OutputFormats
66 outputHeads chan ports.OutputHeads
67 leaseRequests chan ports.LeaseMessage
68 leaseEvents chan ports.LeaseMessage
69 applyOutput chan ports.OutputApply
70 appliedOutput chan ports.OutputApplied
71
72 // D-Bus idle inhibition and SimulateUserActivity (screensaver), for
73 // wayland's idle notifications. Activity sends never block: capacity 1
74 // coalesces them.
75 idleInhibited chan bool
76 idleActivity chan struct{}
77
78 // Screencast commands to wayland (the screencast adapter's side is the
79 // sender); the events come back through wayland.Options.Casts.
80 castCommands chan ports.CastCommand
81}
82
83// newPipes makes the channels. The hint channels exist only with hint set
84// (hint.enable = on): without them core's hint action is a no-op and nothing
85// of hint mode is allocated.
86func newPipes(realSession, hint bool) *pipes {
87 p := &pipes{
88 securityChanges: make(chan ports.SecurityState, 8),
89 securityEvents: make(chan ports.SecurityBackendEvent, 64),
90
91 client: make(chan ports.ClientEvent, 32),
92 input: make(chan ports.InputEvent, 32),
93 output: make(chan ports.OutputEvent, 32),
94 commands: make(chan ports.ClientCommand, 32),
95 spawn: make(chan ports.SpawnRequest, 32),
96
97 scenes: make(chan []ports.Scene, 1),
98 renderScenes: make(chan []ports.Scene, 1),
99 layouts: make(chan ports.Layout, 1),
100 constraints: make(chan ports.PointerConstraint, 1),
101 states: make(chan ports.State, 1),
102 workspaces: make(chan ports.Workspaces, 1),
103 cursorChanges: make(chan ports.CursorChange, 1),
104 keymaps: make(chan *xkb.Keymap, 1),
105 deviceConfigs: make(chan ports.InputDevicesConfig, 1),
106
107 watched: make(chan ports.ConfigChanged, 8),
108 filtered: make(chan ports.ConfigChanged, 8),
109 configured: make(chan ports.ConfigChanged, 8),
110 configErrors: make(chan error, 8),
111
112 frames: make(chan ports.OutputFrame, 8),
113
114 captures: make(chan ports.CaptureRequest, ports.MaxCaptureInflight),
115 captured: make(chan ports.CaptureDone, ports.MaxCaptureInflight),
116
117 outputFormats: make(chan ports.OutputFormats, 8),
118 outputHeads: make(chan ports.OutputHeads, 8),
119 leaseRequests: make(chan ports.LeaseMessage, 32),
120 leaseEvents: make(chan ports.LeaseMessage, 32),
121 applyOutput: make(chan ports.OutputApply, 8),
122 appliedOutput: make(chan ports.OutputApplied, 8),
123
124 idleInhibited: make(chan bool, 1),
125 idleActivity: make(chan struct{}, 1),
126
127 castCommands: make(chan ports.CastCommand, 64),
128 }
129 // Without hint mode every reply is for the wayland consumer: the outputs
130 // reply on its channel directly and nothing demultiplexes.
131 p.capturedAll = p.captured
132 if hint {
133 p.capturedAll = make(chan ports.CaptureDone, ports.MaxCaptureInflight)
134 p.hintScans = make(chan ports.HintScanRequest, 1)
135 p.hintResults = make(chan ports.HintScanResult, 1)
136 p.hintCaptured = make(chan ports.CaptureDone, 1)
137 }
138 if realSession {
139 p.scales = make(chan ports.ScaleChanged, 8)
140 }
141 return p
142}
143
144// core is core's side: channels only. The security gate, clock and terminal
145// flag are not channels; assembleSession passes them as core.Options.
146func (p *pipes) core() core.Channels {
147 return core.Channels{
148 Client: p.client,
149 Input: p.input,
150 Output: p.output,
151 Config: p.configured,
152 Commands: p.commands,
153 Spawn: p.spawn,
154 Scenes: p.scenes,
155 Layouts: p.layouts,
156 Constraints: p.constraints,
157 State: p.states,
158 Workspaces: p.workspaces,
159 ConfigErrors: p.configErrors,
160 Scales: p.scales,
161 Frames: p.frames,
162 HintScans: p.hintScans,
163 Hints: p.hintResults,
164 }
165}
166
167// internalCaptured is the channel internal capture replies are routed to: the
168// hints adapter reads it.
169func (p *pipes) internalCaptured() chan<- ports.CaptureDone { return p.hintCaptured }
170
171// wayland is the wayland server's side.
172func (p *pipes) wayland() wayland.Channels {
173 return wayland.Channels{
174 SecurityChanges: p.securityChanges,
175 SecurityEvents: p.securityEvents,
176 Events: p.client,
177 Commands: p.commands,
178 Workspaces: p.workspaces,
179 Cursors: p.cursorChanges,
180 Captures: p.captures,
181 Captured: p.captured,
182 OutputFormats: p.outputFormats,
183 OutputHeads: p.outputHeads,
184 LeaseRequests: p.leaseRequests,
185 LeaseEvents: p.leaseEvents,
186 OutputApply: p.applyOutput,
187 OutputApplied: p.appliedOutput,
188 IdleInhibited: p.idleInhibited,
189 IdleActivity: p.idleActivity,
190 CastCommands: p.castCommands,
191 }
192}
193
194// outputs is the outputs' side (DRM or headless).
195func (p *pipes) outputs(security ports.SessionSecurity, presented ports.PresentationSink, content contentAttacher) outputChannels {
196 return outputChannels{
197 security: security,
198 securityChanges: p.securityChanges,
199 securityEvents: p.securityEvents,
200 events: p.output,
201 scenes: p.renderScenes,
202 cursorChanges: p.cursorChanges,
203 presented: presented,
204 content: content,
205 frames: p.frames,
206 captures: p.captures,
207 captured: p.capturedAll,
208 formats: p.outputFormats,
209 heads: p.outputHeads,
210 leaseRequests: p.leaseRequests,
211 leaseEvents: p.leaseEvents,
212 reloads: p.filtered,
213 configured: p.configured,
214 requests: p.applyOutput,
215 replies: p.appliedOutput,
216 }
217}