diff --git a/docs/display.md b/docs/display.md index 392ee04..a8e436b 100644 --- a/docs/display.md +++ b/docs/display.md @@ -277,11 +277,14 @@ test/qemu_test.py `), each layering on the last: layer — logging `display: compositor self-check ok`. - **`display-demo`** — the full pipeline from a separate process: the hardware-free [`display-demo`](../system/services/display-demo/) client (the - [`input-source`](../system/services/input-source/) analog) drives layers — a wallpaper, a - sliding rectangle, a cursor — through the layer client API and heartbeats + [`input-source`](../system/services/input-source/) analog) drives layers — a wallpaper and + a sliding rectangle — through the layer client API and heartbeats `display-demo: ok`, proving a frame travelled client → compositor → screen, exactly as - the [input test](input.md) proves an event travels source → service → subscriber. The - visible motion itself is a screenshot away via `zig build run-x86-64`. + the [input test](input.md) proves an event travels source → service → subscriber. It draws + no cursor and reads no input — the cursor is the service's own (below), and the demo + animates on its own frame timer, independent of the mouse (the test spawns `input` + alongside it to keep that independence honest). The visible motion itself is a screenshot + away via `zig build run-x86-64`. - **`display-cursor`** — the mouse-listener thread end to end: with the `input` service up, `input-source mouse` publishes pure motion, and the display's listener thread accumulates it into a cursor position handed to the render loop over the `CursorChannel`. Once the diff --git a/system/kernel/tests.zig b/system/kernel/tests.zig index 1b56b74..d531977 100644 --- a/system/kernel/tests.zig +++ b/system/kernel/tests.zig @@ -2850,6 +2850,11 @@ fn displayDemoTest(boot_information: *const BootInformation) void { result(); return; } + // Spawn the input service too — real boot has it, and it guards the demo's + // independence from input: the demo must animate to `display-demo: ok` on its own + // frame timer even with the input service available (a client that blocks its + // animation loop on a mouse read would stall here, never reaching the marker). + _ = spawnNamed(rd, "input"); _ = spawnNamed(rd, "display-demo"); scheduler.setPriority(1); // below the service + demo, so they run while (true) scheduler.yield(); diff --git a/system/services/display-demo/display-demo.zig b/system/services/display-demo/display-demo.zig index 93576a1..e31ec6f 100644 --- a/system/services/display-demo/display-demo.zig +++ b/system/services/display-demo/display-demo.zig @@ -1,15 +1,19 @@ //! system/services/display-demo — a hardware-free client of the display service, the -//! `input-source` analog for the compositor. It creates a wallpaper, a rectangle it moves -//! each frame, and a small cursor, then drives the compositor in a present loop — proof -//! that a *separate process* can compose a moving scene through the display service over -//! IPC, exercising the layer client API and damage-driven present end to end +//! `input-source` analog for the compositor. It creates a wallpaper and a rectangle it +//! slides each frame, then drives the compositor in a present loop — proof that a +//! *separate process* can compose a moving scene through the display service over IPC, +//! exercising the layer client API and damage-driven present end to end //! (docs/display.md). It logs `display-demo: ok` once it has driven a run of frames. +//! +//! It draws no cursor and reads no input: the on-screen cursor is the display service's +//! own, tracked by the service's mouse-listener thread (docs/display.md). The demo's job +//! is only to prove client-driven animation, so its loop runs on its own frame timer and +//! is deliberately independent of the mouse. const runtime = @import("runtime"); const display = runtime.display; const system = runtime.system; const time = runtime.time; -const input = runtime.input; pub fn main() void { const mode = display.info() orelse { @@ -28,15 +32,6 @@ pub fn main() void { const box = display.createLayer(0, box_y, box_w, box_h, 1) orelse return createFailed(); _ = box.fill(0, 0, box_w, box_h, display.color(0xE0, 0x60, 0x40)); - // A little cursor on top. Its position is signed (the layer API is i32) and clamped to - // the screen; mouse motion arrives as relative deltas we accumulate below. - var cursor_x: i32 = @intCast(mode.width / 2); - var cursor_y: i32 = @intCast(mode.height / 2); - const cursor_max_x: i32 = @as(i32, @intCast(mode.width)) - 12; - const cursor_max_y: i32 = @as(i32, @intCast(mode.height)) - 12; - const cursor = display.createLayer(cursor_x, cursor_y, 12, 12, 2) orelse return createFailed(); - _ = cursor.fill(0, 0, 12, 12, display.color(0xF0, 0xF0, 0xF0)); - _ = display.present(); _ = system.write("display-demo: scene up; animating\n"); @@ -45,18 +40,7 @@ pub fn main() void { var dx: i32 = 8; var frame: u32 = 0; - var mouse = input.subscribeMouse(); // type: ?input.MouseSubscriber - if (mouse == null) _ = system.write("display-demo: no mouse; animating without it\n"); - while (true) : (frame += 1) { - if (mouse) |*ms| { - if (ms.next()) |event| { - cursor_x = clamp(cursor_x + event.dx, 0, cursor_max_x); - cursor_y = clamp(cursor_y + event.dy, 0, cursor_max_y); - _ = cursor.configure(cursor_x, cursor_y, 2, true); - } - } - x += dx; if (x <= 0) { x = 0; @@ -74,13 +58,6 @@ pub fn main() void { } } -/// Clamp `v` to the inclusive range [lo, hi]. -fn clamp(v: i32, lo: i32, hi: i32) i32 { - if (v < lo) return lo; - if (v > hi) return hi; - return v; -} - fn createFailed() void { _ = system.write("display-demo: create failed\n"); }