From eb0e61b1aef2fd0fddabcaec6ea318773b65717e Mon Sep 17 00:00:00 2001 From: salmonik Date: Mon, 2 Mar 2026 17:56:49 +0100 Subject: only use raw mode --- src/bin/client/main.rs | 4 --- src/bin/client/platform.rs | 28 ---------------- src/bin/client/repl.rs | 83 +++++++++++++++++++++++++++++++++++++++++----- 3 files changed, 74 insertions(+), 41 deletions(-) delete mode 100644 src/bin/client/platform.rs (limited to 'src/bin/client') diff --git a/src/bin/client/main.rs b/src/bin/client/main.rs index 9c15e9e..240ad66 100644 --- a/src/bin/client/main.rs +++ b/src/bin/client/main.rs @@ -3,14 +3,10 @@ mod macros; mod repl; mod database; mod connection; -mod platform; use repl::ClientState; fn main() { - #[cfg(windows)] - platform::win::enable_vt().unwrap(); - let mut state = ClientState::new(); if let Some(host) = state.addr.clone() { state.connect_to(&host); diff --git a/src/bin/client/platform.rs b/src/bin/client/platform.rs deleted file mode 100644 index 817c39d..0000000 --- a/src/bin/client/platform.rs +++ /dev/null @@ -1,28 +0,0 @@ -#[cfg(windows)] -pub mod win { - use std::io; - use winapi::um::consoleapi::GetConsoleMode; - use winapi::um::consoleapi::SetConsoleMode; - use winapi::um::wincon::ENABLE_VIRTUAL_TERMINAL_PROCESSING; - use winapi::um::wincon::ENABLE_ECHO_INPUT; - use winapi::um::winbase::STD_OUTPUT_HANDLE; - use winapi::um::processenv::GetStdHandle; - use winapi::shared::minwindef::DWORD; - - pub fn enable_vt() -> io::Result<()> { - unsafe { - let h = GetStdHandle(STD_OUTPUT_HANDLE); - if h.is_null() { return Err(io::Error::last_os_error()); } - - let mut mode: DWORD = 0; - if GetConsoleMode(h, &mut mode) == 0 { - return Err(io::Error::last_os_error()); - } - let new = (mode | ENABLE_VIRTUAL_TERMINAL_PROCESSING) & !ENABLE_ECHO_INPUT; - if SetConsoleMode(h, new) == 0 { - return Err(io::Error::last_os_error()); - } - Ok(()) - } - } -} diff --git a/src/bin/client/repl.rs b/src/bin/client/repl.rs index b2f6ecd..4e787cb 100644 --- a/src/bin/client/repl.rs +++ b/src/bin/client/repl.rs @@ -6,7 +6,7 @@ use std::time::Instant; use crate::connection; use crate::database; -use crossterm::{event::{read, Event, KeyCode, KeyModifiers}, terminal::{disable_raw_mode, enable_raw_mode, Clear, ClearType}, cursor::{MoveToColumn, Show}, execute}; +use crossterm::{event::{read, Event, KeyCode, KeyModifiers}, terminal::{disable_raw_mode, enable_raw_mode, Clear, ClearType}, cursor::{MoveToColumn, Show, Hide}, execute}; use sha2::{Sha256, Digest}; @@ -172,7 +172,7 @@ impl ClientState { if username.is_empty() { continue 'outer; } - let password = read_line("Password: "); + let password = read_password("Password: "); if password.is_empty() { continue 'outer; } @@ -217,7 +217,7 @@ impl ClientState { if username.is_empty() { break; } - let password = read_line("Password: "); + let password = read_password("Password: "); if password.is_empty() { break; } @@ -268,7 +268,7 @@ impl ClientState { if username.is_empty() { continue 'outer; } - let password = read_line("Password: "); + let password = read_password("Password: "); if password.is_empty() { continue 'outer; } @@ -367,14 +367,13 @@ impl ClientState { *current_input = matches[0].clone(); redraw(current_input); } else if matches.len() > 1 { - disable_raw_mode().unwrap(); execute!(stdout(), Show).unwrap(); println!("\nPossible completions:"); for m in &matches { print!("{} ", m); } println!(); - enable_raw_mode().unwrap(); + execute!(stdout(), Hide).unwrap(); redraw(current_input); } // else do nothing @@ -420,12 +419,78 @@ fn redraw(current_input: &str) { } fn read_line(prompt: &str) -> String { - disable_raw_mode().unwrap(); execute!(stdout(), Show).unwrap(); print!("{}", prompt); stdout().flush().unwrap(); + let mut input = String::new(); - std::io::stdin().read_line(&mut input).unwrap(); - enable_raw_mode().unwrap(); + let prompt_len = prompt.len(); + + loop { + match read().unwrap() { + Event::Key(key) => match key.code { + KeyCode::Enter => { + println!(); + break; + } + KeyCode::Backspace => { + if !input.is_empty() { + input.pop(); + // Move cursor back and clear from current position to end of line + execute!(stdout(), MoveToColumn((prompt_len + input.len() + 1) as u16), + Clear(ClearType::UntilNewLine)).unwrap(); + stdout().flush().unwrap(); + } + } + KeyCode::Char(c) => { + input.push(c); + print!("{}", c); + stdout().flush().unwrap(); + } + _ => {} + }, + _ => {} + } + } + input.trim().to_string() +} + +fn read_password(prompt: &str) -> String { + execute!(stdout(), Show).unwrap(); + print!("{}", prompt); + stdout().flush().unwrap(); + + let mut input = String::new(); + let mut display = String::new(); + let prompt_len = prompt.len(); + + loop { + match read().unwrap() { + Event::Key(key) => match key.code { + KeyCode::Enter => { + println!(); + break; + } + KeyCode::Backspace => { + if !input.is_empty() { + input.pop(); + display.pop(); + // Move cursor back and clear from current position to end of line + execute!(stdout(), MoveToColumn((prompt_len + display.len() + 1) as u16), + Clear(ClearType::UntilNewLine)).unwrap(); + stdout().flush().unwrap(); + } + } + KeyCode::Char(c) => { + input.push(c); + display.push('*'); + print!("*"); + stdout().flush().unwrap(); + } + _ => {} + }, + _ => {} + } + } input.trim().to_string() } -- cgit v1.2.3