···7171 press_since: Instant,
7272 long_press_fired: bool,
7373 last_repeat: Instant,
7474+ hold_consumed: bool,
7475 queue: EventQueue,
7576}
7677···8586 press_since: now,
8687 long_press_fired: false,
8788 last_repeat: now,
8989+ hold_consumed: false,
8890 queue: EventQueue::new(),
8991 }
9092 }
91939294 pub fn reset_hold_state(&mut self) {
9393- let now = Instant::now();
9494- self.press_since = now;
9595- self.long_press_fired = false;
9696- self.last_repeat = now;
9595+ self.hold_consumed = true;
9796 }
98979998 pub fn poll(&mut self) -> Option<Event> {
···105104 let now = Instant::now();
106105107106 if raw != self.candidate {
107107+ // raw deviated from stable; restart hold timer so
108108+ // sub-debounce releases don't accumulate into LongPress
109109+ if self.stable.is_some() && raw != self.stable {
110110+ self.press_since = now;
111111+ self.long_press_fired = false;
112112+ self.last_repeat = now;
113113+ }
108114 self.candidate = raw;
109115 self.candidate_since = now;
110116 }
···118124 if debounced != self.stable {
119125 if let Some(old) = self.stable {
120126 self.queue.push(Event::Release(old));
127127+ self.hold_consumed = false;
121128 }
122129 if let Some(new) = debounced {
123130 self.queue.push(Event::Press(new));
···130137 }
131138132139 if let Some(btn) = self.stable {
133133- let held = now - self.press_since;
140140+ if !self.hold_consumed {
141141+ let held = now - self.press_since;
134142135135- if !self.long_press_fired && held >= Duration::from_millis(LONG_PRESS_MS) {
136136- self.long_press_fired = true;
137137- self.last_repeat = now;
138138- return Some(Event::LongPress(btn));
139139- }
143143+ if !self.long_press_fired && held >= Duration::from_millis(LONG_PRESS_MS) {
144144+ self.long_press_fired = true;
145145+ self.last_repeat = now;
146146+ return Some(Event::LongPress(btn));
147147+ }
140148141141- if self.long_press_fired && (now - self.last_repeat) >= Duration::from_millis(REPEAT_MS)
142142- {
143143- self.last_repeat = now;
144144- return Some(Event::Repeat(btn));
149149+ if self.long_press_fired
150150+ && (now - self.last_repeat) >= Duration::from_millis(REPEAT_MS)
151151+ {
152152+ self.last_repeat = now;
153153+ return Some(Event::Repeat(btn));
154154+ }
145155 }
146156 }
147157
+4-4
kernel/src/kernel/console.rs
···11// boot console: accumulates text lines during hardware init, rendered
22// once to EPD before the app layer takes over
33//
44-// uses the embedded-graphics built-in FONT_6X13 -- no TTF assets or
44+// uses the embedded-graphics built-in FONT_9X18 -- no TTF assets or
55// build.rs font pipeline needed; the kernel can show boot progress
66// on a bare display with nothing but this mono font
7788use embedded_graphics::mono_font::MonoTextStyle;
99-use embedded_graphics::mono_font::ascii::FONT_6X13;
99+use embedded_graphics::mono_font::ascii::FONT_9X18;
1010use embedded_graphics::pixelcolor::BinaryColor;
1111use embedded_graphics::prelude::*;
1212use embedded_graphics::text::Text;
···1717const MAX_LINE_LEN: usize = 76;
1818const LEFT_MARGIN: i32 = 8;
1919const TOP_MARGIN: i32 = 6;
2020-const LINE_H: i32 = 15;
2020+const LINE_H: i32 = 20;
21212222pub struct BootConsole {
2323 lines: [[u8; MAX_LINE_LEN]; MAX_LINES],
···5252 }
53535454 pub fn draw(&self, strip: &mut StripBuffer) {
5555- let style = MonoTextStyle::new(&FONT_6X13, BinaryColor::On);
5555+ let style = MonoTextStyle::new(&FONT_9X18, BinaryColor::On);
5656 for i in 0..self.count {
5757 let len = self.lengths[i] as usize;
5858 let text = core::str::from_utf8(&self.lines[i][..len]).unwrap_or("");
+15-5
kernel/src/kernel/scheduler.rs
···168168 // if sleep_deep somehow returned; this version correctly returns
169169 // early so the caller can enter_sleep and continue the loop
170170 fn handle_input<A: AppLayer>(&mut self, hw_event: Event, app_mgr: &mut A) -> bool {
171171+ let _ = tasks::IDLE_SLEEP_DUE.try_take();
172172+171173 if hw_event == Event::LongPress(Button::Power) {
172174 return true;
173175 }
174176177177+ let suppressed_before = app_mgr.suppress_deferred_input();
175178 let transition = app_mgr.dispatch_event(hw_event, &mut *self.bm_cache);
176179177180 if transition != Transition::None {
178181 app_mgr.apply_transition(transition, &mut self.handle());
182182+ tasks::request_hold_reset();
183183+ } else if app_mgr.suppress_deferred_input() != suppressed_before {
179184 tasks::request_hold_reset();
180185 }
181186···325330 // the TICK_MS timeout ensures is_busy is re-checked regularly
326331 // even during long background operations.
327332 //
328328- // first non-None transition wins; hold is reset so the held button
329329- // doesn't re-fire LongPress/Repeat for the remainder of the waveform
333333+ // first non-None transition wins; hold reset prevents the held
334334+ // button from re-firing LongPress/Repeat for the waveform
330335 async fn busy_wait_with_background<A: AppLayer>(
331336 &mut self,
332337 app_mgr: &mut A,
···357362 };
358363359364 if let Some(hw_event) = ev {
360360- if !app_mgr.suppress_deferred_input() {
365365+ let _ = tasks::IDLE_SLEEP_DUE.try_take();
366366+367367+ let suppressed_before = app_mgr.suppress_deferred_input();
368368+ if !suppressed_before {
361369 let t = app_mgr.dispatch_event(hw_event, &mut *self.bm_cache);
362370 if t != Transition::None && deferred.is_none() {
363371 deferred = Some(t);
372372+ tasks::request_hold_reset();
373373+ } else if app_mgr.suppress_deferred_input() != suppressed_before {
364374 tasks::request_hold_reset();
365375 }
366376 }
···376386 // on real hardware this never returns (wake = full MCU reset)
377387 pub async fn enter_sleep(&mut self, reason: &str) {
378388 use embedded_graphics::mono_font::MonoTextStyle;
379379- use embedded_graphics::mono_font::ascii::FONT_6X13;
389389+ use embedded_graphics::mono_font::ascii::FONT_9X18;
380390 use embedded_graphics::pixelcolor::BinaryColor;
381391 use embedded_graphics::prelude::*;
382392 use embedded_graphics::text::Text;
···394404395405 self.epd
396406 .full_refresh_async(self.strip, &mut self.delay, &|s: &mut StripBuffer| {
397397- let style = MonoTextStyle::new(&FONT_6X13, BinaryColor::On);
407407+ let style = MonoTextStyle::new(&FONT_9X18, BinaryColor::On);
398408 let _ = Text::new("(sleep)", Point::new(210, 400), style).draw(s);
399409 })
400410 .await;
+1-1
kernel/src/lib.rs
···11// pulp-kernel -- hardware drivers, scheduling, and system core
22//
33// generic over AppLayer; never imports concrete apps or fonts
44-// ships a built-in mono font (FONT_6X13) for boot console and
44+// ships a built-in mono font (FONT_9X18) for boot console and
55// sleep screen; distros bring their own proportional fonts
6677#![no_std]
+6-6
kernel/src/ui/widget.rs
···11// region geometry, alignment helpers, progress bar, loading indicator
2233use embedded_graphics::{
44- mono_font::MonoTextStyle, mono_font::ascii::FONT_6X13, pixelcolor::BinaryColor, prelude::*,
44+ mono_font::MonoTextStyle, mono_font::ascii::FONT_9X18, pixelcolor::BinaryColor, prelude::*,
55 primitives::PrimitiveStyle, primitives::Rectangle, text::Text,
66};
77···154154155155// loading indicator for 1-bit e-paper
156156// draws "msg...pct%" centered vertically in the region using the
157157-// built-in FONT_6X13 mono font; works without any custom bitmap
157157+// built-in FONT_9X18 mono font; works without any custom bitmap
158158// fonts loaded, usable from any app or the kernel itself
159159//
160160// typical usage:
···176176 let _ = write!(fmt, "{}...{}%", msg, pct.min(100));
177177 let text = fmt.as_str();
178178179179- // FONT_6X13: 6px wide, 13px tall, ~10px ascent
180180- // center vertically; baseline = region.y + (h + 7) / 2
181181- let style = MonoTextStyle::new(&FONT_6X13, BinaryColor::On);
182182- let baseline_y = region.y as i32 + (region.h as i32 + 7) / 2;
179179+ // FONT_9X18: 9px wide, 18px tall, ~14px ascent
180180+ // center vertically; baseline = region.y + (h + 9) / 2
181181+ let style = MonoTextStyle::new(&FONT_9X18, BinaryColor::On);
182182+ let baseline_y = region.y as i32 + (region.h as i32 + 9) / 2;
183183 Text::new(text, Point::new(region.x as i32 + 2, baseline_y), style)
184184 .draw(strip)
185185 .unwrap();
+15-15
src/apps/reader/mod.rs
···1313use core::fmt::Write;
14141515use embedded_graphics::mono_font::MonoTextStyle;
1616-use embedded_graphics::mono_font::ascii::FONT_6X13;
1616+use embedded_graphics::mono_font::ascii::FONT_9X18;
1717use embedded_graphics::pixelcolor::BinaryColor;
1818use embedded_graphics::prelude::*;
1919use embedded_graphics::primitives::{PrimitiveStyle, Rectangle};
···4242pub(super) const HEADER_Y: u16 = CONTENT_TOP + 2;
4343pub(super) const HEADER_H: u16 = 16;
4444pub(super) const TEXT_Y: u16 = HEADER_Y + HEADER_H + 2;
4545-pub(super) const LINE_H: u16 = 13;
4646-pub(super) const CHARS_PER_LINE: usize = 66;
4747-pub(super) const LINES_PER_PAGE: usize = 58;
4545+pub(super) const LINE_H: u16 = 20;
4646+pub(super) const CHARS_PER_LINE: usize = 51;
4747+pub(super) const LINES_PER_PAGE: usize = 37;
4848pub(super) const PAGE_BUF: usize = 8192;
4949pub(super) const MAX_PAGES: usize = 1024;
5050···7979 POSITION_OVERLAY_H,
8080);
81818282-pub(super) const LOADING_REGION: Region = Region::new(MARGIN, TEXT_Y, 464, 18);
8282+pub(super) const LOADING_REGION: Region = Region::new(MARGIN, TEXT_Y, 464, 24);
83838484pub const QA_FONT_SIZE: u8 = 1;
8585pub(super) const QA_PREV_CHAPTER: u8 = 3;
···595595 if let Some(f) = font {
596596 f.draw_aligned(strip, region, text, align, BinaryColor::On);
597597 } else {
598598- let tw = text.len() as u32 * 6;
599599- let pos = align.position(region, embedded_graphics::geometry::Size::new(tw, 13));
600600- let style = MonoTextStyle::new(&FONT_6X13, BinaryColor::On);
601601- Text::new(text, Point::new(pos.x, pos.y + 13), style)
598598+ let tw = text.len() as u32 * 9;
599599+ let pos = align.position(region, embedded_graphics::geometry::Size::new(tw, 18));
600600+ let style = MonoTextStyle::new(&FONT_9X18, BinaryColor::On);
601601+ Text::new(text, Point::new(pos.x, pos.y + 18), style)
602602 .draw(strip)
603603 .unwrap();
604604 }
···12971297 font.draw_str_fg(strip, entry.title_str(), fg, cx, baseline);
12981298 }
12991299 } else {
13001300- let style = MonoTextStyle::new(&FONT_6X13, BinaryColor::On);
13001300+ let style = MonoTextStyle::new(&FONT_9X18, BinaryColor::On);
13011301 let vis_max = (TEXT_AREA_H / LINE_H) as usize;
13021302 let visible = vis_max.min(toc_len.saturating_sub(self.epub.toc_scroll));
13031303 for i in 0..visible {
···14221422 }
14231423 }
14241424 } else {
14251425- let style = MonoTextStyle::new(&FONT_6X13, BinaryColor::On);
14251425+ let style = MonoTextStyle::new(&FONT_9X18, BinaryColor::On);
14261426 for i in 0..self.pg.line_count {
14271427 let span = self.pg.lines[i];
14281428 let start = span.start as usize;
···14991499 BinaryColor::Off,
15001500 );
15011501 } else {
15021502- let tw = text.len() as u32 * 6;
15031503- let pos = Alignment::Center.position(POSITION_OVERLAY, Size::new(tw, 13));
15041504- let style = MonoTextStyle::new(&FONT_6X13, BinaryColor::Off);
15051505- Text::new(text, Point::new(pos.x, pos.y + 13), style)
15021502+ let tw = text.len() as u32 * 9;
15031503+ let pos = Alignment::Center.position(POSITION_OVERLAY, Size::new(tw, 18));
15041504+ let style = MonoTextStyle::new(&FONT_9X18, BinaryColor::Off);
15051505+ Text::new(text, Point::new(pos.x, pos.y + 18), style)
15061506 .draw(strip)
15071507 .unwrap();
15081508 }