class InputChannel
Swift
final class InputChannel
The app's end of SubscribeInput: an async stream on one side, a frame
loop that is not async on the other.
Same shape as LoopQueue in the other direction and for the same reason —
events arrive on an NPRPC thread, and the only thread allowed to touch the
arena is the one writing frames into it.
Sources/LavaClient/ControlPlane.swift:301
Constructors
init(stream: NPRPCBidiStream<InputAck, WireInputEvent>)
Not documented.
Sources/LavaClient/ControlPlane.swift:307
Properties
var consumedSerial: UInt32 { get }
The newest serial this client has taken out of the queue.
Rides on every Present (see LavaClient.open), which is what lets
the compositor tell a frame drawn with an event from one drawn just
before it — the opening Resize being the one that matters, since a
window is held off screen until a frame comes back laid out at the
size it was given. The ack cannot answer that: it is sent from here,
on the way in, and the frame is laid out afterwards.
Read from the frame loop, written on the same thread inside drain,
but under the lock like everything else here — enqueue runs on an
NPRPC thread and this object has one lock, not one per field.
Sources/LavaClient/ControlPlane.swift:419
var isClosed: Bool { get }
True once the renderer has gone away — the app's cue to stop drawing frames nobody will read.
Sources/LavaClient/ControlPlane.swift:388
var onArrival: (@Sendable () -> Void)? { get set }
Called on the NPRPC thread the moment something arrives, for a client whose frame loop parks rather than polls.
A LavaUI client blocks in pumpEvents until something happens, and an
input that only becomes
visible on the next tick would be an input that arrives when the next
tick happens to be — which for an idle app is never.
Arming it is itself an arrival, for whatever landed while it was nil.
The reader task starts in init, and the compositor answers
SubscribeInput with the opening Resize immediately — well before a
caller finishing its bring-up has got as far as installing this. Without
the catch-up call that size waits in the queue for some other event to
wake the loop, which for a window nobody has touched yet never comes:
the window sits at its requested size inside a frame the compositor has
already placed and sized, with the desktop showing between the two.
Sources/LavaClient/ControlPlane.swift:363
Methods
func close()
Ends the lease cleanly from this side.
Finishes the ack stream so the writer closes, which is what the
compositor treats as "the surface is gone" when DestroySurface is
not used first. Cheap if already closed.
Sources/LavaClient/ControlPlane.swift:342
func drain() -> [WireInputEvent]
Everything that arrived since the last call. Acknowledges the batch, which is the honest moment to do it: the serial now means "consumed", not "received".
Sources/LavaClient/ControlPlane.swift:397