Skip to content

Commit

Permalink
Merge pull request #747 from gwenn/hide_cursor
Browse files Browse the repository at this point in the history
Change cursor visibility
  • Loading branch information
gwenn authored Nov 25, 2023
2 parents 77f1d53 + 23bb4ae commit fafa0b9
Show file tree
Hide file tree
Showing 7 changed files with 79 additions and 18 deletions.
1 change: 0 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down
6 changes: 4 additions & 2 deletions examples/read_password.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
}
Expand All @@ -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(())
}
8 changes: 8 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -917,6 +917,14 @@ impl<H: Helper, I: History> Editor<H, I> {
pub fn create_external_printer(&mut self) -> Result<<Terminal as Term>::ExternalPrinter> {
self.term.create_external_printer()
}

/// Change cursor visibility
pub fn set_cursor_visibility(
&mut self,
visible: bool,
) -> Result<Option<<Terminal as Term>::CursorGuard>> {
self.term.set_cursor_visibility(visible)
}
}

impl<H: Helper, I: History> config::Configurer for Editor<H, I> {
Expand Down
3 changes: 3 additions & 0 deletions src/tty/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,7 @@ pub trait Term {
type Writer: Renderer<Reader = Self::Reader>; // rl_outstream
type Mode: RawMode;
type ExternalPrinter: ExternalPrinter;
type CursorGuard;

fn new(
color_mode: ColorMode,
Expand All @@ -246,6 +247,8 @@ pub trait Term {
fn writeln(&self) -> Result<()>;
/// Create an external printer
fn create_external_printer(&mut self) -> Result<Self::ExternalPrinter>;
/// Change cursor visibility
fn set_cursor_visibility(&mut self, visible: bool) -> Result<Option<Self::CursorGuard>>;
}

// If on Windows platform import Windows TTY module
Expand Down
5 changes: 5 additions & 0 deletions src/tty/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,7 @@ pub struct DummyTerminal {
}

impl Term for DummyTerminal {
type CursorGuard = ();
type ExternalPrinter = DummyExternalPrinter;
type KeyMap = KeyMap;
type Mode = Mode;
Expand Down Expand Up @@ -219,6 +220,10 @@ impl Term for DummyTerminal {
Ok(DummyExternalPrinter {})
}

fn set_cursor_visibility(&mut self, _: bool) -> Result<Option<()>> {
Ok(None)
}

fn writeln(&self) -> Result<()> {
Ok(())
}
Expand Down
26 changes: 26 additions & 0 deletions src/tty/unix.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Option<PosixCursorGuard>> {
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"))]
Expand Down Expand Up @@ -1236,6 +1253,7 @@ impl PosixTerminal {
}

impl Term for PosixTerminal {
type CursorGuard = PosixCursorGuard;
type ExternalPrinter = ExternalPrinter;
type KeyMap = PosixKeyMap;
type Mode = PosixMode;
Expand Down Expand Up @@ -1405,6 +1423,14 @@ impl Term for PosixTerminal {
tty_out: self.tty_out,
})
}

fn set_cursor_visibility(&mut self, visible: bool) -> Result<Option<PosixCursorGuard>> {
if self.is_out_a_tty {
set_cursor_visibility(self.tty_out, visible)
} else {
Ok(None)
}
}
}

#[allow(unused_must_use)]
Expand Down
48 changes: 33 additions & 15 deletions src/tty/windows.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Option<ConsoleCursorGuard>> {
set_cursor_visibility(self.conout, visible)
}

// You can't have both ENABLE_WRAP_AT_EOL_OUTPUT and
Expand Down Expand Up @@ -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<Option<ConsoleCursorGuard>> {
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 {
Expand Down Expand Up @@ -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
Expand All @@ -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(())
}

Expand Down Expand Up @@ -628,6 +637,7 @@ impl Console {
}

impl Term for Console {
type CursorGuard = ConsoleCursorGuard;
type ExternalPrinter = ExternalPrinter;
type KeyMap = ConsoleKeyMap;
type Mode = ConsoleMode;
Expand Down Expand Up @@ -831,6 +841,14 @@ impl Term for Console {
conout: self.conout,
})
}

fn set_cursor_visibility(&mut self, visible: bool) -> Result<Option<ConsoleCursorGuard>> {
if self.conout_isatty {
set_cursor_visibility(self.conout, visible)
} else {
Ok(None)
}
}
}

impl Drop for Console {
Expand Down

0 comments on commit fafa0b9

Please sign in to comment.