Files
RedBear-OS/drivers/graphics/fbcond/src/text.rs
T
Red Bear OS bd595851e2 base: apply Red Bear patches on latest upstream/main
251 files: init, acpid, ipcd, netcfg, ihdgd, virtio-gpud, scheme-utils,
inputd, block driver, ptyd, ramfs, randd, initfs bootstrap, path deps,
version +rb0.3.1, author attribution
2026-07-11 11:39:24 +03:00

135 lines
3.9 KiB
Rust

use std::collections::VecDeque;
use orbclient::{Event, EventOption};
use syscall::error::*;
use crate::display::Display;
use crate::keymap::Keymap;
pub struct TextScreen {
pub display: Display,
inner: console_draw::TextScreen,
ctrl: bool,
input: VecDeque<u8>,
pending_writes: Vec<Vec<u8>>,
/// Active keyboard layout. Defaults to US QWERTY.
pub keymap: Keymap,
}
impl TextScreen {
pub fn new(display: Display) -> TextScreen {
TextScreen {
display,
inner: console_draw::TextScreen::new(),
ctrl: false,
input: VecDeque::new(),
pending_writes: Vec::new(),
keymap: Keymap::us(),
}
}
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) {
let mut buf = vec![];
match event.to_option() {
EventOption::Key(key_event) => {
if key_event.scancode == 0x1D {
self.ctrl = key_event.pressed;
} else if key_event.pressed {
let sc = key_event.scancode as u8;
if let Some(seq) = self.keymap.lookup(sc) {
buf.extend_from_slice(seq);
} else {
let c = match key_event.character {
c @ 'A'..='Z' if self.ctrl => ((c as u8 - b'A') + b'\x01') as char,
c @ 'a'..='z' if self.ctrl => ((c as u8 - b'a') + b'\x01') as char,
c => c,
};
if c != '\0' {
let mut b = [0; 4];
buf.extend_from_slice(c.encode_utf8(&mut b).as_bytes());
}
}
}
}
_ => (), //TODO: Mouse in terminal
}
for &b in buf.iter() {
self.input.push_back(b);
}
}
pub fn can_read(&self) -> bool {
!self.input.is_empty()
}
}
impl TextScreen {
pub fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
let mut i = 0;
while i < buf.len() && !self.input.is_empty() {
buf[i] = self.input.pop_front().unwrap();
i += 1;
}
Ok(i)
}
pub fn write(&mut self, buf: &[u8]) -> Result<usize> {
if let Some(map) = &mut self.display.map {
Display::handle_resize(map, &mut self.inner);
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(),
);
}
}
}