87 lines
3.4 KiB
Zig
87 lines
3.4 KiB
Zig
//! 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
|
|
//! (docs/display.md). It logs `display-demo: ok` once it has driven a run of frames.
|
|
|
|
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 {
|
|
_ = system.write("display-demo: no display service\n");
|
|
return;
|
|
};
|
|
|
|
// A full-screen wallpaper under everything.
|
|
const wallpaper = display.createLayer(0, 0, mode.width, mode.height, 0) orelse return createFailed();
|
|
_ = wallpaper.fill(0, 0, mode.width, mode.height, display.color(0x10, 0x18, 0x28));
|
|
|
|
// A rectangle that slides back and forth.
|
|
const box_w: u32 = 140;
|
|
const box_h: u32 = 100;
|
|
const box_y: i32 = 200;
|
|
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");
|
|
|
|
const span: i32 = @as(i32, @intCast(mode.width)) - @as(i32, @intCast(box_w));
|
|
var x: i32 = 0;
|
|
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;
|
|
dx = -dx;
|
|
} else if (x >= span) {
|
|
x = span;
|
|
dx = -dx;
|
|
}
|
|
_ = box.configure(x, box_y, 1, true); // move it; the compositor repaints old + new
|
|
_ = display.present();
|
|
// A run of frames drawn through the compositor is the automated proof (the visible
|
|
// motion is a screenshot away via `zig build run-x86-64`).
|
|
if (frame == 20) _ = system.write("display-demo: ok\n");
|
|
time.sleep(time.Duration.fromMillis(30));
|
|
}
|
|
}
|
|
|
|
/// 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");
|
|
}
|