Raw Screen Cursor Position UNIX Bug Fix (#134)

This commit is contained in:
Timon 2019-05-03 20:15:07 +02:00 committed by GitHub
parent d31d712a38
commit 0bfebb338d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 57 additions and 119 deletions

View File

@ -32,12 +32,12 @@ members = [
] ]
[dependencies] [dependencies]
crossterm_screen = { optional = true, version = "0.2.1" } crossterm_screen = { optional = true, path = "./crossterm_screen" }
crossterm_cursor = { optional = true, version = "0.2.1" } crossterm_cursor = { optional = true, path = "./crossterm_cursor" }
crossterm_terminal = { optional = true, version = "0.2.2" } crossterm_terminal = { optional = true, version = "0.2.2" }
crossterm_style = { optional = true, version = "0.3.1" } crossterm_style = { optional = true, path = "./crossterm_style" }
crossterm_input = { optional = true, version = "0.3.3" } crossterm_input = { optional = true, path = "./crossterm_input" }
crossterm_utils = { optional = false, version = "0.2.1" } crossterm_utils = { optional = false, path = "./crossterm_utils" }
[lib] [lib]
name = "crossterm" name = "crossterm"

View File

@ -16,4 +16,4 @@ winapi = { version = "0.3.7", features = ["wincon","winnt","minwindef"] }
crossterm_winapi = "0.1.2" crossterm_winapi = "0.1.2"
[dependencies] [dependencies]
crossterm_utils = "0.2.1" crossterm_utils = { path = "../crossterm_utils" }

View File

@ -1,28 +1,32 @@
use crossterm_utils::sys::unix; use crossterm_utils::sys::unix::{self, RAW_MODE_ENABLED};
use std::io::{self, Error, ErrorKind, Read, Write}; use std::io::{self, Error, ErrorKind, Read, Write};
/// Get the cursor position based on the current platform. /// Get the cursor position based on the current platform.
#[cfg(unix)] #[cfg(unix)]
pub fn get_cursor_position() -> (u16, u16) { pub fn get_cursor_position() -> (u16, u16) {
if unsafe { RAW_MODE_ENABLED } {
if let Ok(pos) = pos_raw() {
pos
} else {
(0, 0)
}
} else {
if let Ok(pos) = pos() { if let Ok(pos) = pos() {
pos pos
} else { } else {
(0, 0) (0, 0)
} }
} }
}
pub fn pos() -> io::Result<(u16, u16)> { pub fn pos() -> io::Result<(u16, u16)> {
// if we enable raw modes with screen, this could cause problems if raw mode is already enabled in application.
// I am not completely happy with this approach so feel free to find an other way.
unsafe {
if !unix::RAW_MODE_ENABLED_BY_USER || !unix::RAW_MODE_ENABLED_BY_SYSTEM {
// set this boolean so that we know that the systems has enabled raw mode.
unix::RAW_MODE_ENABLED_BY_SYSTEM = true;
unix::into_raw_mode()?; unix::into_raw_mode()?;
} let pos = pos_raw();
unix::disable_raw_mode()?;
pos
} }
pub fn pos_raw() -> io::Result<(u16, u16)> {
// Where is the cursor? // Where is the cursor?
// Use `ESC [ 6 n`. // Use `ESC [ 6 n`.
let mut stdout = io::stdout(); let mut stdout = io::stdout();
@ -69,21 +73,10 @@ pub fn pos() -> io::Result<(u16, u16)> {
let (cols, c) = read_num()?; let (cols, c) = read_num()?;
// Expect `R` // Expect `R`
let res = if c == 'R' { if c == 'R' {
// subtract one to get 0-based coords // subtract one to get 0-based coords
Ok(((cols - 1) as u16, (rows - 1) as u16)) Ok(((cols - 1) as u16, (rows - 1) as u16))
} else { } else {
return Err(Error::new(ErrorKind::Other, "test")); Err(Error::new(ErrorKind::Other, "test"))
};
// If raw mode is enabled from else where in the application (by the user) we do not want to disable raw modes.
// I am not completely happy with this approach so feel free to find an other way.
unsafe {
if unix::RAW_MODE_ENABLED_BY_SYSTEM && !unix::RAW_MODE_ENABLED_BY_USER {
unix::RAW_MODE_ENABLED_BY_SYSTEM = false;
unix::disable_raw_mode()?;
} }
} }
res
}

View File

@ -19,5 +19,5 @@ crossterm_winapi = "0.1.2"
libc = "0.2.51" libc = "0.2.51"
[dependencies] [dependencies]
crossterm_utils = "0.2.1" crossterm_utils = {path = "../crossterm_utils"}
crossterm_screen = "0.2.1" crossterm_screen = {path = "../crossterm_screen"}

View File

@ -128,7 +128,7 @@ impl Iterator for SyncReader {
}, },
Ok(2) => { Ok(2) => {
let option_iter = &mut Some(buf[1]).into_iter(); let option_iter = &mut Some(buf[1]).into_iter();
let mut iter = option_iter.map(|c| Ok(c)).chain(source.bytes()); let iter = option_iter.map(|c| Ok(c)).chain(source.bytes());
if let Ok(e) = parse_event(buf[0], &mut iter.flatten()) { if let Ok(e) = parse_event(buf[0], &mut iter.flatten()) {
self.leftover = option_iter.next(); self.leftover = option_iter.next();
Some(e) Some(e)

View File

@ -1,4 +1,3 @@
use crossterm_utils::sys::unix;
use std::fs; use std::fs;
use std::io; use std::io;
use std::os::unix::io::AsRawFd; use std::os::unix::io::AsRawFd;

View File

@ -12,7 +12,7 @@ readme = "README.md"
edition = "2018" edition = "2018"
[dependencies] [dependencies]
crossterm_utils = "0.2.1" crossterm_utils = {path = "../crossterm_utils"}
[target.'cfg(windows)'.dependencies] [target.'cfg(windows)'.dependencies]
winapi = { version = "0.3.7", features = ["minwindef", "wincon"] } winapi = { version = "0.3.7", features = ["minwindef", "wincon"] }

View File

@ -24,7 +24,7 @@ pub struct AlternateScreen {
command: Box<(dyn IAlternateScreenCommand + Sync + Send)>, command: Box<(dyn IAlternateScreenCommand + Sync + Send)>,
#[cfg(unix)] #[cfg(unix)]
command: sys::ToAlternateScreenCommand, command: sys::ToAlternateScreenCommand,
raw_screen: Option<RawScreen>, _raw_screen: Option<RawScreen>,
} }
impl AlternateScreen { impl AlternateScreen {
@ -56,13 +56,13 @@ impl AlternateScreen {
let raw_screen = RawScreen::into_raw_mode()?; let raw_screen = RawScreen::into_raw_mode()?;
return Ok(AlternateScreen { return Ok(AlternateScreen {
command, command,
raw_screen: Some(raw_screen), _raw_screen: Some(raw_screen),
}); });
} }
Ok(AlternateScreen { Ok(AlternateScreen {
command, command,
raw_screen: None, _raw_screen: None,
}) })
} }

View File

@ -5,8 +5,7 @@ pub use libc::{c_int, termios as Termios};
use std::{io, mem}; use std::{io, mem};
static mut ORIGINAL_TERMINAL_MODE: Option<Termios> = None; static mut ORIGINAL_TERMINAL_MODE: Option<Termios> = None;
pub static mut RAW_MODE_ENABLED_BY_SYSTEM: bool = false; pub static mut RAW_MODE_ENABLED: bool = false;
pub static mut RAW_MODE_ENABLED_BY_USER: bool = false;
fn unwrap(t: i32) -> io::Result<()> { fn unwrap(t: i32) -> io::Result<()> {
if t == -1 { if t == -1 {
@ -48,10 +47,11 @@ pub fn into_raw_mode() -> io::Result<()> {
unsafe { unsafe {
if ORIGINAL_TERMINAL_MODE.is_none() { if ORIGINAL_TERMINAL_MODE.is_none() {
ORIGINAL_TERMINAL_MODE = Some(prev_ios.clone()) ORIGINAL_TERMINAL_MODE = Some(prev_ios.clone());
}
} }
RAW_MODE_ENABLED = true;
}
raw_terminal_attr(&mut ios); raw_terminal_attr(&mut ios);
set_terminal_attr(&ios)?; set_terminal_attr(&ios)?;
Ok(()) Ok(())
@ -61,6 +61,8 @@ pub fn disable_raw_mode() -> io::Result<()> {
unsafe { unsafe {
if ORIGINAL_TERMINAL_MODE.is_some() { if ORIGINAL_TERMINAL_MODE.is_some() {
set_terminal_attr(&ORIGINAL_TERMINAL_MODE.unwrap())?; set_terminal_attr(&ORIGINAL_TERMINAL_MODE.unwrap())?;
RAW_MODE_ENABLED = false;
} }
} }
Ok(()) Ok(())

View File

@ -1,76 +1,20 @@
extern crate crossterm; extern crate crossterm;
//use crossterm::{Color, Crossterm}; use crossterm::{Color, Crossterm};
use crossterm::{
AlternateScreen, Attribute, ClearType, Crossterm, InputEvent, KeyEvent, Styler, TerminalCursor,
};
use std::io::Write;
use std::{io, thread, time};
fn run() -> io::Result<()> {
let alternate_screen = AlternateScreen::to_alternate(true)?;
let crossterm = crossterm::Crossterm::new();
let input = crossterm.input();
let mut crossterm_events = input.read_sync();
loop {
if let Some(event) = crossterm_events.next() {
let terminal = crossterm.terminal();
let cursor = crossterm.cursor();
cursor.goto(1, 1)?;
terminal.clear(ClearType::UntilNewLine)?;
if let InputEvent::Keyboard(key) = &event {
match key {
KeyEvent::Ctrl('q') => {
println!("quitting...");
::std::io::stdout().flush();
break;
}
_ => {
let s = "event";
println!(
" {}{}{} : {:?}",
Attribute::Bold,
s,
Attribute::Reset,
event
);
}
}
} else {
println!("disregarding unrelevant event: {:?}", event);
}
}
}
thread::sleep(time::Duration::from_secs(1));
Ok(())
}
fn main() {
match run() {
Ok(_) => {
println!("ok");
}
Err(e) => {
println!("error {:?}", e);
}
}
}
// use the `Crossterm` to get an instance to the cursor module | demonstration. // use the `Crossterm` to get an instance to the cursor module | demonstration.
//pub fn main() { pub fn main() {
// // Create the crossterm type to access different modules. // Create the crossterm type to access different modules.
// let crossterm = Crossterm::new(); let crossterm = Crossterm::new();
//
// // pass a reference to the current screen. // pass a reference to the current screen.
// let cursor = crossterm.cursor(); let cursor = crossterm.cursor();
// let color = crossterm.color(); let color = crossterm.color();
// let terminal = crossterm.terminal(); let terminal = crossterm.terminal();
// let terminal = crossterm.input(); let terminal = crossterm.input();
// let style = crossterm let style = crossterm
// .style("Black font on green background") .style("Black font on green background")
// .with(Color::Black) .with(Color::Black)
// .on(Color::Green); .on(Color::Green);
//
// // TODO: perform some actions with the instances above. // TODO: perform some actions with the instances above.
//} }

View File

@ -92,5 +92,5 @@ pub fn blink_cursor() {
} }
fn main() { fn main() {
save_and_reset_position().unwrap(); pos()
} }