Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions riddle/src/display.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@ mod quill_ffi {
impl Display {
pub fn open() -> io::Result<(Self, Surface)> {
if let Ok(key) = std::env::var("QTFB_KEY") {
// Windowed AppLoad mode. FBFMT_RMPP_RGB565 is a Paper Pro
// ("ferrari") -specific wire format (see qtfb.rs), so this path
// is Ferrari-only; riddle ships with "qtfb": false in its
// manifest and always runs via takeover (below) instead. A
// Move-native qtfb format would need its own investigation.
let key: i32 = key.parse().map_err(io::Error::other)?;
let mut client = crate::qtfb::QtfbClient::connect(
key,
Expand Down
35 changes: 31 additions & 4 deletions riddle/src/fb.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,34 @@
//! Geometry helpers. Drawing lives in surface.rs.

pub const SCREEN_W: usize = 1620;
pub const SCREEN_H: usize = 2160;
use std::sync::OnceLock;

// Panel size varies by device (Paper Pro: 1620x2160, Paper Pro Move:
// 960x1696) and isn't known until the display backend opens and reports
// the real aux/qtfb framebuffer geometry. `init_screen` sets it once, early
// in `main`; every reader after that goes through `screen_w`/`screen_h`.
static SCREEN_DIMS: OnceLock<(usize, usize)> = OnceLock::new();

pub fn init_screen(w: usize, h: usize) {
let _ = SCREEN_DIMS.set((w, h));
}

pub fn screen_w() -> usize {
SCREEN_DIMS.get().expect("init_screen not called before screen_w").0
Comment on lines +15 to +16

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Initialize geometry before using screen helpers in tests

When unit tests or other non-run() paths call these helpers, SCREEN_DIMS has never been set because fb::init_screen() is only called after Display::open() in run(). Existing tests such as help::modal_renders_and_restores/sleep_page_renders_and_restores call screen_w() directly, and the ink tests reach it through BBox::add, so the suite now panics before exercising those cases; add a test/default initialization path or avoid requiring global display initialization for these helpers.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed — added fb::test_init_screen() (idempotent OnceLock::set, safe under parallel test execution) and called it at the top of the four affected tests: help::tests::modal_renders_and_restores, help::tests::sleep_page_renders_and_restores, and the two ink::tests that reach BBox::add via Ink::pen_point. cargo test now passes: 26 passed; 0 failed. See fc75f1f.

}

pub fn screen_h() -> usize {
SCREEN_DIMS.get().expect("init_screen not called before screen_h").1
}

/// Tests exercise geometry helpers without ever opening a display, so they
/// need `init_screen` called explicitly. Any dimensions work for a test;
/// this uses the Paper Pro's so tests match the values they were written
/// against. Idempotent (and safe under parallel test execution) since it's
/// the same `OnceLock::set` `init_screen` itself uses.
#[cfg(test)]
pub fn test_init_screen() {
init_screen(1620, 2160);
}

/// Grow-only pixel bounding box, used to build update/dissolve regions.
#[derive(Clone, Copy, Debug)]
Expand All @@ -22,8 +49,8 @@ impl BBox {
pub fn add(&mut self, x: i32, y: i32, margin: i32) {
self.x0 = self.x0.min(x - margin).max(0);
self.y0 = self.y0.min(y - margin).max(0);
self.x1 = self.x1.max(x + margin).min(SCREEN_W as i32 - 1);
self.y1 = self.y1.max(y + margin).min(SCREEN_H as i32 - 1);
self.x1 = self.x1.max(x + margin).min(screen_w() as i32 - 1);
self.y1 = self.y1.max(y + margin).min(screen_h() as i32 - 1);
}
pub fn rect(&self) -> (i32, i32, i32, i32) {
(self.x0, self.y0, self.x1 - self.x0 + 1, self.y1 - self.y0 + 1)
Expand Down
30 changes: 16 additions & 14 deletions riddle/src/help.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
//! the diary's gestures; touching the pen to the page dismisses it. Detection
//! is local geometry — no oracle — so the guide works even with no network.

use crate::fb::{BBox, SCREEN_H, SCREEN_W};
use crate::fb::{screen_h, screen_w, BBox};
use crate::script;
use crate::surface::{Surface, BLACK, WHITE};
use ab_glyph::FontRef;
Expand Down Expand Up @@ -154,10 +154,10 @@ pub fn show(surf: &mut Surface, font: &FontRef, takeover: bool) -> Help {
for l in body {
wmax = wmax.max(script::measure(font, l, BODY_PX));
}
let pw = (wmax as usize + 2 * PAD).min(SCREEN_W - 40);
let pw = (wmax as usize + 2 * PAD).min(screen_w() - 40);
let ph = PAD + title_h + line_h / 2 + body.len() * line_h + footer_h + PAD;
let px = (SCREEN_W - pw) / 2;
let py = (SCREEN_H.saturating_sub(ph)) / 2;
let px = (screen_w() - pw) / 2;
let py = (screen_h().saturating_sub(ph)) / 2;

let saved = surf.copy_rect(px, py, pw, ph);
surf.fill_rect(px, py, pw, ph, WHITE);
Expand Down Expand Up @@ -192,18 +192,18 @@ impl Help {
/// Replace the page with the full-screen sleep card; returns the saved page
/// pixels so waking can restore them exactly.
pub fn show_sleep(surf: &mut Surface, font: &FontRef) -> Vec<u8> {
let saved = surf.copy_rect(0, 0, SCREEN_W, SCREEN_H);
surf.fill_rect(0, 0, SCREEN_W, SCREEN_H, WHITE);
frame(surf, 48, 48, SCREEN_W - 96, SCREEN_H - 96, 4);
frame(surf, 66, 66, SCREEN_W - 132, SCREEN_H - 132, 1);
let y = SCREEN_H * 38 / 100;
blit_centered(surf, font, "The diary sleeps.", 116.0, 0, SCREEN_W, y);
blit_centered(surf, font, "Press the button to wake it.", 56.0, 0, SCREEN_W, y + 230);
let saved = surf.copy_rect(0, 0, screen_w(), screen_h());
surf.fill_rect(0, 0, screen_w(), screen_h(), WHITE);
frame(surf, 48, 48, screen_w() - 96, screen_h() - 96, 4);
frame(surf, 66, 66, screen_w() - 132, screen_h() - 132, 1);
let y = screen_h() * 38 / 100;
blit_centered(surf, font, "The diary sleeps.", 116.0, 0, screen_w(), y);
blit_centered(surf, font, "Press the button to wake it.", 56.0, 0, screen_w(), y + 230);
saved
}

pub fn restore_sleep(surf: &mut Surface, saved: &[u8]) {
surf.paste_rect(0, 0, SCREEN_W, SCREEN_H, saved);
surf.paste_rect(0, 0, screen_w(), screen_h(), saved);
}

fn frame(surf: &mut Surface, x: usize, y: usize, w: usize, h: usize, t: usize) {
Expand Down Expand Up @@ -290,7 +290,8 @@ mod tests {

#[test]
fn modal_renders_and_restores() {
let (w, h) = (SCREEN_W, SCREEN_H);
crate::fb::test_init_screen();
let (w, h) = (screen_w(), screen_h());
let mut buf = vec![0xFFu8; w * h * 4];
let ptr = buf.as_mut_ptr();
let mut surf = Surface::new(ptr, buf.len(), w, h, w * 4, crate::surface::PixFmt::Rgb32);
Expand Down Expand Up @@ -336,7 +337,8 @@ mod tests {

#[test]
fn sleep_page_renders_and_restores() {
let (w, h) = (SCREEN_W, SCREEN_H);
crate::fb::test_init_screen();
let (w, h) = (screen_w(), screen_h());
let mut buf = vec![0xFFu8; w * h * 4];
let ptr = buf.as_mut_ptr();
let mut surf = Surface::new(ptr, buf.len(), w, h, w * 4, crate::surface::PixFmt::Rgb32);
Expand Down
2 changes: 2 additions & 0 deletions riddle/src/ink.rs
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,7 @@ mod tests {

#[test]
fn erase_forgets_covered_points_and_splits_strokes() {
crate::fb::test_init_screen();
let (_buf, mut s) = surf();
let mut ink = Ink::new();
// A horizontal stroke across the page.
Expand All @@ -210,6 +211,7 @@ mod tests {

#[test]
fn erasing_everything_empties_the_ink() {
crate::fb::test_init_screen();
let (_buf, mut s) = surf();
let mut ink = Ink::new();
ink.pen_point(&mut s, 100, 100, 3);
Expand Down
Loading