//! system/services/input — the user-space input service. Shipped in the initial_ramdisk, //! spawned as a ring-3 process, and bound at `/protocol/input`. It //! is the fan-out point between **sources** (keyboard, mouse, and joystick/gamepad drivers) //! and **subscribers** (any program that wants input): a source `publish`es an //! `InputEvent`, and the service pushes it to every subscriber whose interest mask includes //! that event's device class (keyboard / mouse / joystick). //! //! The delivery discipline is the whole design (see docs/input.md). The kernel's IPC is a //! synchronous rendezvous: a server holds one pending reply, so it cannot park N //! subscribers blocked in a "wait for next event" call. Broadcasting therefore has to be //! *push* — the service delivering to subscribers. But a synchronous push (`ipc_call`) //! would let one dead or wedged subscriber hang the whole broadcast, since the kernel //! never wakes a sender parked on a dead peer's endpoint. So delivery uses the //! asynchronous `ipc.send`: it posts the event to each subscriber's endpoint queue and //! returns at once, and can never block on a subscriber. That primitive exists for exactly //! this ([ipc.md](../../../docs/ipc.md), "asynchronous / buffered send"). //! //! A subscriber registers by handing the service its own endpoint as a capability (M13 //! capability passing — this service is its first real consumer). The service keeps that //! handle and `ipc.send`s each event to it. That is the envelope's reserved `subscribe` //! verb, which this protocol adopts rather than defining its own. //! //! P4a moved this service onto the shared harness (library/kernel/service.zig) — it was the //! last hand-rolled receive loop in the tree, and the one service that answered neither the //! universal ping nor a `terminate` signal. P4c finished the job: the subscriber table, the //! fan-out, and the dead-subscriber sweep are the harness's now //! (`service.Subscribers`), so what is left here is what is actually about input — which //! device class an event belongs to, and which classes a subscriber asked for. The sweep //! that replaced the old prune is the one idiom the system uses everywhere: published //! process-exit notifications, not a poll of the process list on every subscribe. const ipc = @import("ipc"); const service = @import("service"); const logging = @import("logging"); const input_protocol = @import("input-protocol"); const envelope = @import("envelope"); /// The subscriber side of the input contract: the table, the reserved `subscribe` /// verb, the exit sweep, and the fan-out. One fan-out point per process, so the /// handler context is empty. const Subscriptions = service.Subscribers(input_protocol.Protocol, void); const Invocation = envelope.Invocation; const Answer = envelope.Answer; /// Push `event` to every subscriber whose interest mask includes its device class. The /// class is the packet's operation, so this picks *which event* to publish and the harness /// frames it once for the whole fan-out — the mapping from device to class is the only part /// of a broadcast that is this service's own. fn broadcast(event: input_protocol.InputEvent) void { const class = input_protocol.deviceBit(event.device); switch (input_protocol.eventOfDevice(event.device) orelse return) { // no class wants it .keyboard => Subscriptions.publishClass(.keyboard, 0, event.asKeyboard() orelse return, class), .mouse => Subscriptions.publishClass(.mouse, 0, event.asMouse() orelse return, class), .joystick => Subscriptions.publishClass(.joystick, 0, event.asJoystick() orelse return, class), } } fn onPublish(_: void, invocation: Invocation(input_protocol.InputEvent), _: Answer(void)) isize { broadcast(invocation.request); return 0; } /// `subscribe` and `unsubscribe` are absent on purpose: the harness answers both. const handlers = Subscriptions.Handlers{ .publish = onPublish }; fn onMessage(message: []const u8, out: []u8, sender: u32, arrived: *ipc.Arrival) usize { return Subscriptions.dispatch({}, handlers, message, sender, arrived, out); } fn initialise(_: ipc.Handle) bool { // The harness has already bound `/protocol/input` by the time this runs, so // "ready" still means what it always meant: the name is claimed and the loop // is about to serve it. _ = logging.write("/system/services/input: ready\n"); return true; } pub fn main() void { service.run(input_protocol.message_maximum, .{ .service = "input", .init = initialise, .on_message = onMessage, .subscribers = Subscriptions.hooks, }); }