From b9d0599bac57a9982f5a13780e6dcd2e15f69318 Mon Sep 17 00:00:00 2001 From: Wildan M Date: Wed, 22 Oct 2025 16:27:32 +0700 Subject: [PATCH] Add scrollback into fbbootlogd --- graphics/console-draw/src/lib.rs | 34 ++++++++++ graphics/fbbootlogd/src/main.rs | 6 +- graphics/fbbootlogd/src/scheme.rs | 109 ++++++++++++++++++++++++++---- 3 files changed, 136 insertions(+), 13 deletions(-) diff --git a/graphics/console-draw/src/lib.rs b/graphics/console-draw/src/lib.rs index 390c5cc73a..93fa641cd1 100644 --- a/graphics/console-draw/src/lib.rs +++ b/graphics/console-draw/src/lib.rs @@ -260,3 +260,37 @@ impl TextScreen { } } } + +pub struct TextBuffer { + pub lines: VecDeque>, + pub lines_max: u32, +} + +impl TextBuffer { + pub fn new(max: u32) -> Self { + let mut lines = VecDeque::new(); + lines.push_back(Vec::new()); + Self { + lines, + lines_max: max, + } + } + pub fn write(&mut self, buf: &[u8]) { + if buf.is_empty() { + return; + } + + for &byte in buf { + self.lines.back_mut().unwrap().push(byte); + + if byte == b'\n' { + self.lines.push_back(Vec::new()); + } + } + + let max_len = self.lines_max as usize; + while self.lines.len() > max_len { + self.lines.pop_front(); + } + } +} diff --git a/graphics/fbbootlogd/src/main.rs b/graphics/fbbootlogd/src/main.rs index b2f723ba3f..bc3759c469 100644 --- a/graphics/fbbootlogd/src/main.rs +++ b/graphics/fbbootlogd/src/main.rs @@ -107,7 +107,11 @@ fn inner(daemon: redox_daemon::Daemon) -> ! { .expect("fbbootlogd: error while reading events") { ConsumerHandleEvent::Events(&[]) => break, - ConsumerHandleEvent::Events(_) => {} + ConsumerHandleEvent::Events(events) => { + for event in events { + scheme.handle_input(&event); + } + } ConsumerHandleEvent::Handoff => { eprintln!("fbbootlogd: handoff requested"); scheme.handle_handoff(); diff --git a/graphics/fbbootlogd/src/scheme.rs b/graphics/fbbootlogd/src/scheme.rs index f6180e280b..0b33312959 100644 --- a/graphics/fbbootlogd/src/scheme.rs +++ b/graphics/fbbootlogd/src/scheme.rs @@ -1,9 +1,10 @@ use std::collections::VecDeque; -use std::ptr; +use std::{cmp, ptr}; use console_draw::TextScreen; use graphics_ipc::v2::V2GraphicsHandle; use inputd::ConsumerHandle; +use orbclient::{Event, EventOption}; use redox_scheme::scheme::SchemeSync; use redox_scheme::{CallerCtx, OpenResult}; use syscall::schemev2::NewFdFlags; @@ -19,6 +20,9 @@ pub struct FbbootlogScheme { pub input_handle: ConsumerHandle, display_map: Option, text_screen: console_draw::TextScreen, + text_buffer: console_draw::TextBuffer, + is_scrollback: bool, + scrollback_offset: usize, } impl FbbootlogScheme { @@ -27,6 +31,9 @@ impl FbbootlogScheme { input_handle: ConsumerHandle::new_vt().expect("fbbootlogd: Failed to open vt"), display_map: None, text_screen: console_draw::TextScreen::new(), + text_buffer: console_draw::TextBuffer::new(1000), + is_scrollback: false, + scrollback_offset: 1000, }; scheme.handle_handoff(); @@ -64,6 +71,81 @@ impl FbbootlogScheme { } } + pub fn handle_input(&mut self, ev: &Event) { + match ev.to_option() { + EventOption::Key(key_event) => { + match key_event.scancode { + 0x48 => { + // Up + if self.scrollback_offset >= 1 { + self.scrollback_offset -= 1; + } + } + 0x49 => { + // Page up + if self.scrollback_offset >= 10 { + self.scrollback_offset -= 10; + } else { + self.scrollback_offset = 0; + } + } + 0x50 => { + // Down + self.scrollback_offset += 1; + } + 0x51 => { + // Page down + self.scrollback_offset += 10; + } + 0x47 => { + // Home + self.scrollback_offset = 0; + } + 0x4F => { + // End + self.scrollback_offset = 1000; + } + _ => return, + } + } + _ => return, + } + self.handle_scrollback_render(); + } + + fn handle_scrollback_render(&mut self) { + let Some(map) = &mut self.display_map else { + return; + }; + self.is_scrollback = true; + let buffer_len = self.text_buffer.lines.len(); + self.scrollback_offset = cmp::min(self.scrollback_offset, buffer_len - 10); + let mut i = self.scrollback_offset; + let dmap = &mut console_draw::DisplayMap { + offscreen: map.inner.ptr_mut(), + width: map.inner.width(), + height: map.inner.height(), + }; + self.text_screen + .write(dmap, b"\x1B[1;1H\x1B[2J", &mut VecDeque::new()); + while i < buffer_len { + let mut damage = + self.text_screen + .write(dmap, &self.text_buffer.lines[i][..], &mut VecDeque::new()); + i += 1; + let yd = damage.y + damage.height; + if i == buffer_len || yd + 48 >= dmap.height as u32 { + // render until end of screen + damage.height = (dmap.height as u32) - damage.y; + map.display_handle.update_plane(0, map.fb, damage).unwrap(); + self.is_scrollback = i < buffer_len; + break; + } else { + map.display_handle.update_plane(0, map.fb, damage).unwrap(); + } + } + } + fn handle_resize(map: &mut DisplayMap, text_screen: &mut TextScreen) { let (width, height) = match map.display_handle.display_size(0) { Ok((width, height)) => (width, height), @@ -163,19 +245,22 @@ impl SchemeSync for FbbootlogScheme { ) -> Result { if let Some(map) = &mut self.display_map { Self::handle_resize(map, &mut self.text_screen); + self.text_buffer.write(buf); - let damage = self.text_screen.write( - &mut console_draw::DisplayMap { - offscreen: map.inner.ptr_mut(), - width: map.inner.width(), - height: map.inner.height(), - }, - buf, - &mut VecDeque::new(), - ); + if !self.is_scrollback { + let damage = self.text_screen.write( + &mut console_draw::DisplayMap { + offscreen: map.inner.ptr_mut(), + width: map.inner.width(), + height: map.inner.height(), + }, + buf, + &mut VecDeque::new(), + ); - if let Some(map) = &self.display_map { - map.display_handle.update_plane(0, map.fb, damage).unwrap(); + if let Some(map) = &self.display_map { + map.display_handle.update_plane(0, map.fb, damage).unwrap(); + } } }