fix(fbcond): buffer TextScreen writes while display map is unavailable

fbcond can start before vesad has registered the display, leaving
TextScreen.display.map as None. The old write() silently dropped all
output in that state, so getty/login prompts written during the race
window never appeared.

- Add pending_writes buffer to TextScreen.
- Buffer writes when display.map is None and log a warning.
- Flush buffered writes after a successful display handoff.
- Upgrade handoff success logs from debug to info so they appear in
the default boot log.
This commit is contained in:
2026-07-06 23:06:03 +03:00
parent d1fe24066f
commit f0ff6a7976
2 changed files with 41 additions and 2 deletions
+2 -2
View File
@@ -33,11 +33,11 @@ impl Display {
};
let new_display_handle = V2GraphicsHandle::from_file(display_file).unwrap();
log::debug!("fbcond: Opened new display");
log::info!("fbcond: Opened new display");
match V2DisplayMap::new(new_display_handle) {
Ok(map) => {
log::debug!(
log::info!(
"fbcond: Mapped new display with size {}x{}",
map.buffer.buffer().size().0,
map.buffer.buffer().size().1,
+39
View File
@@ -10,6 +10,7 @@ pub struct TextScreen {
inner: console_draw::TextScreen,
ctrl: bool,
input: VecDeque<u8>,
pending_writes: Vec<Vec<u8>>,
}
impl TextScreen {
@@ -19,12 +20,16 @@ impl TextScreen {
inner: console_draw::TextScreen::new(),
ctrl: false,
input: VecDeque::new(),
pending_writes: Vec::new(),
}
}
pub fn handle_handoff(&mut self) {
log::info!("fbcond: Performing handoff");
self.display.reopen_for_handoff();
if self.display.map.is_some() {
self.flush_pending_writes();
}
}
pub fn input(&mut self, event: &Event) {
@@ -127,8 +132,42 @@ impl TextScreen {
let damage = self.inner.write(map, buf, &mut self.input);
self.display.sync_rect(damage);
} else {
log::warn!(
"fbcond: TextScreen::write() called while display map is None; \
buffering {} bytes ({} pending chunks). Will flush after handoff.",
buf.len(),
self.pending_writes.len() + 1,
);
self.pending_writes.push(buf.to_vec());
}
Ok(buf.len())
}
pub fn flush_pending_writes(&mut self) {
if self.pending_writes.is_empty() {
return;
}
let count = self.pending_writes.len();
log::info!(
"fbcond: Flushing {} pending write chunks after display handoff",
count,
);
if let Some(map) = &mut self.display.map {
for pending in self.pending_writes.drain(..) {
Display::handle_resize(map, &mut self.inner);
let damage = self.inner.write(map, &pending, &mut self.input);
map.dirty_fb(damage).unwrap();
}
} else {
log::error!(
"fbcond: flush_pending_writes called but display map is still None; \
keeping {} buffered chunks",
self.pending_writes.len(),
);
}
}
}