From 23bb4ae1e667ab38b83f391b2bc79ebffcd8e49e Mon Sep 17 00:00:00 2001 From: gwenn Date: Sat, 25 Nov 2023 16:58:34 +0100 Subject: [PATCH] Change cursor visibility --- Cargo.toml | 1 - examples/read_password.rs | 6 +++-- src/lib.rs | 8 +++++++ src/tty/mod.rs | 3 +++ src/tty/test.rs | 5 ++++ src/tty/unix.rs | 26 +++++++++++++++++++++ src/tty/windows.rs | 48 +++++++++++++++++++++++++++------------ 7 files changed, 79 insertions(+), 18 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index cbe1fdbb1..3e9492efb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -50,7 +50,6 @@ termios = { version = "0.3.3", optional = true } [target.'cfg(windows)'.dependencies] winapi = { version = "0.3", features = ["consoleapi", "handleapi", "synchapi", "minwindef", "processenv", "std", "winbase", "wincon", "winerror", "winuser"] } -scopeguard = "1.1" clipboard-win = "5.0" [dev-dependencies] diff --git a/examples/read_password.rs b/examples/read_password.rs index ddcdd79e9..d669d511b 100644 --- a/examples/read_password.rs +++ b/examples/read_password.rs @@ -14,13 +14,13 @@ impl Highlighter for MaskingHighlighter { fn highlight<'l>(&self, line: &'l str, _pos: usize) -> Cow<'l, str> { use unicode_width::UnicodeWidthStr; if self.masking { - Owned("*".repeat(line.width())) + Owned(" ".repeat(line.width())) } else { Borrowed(line) } } - fn highlight_char(&self, line: &str, pos: usize, forced: bool) -> bool { + fn highlight_char(&self, _line: &str, _pos: usize, _forced: bool) -> bool { self.masking } } @@ -37,7 +37,9 @@ fn main() -> Result<()> { rl.helper_mut().expect("No helper").masking = true; rl.set_color_mode(ColorMode::Forced); // force masking rl.set_auto_add_history(false); // make sure password is not added to history + let mut guard = rl.set_cursor_visibility(false)?; let passwd = rl.readline("Password:")?; + guard.take(); println!("Secret: {passwd}"); Ok(()) } diff --git a/src/lib.rs b/src/lib.rs index e15bb3029..b50e807c6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -917,6 +917,14 @@ impl Editor { pub fn create_external_printer(&mut self) -> Result<::ExternalPrinter> { self.term.create_external_printer() } + + /// Change cursor visibility + pub fn set_cursor_visibility( + &mut self, + visible: bool, + ) -> Result::CursorGuard>> { + self.term.set_cursor_visibility(visible) + } } impl config::Configurer for Editor { diff --git a/src/tty/mod.rs b/src/tty/mod.rs index 24f04f751..8bb34c236 100644 --- a/src/tty/mod.rs +++ b/src/tty/mod.rs @@ -220,6 +220,7 @@ pub trait Term { type Writer: Renderer; // rl_outstream type Mode: RawMode; type ExternalPrinter: ExternalPrinter; + type CursorGuard; fn new( color_mode: ColorMode, @@ -246,6 +247,8 @@ pub trait Term { fn writeln(&self) -> Result<()>; /// Create an external printer fn create_external_printer(&mut self) -> Result; + /// Change cursor visibility + fn set_cursor_visibility(&mut self, visible: bool) -> Result>; } // If on Windows platform import Windows TTY module diff --git a/src/tty/test.rs b/src/tty/test.rs index 88e22a6e0..d26af5e0a 100644 --- a/src/tty/test.rs +++ b/src/tty/test.rs @@ -160,6 +160,7 @@ pub struct DummyTerminal { } impl Term for DummyTerminal { + type CursorGuard = (); type ExternalPrinter = DummyExternalPrinter; type KeyMap = KeyMap; type Mode = Mode; @@ -219,6 +220,10 @@ impl Term for DummyTerminal { Ok(DummyExternalPrinter {}) } + fn set_cursor_visibility(&mut self, _: bool) -> Result> { + Ok(None) + } + fn writeln(&self) -> Result<()> { Ok(()) } diff --git a/src/tty/unix.rs b/src/tty/unix.rs index 00e33dc1e..75dace14a 100644 --- a/src/tty/unix.rs +++ b/src/tty/unix.rs @@ -1139,6 +1139,23 @@ fn write_all(fd: RawFd, buf: &str) -> nix::Result<()> { Ok(()) } +pub struct PosixCursorGuard(RawFd); + +impl Drop for PosixCursorGuard { + fn drop(&mut self) { + let _ = set_cursor_visibility(self.0, true); + } +} + +fn set_cursor_visibility(fd: RawFd, visible: bool) -> Result> { + write_all(fd, if visible { "\x1b[?25h" } else { "\x1b[?25l" })?; + Ok(if visible { + None + } else { + Some(PosixCursorGuard(fd)) + }) +} + #[cfg(not(feature = "signal-hook"))] static mut SIGWINCH_PIPE: RawFd = -1; #[cfg(not(feature = "signal-hook"))] @@ -1236,6 +1253,7 @@ impl PosixTerminal { } impl Term for PosixTerminal { + type CursorGuard = PosixCursorGuard; type ExternalPrinter = ExternalPrinter; type KeyMap = PosixKeyMap; type Mode = PosixMode; @@ -1405,6 +1423,14 @@ impl Term for PosixTerminal { tty_out: self.tty_out, }) } + + fn set_cursor_visibility(&mut self, visible: bool) -> Result> { + if self.is_out_a_tty { + set_cursor_visibility(self.tty_out, visible) + } else { + Ok(None) + } + } } #[allow(unused_must_use)] diff --git a/src/tty/windows.rs b/src/tty/windows.rs index caba851a5..8147ee0ce 100644 --- a/src/tty/windows.rs +++ b/src/tty/windows.rs @@ -330,8 +330,8 @@ impl ConsoleRenderer { })?) } - fn set_cursor_visible(&mut self, visible: BOOL) -> Result<()> { - set_cursor_visible(self.conout, visible) + fn set_cursor_visibility(&mut self, visible: bool) -> Result> { + set_cursor_visibility(self.conout, visible) } // You can't have both ENABLE_WRAP_AT_EOL_OUTPUT and @@ -374,16 +374,28 @@ impl ConsoleRenderer { } } -fn set_cursor_visible(handle: HANDLE, visible: BOOL) -> Result<()> { +pub struct ConsoleCursorGuard(HANDLE); + +impl Drop for ConsoleCursorGuard { + fn drop(&mut self) { + let _ = set_cursor_visibility(self.0, true); + } +} + +fn set_cursor_visibility(handle: HANDLE, visible: bool) -> Result> { let mut info = unsafe { mem::zeroed() }; check(unsafe { wincon::GetConsoleCursorInfo(handle, &mut info) })?; - if info.bVisible == visible { - return Ok(()); + let b = if visible { TRUE } else { FALSE }; + if info.bVisible == b { + return Ok(None); } - info.bVisible = visible; - Ok(check(unsafe { - wincon::SetConsoleCursorInfo(handle, &info) - })?) + info.bVisible = b; + check(unsafe { wincon::SetConsoleCursorInfo(handle, &info) })?; + Ok(if visible { + None + } else { + Some(ConsoleCursorGuard(handle)) + }) } impl Renderer for ConsoleRenderer { @@ -449,11 +461,8 @@ impl Renderer for ConsoleRenderer { } } let info = self.get_console_screen_buffer_info()?; - self.set_cursor_visible(FALSE)?; // just to avoid flickering - let handle = self.conout; - scopeguard::defer! { - let _ = set_cursor_visible(handle, TRUE); - } + // just to avoid flickering + let mut guard = self.set_cursor_visibility(false)?; // position at the start of the prompt, clear to end of previous input self.clear_old_rows(&info, old_layout)?; // display prompt, input line and hint @@ -465,7 +474,7 @@ impl Renderer for ConsoleRenderer { coord.X = cursor.col as i16; coord.Y -= (end_pos.row - cursor.row) as i16; self.set_console_cursor_position(coord, info.dwSize)?; - + guard.take(); Ok(()) } @@ -628,6 +637,7 @@ impl Console { } impl Term for Console { + type CursorGuard = ConsoleCursorGuard; type ExternalPrinter = ExternalPrinter; type KeyMap = ConsoleKeyMap; type Mode = ConsoleMode; @@ -831,6 +841,14 @@ impl Term for Console { conout: self.conout, }) } + + fn set_cursor_visibility(&mut self, visible: bool) -> Result> { + if self.conout_isatty { + set_cursor_visibility(self.conout, visible) + } else { + Ok(None) + } + } } impl Drop for Console {