diff --git a/graphics/console-draw/src/lib.rs b/graphics/console-draw/src/lib.rs index 390c5cc73a..20bc77b6e6 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: usize, +} + +impl TextBuffer { + pub fn new(max: usize) -> 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; + 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..58166f906c 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,10 @@ 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, + shift: bool, } impl FbbootlogScheme { @@ -27,6 +32,10 @@ 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, + shift: false, }; scheme.handle_handoff(); @@ -64,6 +73,91 @@ impl FbbootlogScheme { } } + pub fn handle_input(&mut self, ev: &Event) { + match ev.to_option() { + EventOption::Key(key_event) => { + if key_event.scancode == 0x2A || key_event.scancode == 0x36 { + self.shift = key_event.pressed; + } else if !key_event.pressed || !self.shift { + return; + } + 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 = self.text_buffer.lines_max; + } + _ => return, + } + } + _ => return, + } + self.handle_scrollback_render(); + } + + fn handle_scrollback_render(&mut self) { + let Some(map) = &mut self.display_map else { + return; + }; + let buffer_len = self.text_buffer.lines.len(); + let dmap = &mut console_draw::DisplayMap { + offscreen: map.inner.ptr_mut(), + width: map.inner.width(), + height: map.inner.height(), + }; + // for both extra space on wrapping text and a scrollback indicator + let spare_lines = 3; + self.is_scrollback = true; + self.scrollback_offset = cmp::min( + self.scrollback_offset, + buffer_len - dmap.height / 16 + spare_lines, + ); + let mut i = self.scrollback_offset; + 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) as usize; + if i == buffer_len || yd + spare_lines * 16 > dmap.height { + // 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 +257,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(); + } } } diff --git a/storage/ahcid/src/ahci/hba.rs b/storage/ahcid/src/ahci/hba.rs index a9d51792b0..bf5dac6e3a 100644 --- a/storage/ahcid/src/ahci/hba.rs +++ b/storage/ahcid/src/ahci/hba.rs @@ -182,23 +182,24 @@ impl HbaPort { ) -> Result { let dest: Dma<[u16; 256]> = Dma::new([0; 256]).unwrap(); - let slot = self.ata_start(clb, ctbas, |cmdheader, cmdfis, prdt_entries, _acmd| { - cmdheader.prdtl.write(1); + let slot = self + .ata_start(clb, ctbas, |cmdheader, cmdfis, prdt_entries, _acmd| { + cmdheader.prdtl.write(1); - let prdt_entry = &mut prdt_entries[0]; - prdt_entry.dba_low.write(dest.physical() as u32); - prdt_entry - .dba_high - .write((dest.physical() as u64 >> 32) as u32); - prdt_entry.dbc.write(512 | 1); + let prdt_entry = &mut prdt_entries[0]; + prdt_entry.dba_low.write(dest.physical() as u32); + prdt_entry + .dba_high + .write((dest.physical() as u64 >> 32) as u32); + prdt_entry.dbc.write(512 | 1); - cmdfis.pm.write(1 << 7); - cmdfis.command.write(cmd); - cmdfis.device.write(0); - cmdfis.countl.write(1); - cmdfis.counth.write(0); - })? - .ok_or(Error::new(EIO))?; + cmdfis.pm.write(1 << 7); + cmdfis.command.write(cmd); + cmdfis.device.write(0); + cmdfis.countl.write(1); + cmdfis.counth.write(0); + })? + .ok_or(Error::new(EIO))?; self.ata_stop(slot)?;