diff --git a/userspace/lib/cross/src/term.rs b/userspace/lib/cross/src/term.rs index 767d05a4..98abdce9 100644 --- a/userspace/lib/cross/src/term.rs +++ b/userspace/lib/cross/src/term.rs @@ -192,6 +192,55 @@ impl TerminalInput { self.parse_utf8_from_buffer() } + pub fn read_cursor_position_report(&mut self) -> io::Result> { + #[derive(Clone, Copy)] + enum State { + ReadRow, + ReadColumn, + } + + // CSI r ; c R + let mut ch_buffer = [0]; + + let mut state = State::ReadRow; + let mut row = 0; + let mut column = 0; + + self.stdin.read_exact(&mut ch_buffer)?; + if ch_buffer[0] != b'\x1B' { + return Ok(None); + } + self.stdin.read_exact(&mut ch_buffer)?; + if ch_buffer[0] != b'[' { + return Ok(None); + } + + loop { + self.stdin.read_exact(&mut ch_buffer)?; + let ch = ch_buffer[0]; + + match (state, ch) { + (State::ReadRow, b';') => { + state = State::ReadColumn; + } + (State::ReadRow, b'0'..=b'9') => { + row *= 10; + row += (ch - b'0') as u32; + } + (State::ReadColumn, b'R') => { + return Ok(Some((row, column))); + } + (State::ReadColumn, b'0'..=b'9') => { + column *= 10; + column += (ch - b'0') as u32; + } + (_, _) => { + return Ok(None); + } + } + } + } + pub fn read_key(&mut self) -> io::Result { while self.buffer_len < self.buffer.len() { if let Some(key) = self.parse_from_buffer() { diff --git a/userspace/lib/libterm/src/lib.rs b/userspace/lib/libterm/src/lib.rs index 8b09b1d6..29cd7fea 100644 --- a/userspace/lib/libterm/src/lib.rs +++ b/userspace/lib/libterm/src/lib.rs @@ -28,6 +28,7 @@ pub trait RawTerminal { fn raw_leave_alternate_mode(&mut self) -> io::Result<()>; fn raw_clear_all(&mut self) -> io::Result<()>; fn raw_clear_line(&mut self, what: u32) -> io::Result<()>; + fn raw_report_cursor_position(&mut self) -> io::Result<()>; fn raw_move_cursor(&mut self, row: usize, column: usize) -> io::Result<()>; fn raw_set_cursor_style(&mut self, style: CursorStyle) -> io::Result<()>; fn raw_set_color(&mut self, fgbg: u32, color: Color) -> io::Result<()>; @@ -84,6 +85,10 @@ impl RawTerminal for Stdout { write!(self, "\x1B[{}K", what) } + fn raw_report_cursor_position(&mut self) -> io::Result<()> { + write!(self, "\x1B[6n") + } + fn raw_move_cursor(&mut self, row: usize, column: usize) -> io::Result<()> { write!(self, "\x1B[{};{}f", row + 1, column + 1) } @@ -140,9 +145,16 @@ impl Term { Ok(Self { stdin, stdout }) } + pub fn report_cursor_position(&mut self) -> io::Result> { + self.stdout.raw_report_cursor_position()?; + self.stdout.flush()?; + self.stdin.read_cursor_position_report() + } + pub fn set_cursor_position(&mut self, row: usize, column: usize) -> io::Result<()> { self.stdout.raw_move_cursor(row, column) } + pub fn set_cursor_visible(&mut self, visible: bool) -> io::Result<()> { #[cfg(unix)] { diff --git a/userspace/sysutils/src/tst.rs b/userspace/sysutils/src/tst.rs index ff7fecb4..73eedcc2 100644 --- a/userspace/sysutils/src/tst.rs +++ b/userspace/sysutils/src/tst.rs @@ -13,6 +13,9 @@ fn main() { term.set_cursor_position(i, 0).ok(); write!(term, "{key:?}").ok(); } + let (row, column) = term.report_cursor_position().unwrap().unwrap(); + term.set_cursor_position(25, 0).ok(); + write!(term, "row = {row}, column = {column}").ok(); term.flush().ok(); let key = term.read_key().unwrap();