-
-
Notifications
You must be signed in to change notification settings - Fork 131
feat: add get cursor position #269
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
extern crate console; | ||
use console::Term; | ||
use std::io; | ||
|
||
fn draw_point(term: &Term, width: usize) -> io::Result<()> { | ||
// no cheating...get the cursor position here | ||
let (x, y) = term.cursor_position()?; | ||
let str = format!("({x}, {y})"); | ||
let w = str.len() + 2; | ||
if x + w > width { | ||
term.move_cursor_left(w - 1)?; | ||
term.write_str(&format!("{str} •"))?; | ||
} else { | ||
term.write_str(&format!("• {str}"))?; | ||
} | ||
Ok(()) | ||
} | ||
|
||
fn main() -> io::Result<()> { | ||
let term = Term::stdout(); | ||
term.hide_cursor()?; | ||
term.clear_screen()?; | ||
|
||
let (height, width): (usize, usize) = (term.size().0 as usize, term.size().1 as usize); | ||
|
||
// draw the four corners | ||
term.move_cursor_to(0, 0)?; | ||
draw_point(&term, width)?; | ||
// this tests the formatting logic | ||
for i in 0..20 { | ||
term.move_cursor_to(width - i - 1, i)?; | ||
draw_point(&term, width)?; | ||
} | ||
term.move_cursor_to(0, height - 2)?; | ||
draw_point(&term, width)?; | ||
term.move_cursor_to(width, height - 2)?; | ||
draw_point(&term, width)?; | ||
|
||
for _ in 0..10 { | ||
let x = rand::random_range(..=width - 1); | ||
let y = rand::random_range(1..=height - 3); | ||
term.move_cursor_to(x, y)?; | ||
draw_point(&term, width)?; | ||
} | ||
|
||
term.move_cursor_to(0, height)?; | ||
term.show_cursor()?; | ||
|
||
Ok(()) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,7 +3,7 @@ use core::ptr; | |
use core::{fmt::Display, mem, str}; | ||
use std::env; | ||
use std::fs; | ||
use std::io::{self, BufRead, BufReader}; | ||
use std::io::{self, BufRead, BufReader, Write}; | ||
use std::os::fd::{AsRawFd, RawFd}; | ||
|
||
#[cfg(not(target_os = "macos"))] | ||
|
@@ -258,6 +258,44 @@ fn read_single_key_impl(fd: RawFd) -> Result<Key, io::Error> { | |
'H' => Ok(Key::Home), | ||
'F' => Ok(Key::End), | ||
'Z' => Ok(Key::BackTab), | ||
'0'..='9' => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does it make sense to keep this inside (Part of this is is the very deeply nested indentation level making this code harder to follow.) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I could refactor this to reduce the indentation at several levels. I was trying to keep my edits minimal. If it were me I would refactor the block to be something like:
Do you think moving the match arm bodies to functions is easier to read?
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I might prefer that but let's not do it in this PR. For this PR, do you think we need the specific |
||
// This is a special case for handling the response to a cursor | ||
// position request ("\x1b[6n"). The response is given as | ||
// "\x1b[<row>;<col>R", where <row> and <col> are numbers." | ||
let mut buf = String::new(); | ||
buf.push(c2); | ||
while let Some(c) = read_single_char(fd)? { | ||
if c == 'R' { | ||
break; | ||
} else if c.is_ascii_digit() || c == ';' { | ||
buf.push(c); | ||
if buf.len() > 64 { | ||
// Prevent infinite loop in case of malformed input | ||
return Ok(Key::UnknownEscSeq( | ||
buf.chars().collect(), | ||
)); | ||
} | ||
} else { | ||
// If we encounter an unexpected character, we treat it | ||
// as an unknown escape sequence | ||
return Ok(Key::UnknownEscSeq(vec![c1, c2, c])); | ||
} | ||
} | ||
// buf now contains "<row>;<col>" | ||
let v = buf | ||
.split(';') | ||
.map(|s| s.parse::<usize>().unwrap_or(0)) | ||
.collect::<Vec<_>>(); | ||
if v.len() == 2 { | ||
// x is column, y is row | ||
Ok(Key::CursorPosition( | ||
v[1].saturating_sub(1), | ||
v[0].saturating_sub(1), | ||
)) | ||
} else { | ||
Ok(Key::UnknownEscSeq(buf.chars().collect())) | ||
} | ||
} | ||
_ => { | ||
let c3 = read_single_char(fd)?; | ||
if let Some(c3) = c3 { | ||
|
@@ -335,6 +373,23 @@ fn read_single_key_impl(fd: RawFd) -> Result<Key, io::Error> { | |
} | ||
} | ||
|
||
pub(crate) fn cursor_position() -> io::Result<(usize, usize)> { | ||
// Send the cursor position request escape sequence | ||
print!("\x1b[6n"); | ||
io::stdout().flush()?; | ||
|
||
// Read the response from the terminal | ||
let key = read_single_key(false)?; | ||
dhuseby marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
||
match key { | ||
Key::CursorPosition(x, y) => Ok((x, y)), | ||
_ => Err(io::Error::new( | ||
io::ErrorKind::InvalidData, | ||
"Unexpected response to cursor position request", | ||
)), | ||
} | ||
} | ||
|
||
pub(crate) fn read_single_key(ctrlc_key: bool) -> io::Result<Key> { | ||
let input = Input::unbuffered()?; | ||
|
||
|
Uh oh!
There was an error while loading. Please reload this page.