summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorsalmonik <raisersoft@proton.me>2026-03-02 17:56:49 +0100
committersalmonik <raisersoft@proton.me>2026-03-02 17:56:49 +0100
commiteb0e61b1aef2fd0fddabcaec6ea318773b65717e (patch)
tree7bb7aec64570f52fb010e1f4248a6ff82181b812
parentslowly preparing for actual casino (diff)
downloadzagan-bet-eb0e61b1aef2fd0fddabcaec6ea318773b65717e.tar.gz
zagan-bet-eb0e61b1aef2fd0fddabcaec6ea318773b65717e.zip
only use raw mode
-rw-r--r--Cargo.toml5
-rw-r--r--src/bin/client/main.rs4
-rw-r--r--src/bin/client/platform.rs28
-rw-r--r--src/bin/client/repl.rs83
-rw-r--r--src/bin/windows-test/main.rs61
5 files changed, 138 insertions, 43 deletions
diff --git a/Cargo.toml b/Cargo.toml
index 438fde9..ce86a04 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -5,7 +5,7 @@ edition = "2024"
5 5
6[dependencies] 6[dependencies]
7rusqlite = { version = "0.29", features = ["bundled"] } 7rusqlite = { version = "0.29", features = ["bundled"] }
8winapi = { version = "0.3", features = ["consoleapi", "processenv", "wincon", "minwindef", "winbase"] } 8windows-sys = { version = "0.61.2", features = ["Win32_System_Console"] }
9crossterm = "0.27" 9crossterm = "0.27"
10bcrypt = "0.15" 10bcrypt = "0.15"
11aes-gcm = "0.10" 11aes-gcm = "0.10"
@@ -14,4 +14,5 @@ rand = "0.8"
14once_cell = "1.18" 14once_cell = "1.18"
15hmac = "0.12" 15hmac = "0.12"
16sha2 = "0.10" 16sha2 = "0.10"
17base64 = "0.21" \ No newline at end of file 17base64 = "0.21"
18ctrlc = "3.4" \ No newline at end of file
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;
3mod repl; 3mod repl;
4mod database; 4mod database;
5mod connection; 5mod connection;
6mod platform;
7 6
8use repl::ClientState; 7use repl::ClientState;
9 8
10fn main() { 9fn main() {
11 #[cfg(windows)]
12 platform::win::enable_vt().unwrap();
13
14 let mut state = ClientState::new(); 10 let mut state = ClientState::new();
15 if let Some(host) = state.addr.clone() { 11 if let Some(host) = state.addr.clone() {
16 state.connect_to(&host); 12 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 @@
1#[cfg(windows)]
2pub mod win {
3 use std::io;
4 use winapi::um::consoleapi::GetConsoleMode;
5 use winapi::um::consoleapi::SetConsoleMode;
6 use winapi::um::wincon::ENABLE_VIRTUAL_TERMINAL_PROCESSING;
7 use winapi::um::wincon::ENABLE_ECHO_INPUT;
8 use winapi::um::winbase::STD_OUTPUT_HANDLE;
9 use winapi::um::processenv::GetStdHandle;
10 use winapi::shared::minwindef::DWORD;
11
12 pub fn enable_vt() -> io::Result<()> {
13 unsafe {
14 let h = GetStdHandle(STD_OUTPUT_HANDLE);
15 if h.is_null() { return Err(io::Error::last_os_error()); }
16
17 let mut mode: DWORD = 0;
18 if GetConsoleMode(h, &mut mode) == 0 {
19 return Err(io::Error::last_os_error());
20 }
21 let new = (mode | ENABLE_VIRTUAL_TERMINAL_PROCESSING) & !ENABLE_ECHO_INPUT;
22 if SetConsoleMode(h, new) == 0 {
23 return Err(io::Error::last_os_error());
24 }
25 Ok(())
26 }
27 }
28}
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;
6use crate::connection; 6use crate::connection;
7use crate::database; 7use crate::database;
8 8
9use crossterm::{event::{read, Event, KeyCode, KeyModifiers}, terminal::{disable_raw_mode, enable_raw_mode, Clear, ClearType}, cursor::{MoveToColumn, Show}, execute}; 9use crossterm::{event::{read, Event, KeyCode, KeyModifiers}, terminal::{disable_raw_mode, enable_raw_mode, Clear, ClearType}, cursor::{MoveToColumn, Show, Hide}, execute};
10 10
11use sha2::{Sha256, Digest}; 11use sha2::{Sha256, Digest};
12 12
@@ -172,7 +172,7 @@ impl ClientState {
172 if username.is_empty() { 172 if username.is_empty() {
173 continue 'outer; 173 continue 'outer;
174 } 174 }
175 let password = read_line("Password: "); 175 let password = read_password("Password: ");
176 if password.is_empty() { 176 if password.is_empty() {
177 continue 'outer; 177 continue 'outer;
178 } 178 }
@@ -217,7 +217,7 @@ impl ClientState {
217 if username.is_empty() { 217 if username.is_empty() {
218 break; 218 break;
219 } 219 }
220 let password = read_line("Password: "); 220 let password = read_password("Password: ");
221 if password.is_empty() { 221 if password.is_empty() {
222 break; 222 break;
223 } 223 }
@@ -268,7 +268,7 @@ impl ClientState {
268 if username.is_empty() { 268 if username.is_empty() {
269 continue 'outer; 269 continue 'outer;
270 } 270 }
271 let password = read_line("Password: "); 271 let password = read_password("Password: ");
272 if password.is_empty() { 272 if password.is_empty() {
273 continue 'outer; 273 continue 'outer;
274 } 274 }
@@ -367,14 +367,13 @@ impl ClientState {
367 *current_input = matches[0].clone(); 367 *current_input = matches[0].clone();
368 redraw(current_input); 368 redraw(current_input);
369 } else if matches.len() > 1 { 369 } else if matches.len() > 1 {
370 disable_raw_mode().unwrap();
371 execute!(stdout(), Show).unwrap(); 370 execute!(stdout(), Show).unwrap();
372 println!("\nPossible completions:"); 371 println!("\nPossible completions:");
373 for m in &matches { 372 for m in &matches {
374 print!("{} ", m); 373 print!("{} ", m);
375 } 374 }
376 println!(); 375 println!();
377 enable_raw_mode().unwrap(); 376 execute!(stdout(), Hide).unwrap();
378 redraw(current_input); 377 redraw(current_input);
379 } 378 }
380 // else do nothing 379 // else do nothing
@@ -420,12 +419,78 @@ fn redraw(current_input: &str) {
420} 419}
421 420
422fn read_line(prompt: &str) -> String { 421fn read_line(prompt: &str) -> String {
423 disable_raw_mode().unwrap();
424 execute!(stdout(), Show).unwrap(); 422 execute!(stdout(), Show).unwrap();
425 print!("{}", prompt); 423 print!("{}", prompt);
426 stdout().flush().unwrap(); 424 stdout().flush().unwrap();
425
427 let mut input = String::new(); 426 let mut input = String::new();
428 std::io::stdin().read_line(&mut input).unwrap(); 427 let prompt_len = prompt.len();
429 enable_raw_mode().unwrap(); 428
429 loop {
430 match read().unwrap() {
431 Event::Key(key) => match key.code {
432 KeyCode::Enter => {
433 println!();
434 break;
435 }
436 KeyCode::Backspace => {
437 if !input.is_empty() {
438 input.pop();
439 // Move cursor back and clear from current position to end of line
440 execute!(stdout(), MoveToColumn((prompt_len + input.len() + 1) as u16),
441 Clear(ClearType::UntilNewLine)).unwrap();
442 stdout().flush().unwrap();
443 }
444 }
445 KeyCode::Char(c) => {
446 input.push(c);
447 print!("{}", c);
448 stdout().flush().unwrap();
449 }
450 _ => {}
451 },
452 _ => {}
453 }
454 }
455 input.trim().to_string()
456}
457
458fn read_password(prompt: &str) -> String {
459 execute!(stdout(), Show).unwrap();
460 print!("{}", prompt);
461 stdout().flush().unwrap();
462
463 let mut input = String::new();
464 let mut display = String::new();
465 let prompt_len = prompt.len();
466
467 loop {
468 match read().unwrap() {
469 Event::Key(key) => match key.code {
470 KeyCode::Enter => {
471 println!();
472 break;
473 }
474 KeyCode::Backspace => {
475 if !input.is_empty() {
476 input.pop();
477 display.pop();
478 // Move cursor back and clear from current position to end of line
479 execute!(stdout(), MoveToColumn((prompt_len + display.len() + 1) as u16),
480 Clear(ClearType::UntilNewLine)).unwrap();
481 stdout().flush().unwrap();
482 }
483 }
484 KeyCode::Char(c) => {
485 input.push(c);
486 display.push('*');
487 print!("*");
488 stdout().flush().unwrap();
489 }
490 _ => {}
491 },
492 _ => {}
493 }
494 }
430 input.trim().to_string() 495 input.trim().to_string()
431} 496}
diff --git a/src/bin/windows-test/main.rs b/src/bin/windows-test/main.rs
new file mode 100644
index 0000000..b8f84fe
--- /dev/null
+++ b/src/bin/windows-test/main.rs
@@ -0,0 +1,61 @@
1use std::io::{self, Write};
2use std::ptr;
3use windows_sys::Win32::Foundation::HANDLE;
4use windows_sys::Win32::System::Console::{
5 GetConsoleMode, ReadConsoleW, SetConsoleMode, GetStdHandle, STD_INPUT_HANDLE,
6 ENABLE_ECHO_INPUT, ENABLE_LINE_INPUT,
7};
8
9fn main() -> io::Result<()> {
10 unsafe {
11 let h: HANDLE = GetStdHandle(STD_INPUT_HANDLE);
12 if h == std::ptr::null_mut() || h == -1_i32 as HANDLE { return Err(io::Error::last_os_error()); }
13
14 // save current mode
15 let mut mode: u32 = 0;
16 if GetConsoleMode(h, &mut mode) == 0 { return Err(io::Error::last_os_error()); }
17
18 // disable line input and echo so input is available per-key and not echoed
19 let new_mode = mode & !(ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT);
20 if SetConsoleMode(h, new_mode) == 0 { return Err(io::Error::last_os_error()); }
21
22 print!("Press keys (q to exit): ");
23 io::stdout().flush()?;
24
25 // ReadConsoleW reads UTF-16; we'll read one wchar at a time
26 let mut buf: [u16; 1] = [0];
27 loop {
28 let mut read = 0u32;
29 if ReadConsoleW(
30 h,
31 buf.as_mut_ptr() as *mut _,
32 1,
33 &mut read,
34 ptr::null_mut(),
35 ) == 0
36 {
37 // restore mode on error before returning
38 let _ = SetConsoleMode(h, mode);
39 return Err(io::Error::last_os_error());
40 }
41
42 let ch = std::char::from_u32(buf[0] as u32).unwrap_or('\u{FFFD}');
43 if ch == 'q' { break; } // q to exit
44
45 // handle Enter (CR) so output is readable
46 if ch == '\r' {
47 println!();
48 } else {
49 // show code point (no automatic console echo)
50 print!("[{:?}]", ch);
51 io::stdout().flush()?;
52 }
53 }
54
55 // restore original console mode
56 let _ = SetConsoleMode(h, mode);
57 println!("\nexited");
58 }
59
60 Ok(())
61}