1use std::{io, time::Duration};
7
8#[derive(Debug, Clone, PartialEq, Eq)]
10pub enum Key {
11 ArrowUp,
13 ArrowDown,
15 ArrowRight,
17 ArrowLeft,
19 Enter,
21 Tab,
23 Backspace,
25 Escape,
27 Char(char),
29 Unknown,
31}
32
33pub fn read_key() -> io::Result<Key> {
35 #[cfg(windows)]
36 {
37 windows::read_key()
38 }
39
40 #[cfg(unix)]
41 {
42 unix::read_key()
43 }
44}
45
46pub fn key_pressed_within(timeout: Duration) -> io::Result<Option<Key>> {
48 #[cfg(windows)]
49 {
50 windows::key_pressed_within(timeout)
51 }
52 #[cfg(unix)]
53 {
54 unix::key_pressed_within(timeout)
55 }
56}
57
58#[cfg(windows)]
61pub mod windows {
62 use super::Key;
63 use std::io;
64 use std::mem;
65 use std::os::windows::raw::HANDLE;
66 use std::time::Instant;
67 use windows_sys::Win32::Foundation::{INVALID_HANDLE_VALUE, WAIT_OBJECT_0, WAIT_TIMEOUT};
68 use windows_sys::Win32::System::Console::{
69 GetStdHandle, PeekConsoleInputW, ReadConsoleInputW, INPUT_RECORD, KEY_EVENT,
70 KEY_EVENT_RECORD, STD_INPUT_HANDLE,
71 };
72 use windows_sys::Win32::System::Threading::WaitForSingleObject;
73 use windows_sys::Win32::UI::Input::KeyboardAndMouse;
74
75 pub(crate) fn read_key() -> io::Result<Key> {
76 let handle = unsafe { GetStdHandle(STD_INPUT_HANDLE) };
77 let mut buffer: INPUT_RECORD = unsafe { mem::zeroed() };
78
79 let mut events_read: u32 = unsafe { mem::zeroed() };
80
81 loop {
82 let success = unsafe { ReadConsoleInputW(handle, &mut buffer, 1, &mut events_read) };
83 if success == 0 {
84 return Err(io::Error::last_os_error());
85 }
86 if events_read == 0 {
87 return Err(io::Error::new(
88 io::ErrorKind::Other,
89 "ReadConsoleInput returned no events, instead of waiting for an event",
90 ));
91 }
92
93 if events_read == 1 && buffer.EventType == KEY_EVENT as u16 {
94 let key_event: KEY_EVENT_RECORD = unsafe { mem::transmute(buffer.Event) };
95
96 if key_event.bKeyDown != 0 {
97 return match key_event.wVirtualKeyCode {
98 KeyboardAndMouse::VK_UP => Ok(Key::ArrowUp),
99 KeyboardAndMouse::VK_DOWN => Ok(Key::ArrowDown),
100 KeyboardAndMouse::VK_RIGHT => Ok(Key::ArrowRight),
101 KeyboardAndMouse::VK_LEFT => Ok(Key::ArrowLeft),
102 KeyboardAndMouse::VK_RETURN => Ok(Key::Enter),
103 KeyboardAndMouse::VK_TAB => Ok(Key::Tab),
104 KeyboardAndMouse::VK_BACK => Ok(Key::Backspace),
105 KeyboardAndMouse::VK_ESCAPE => Ok(Key::Escape),
106 c => Ok(Key::Char(char::from_u32(c as u32).unwrap_or_default())),
107 };
108 }
109 }
110 }
111 }
112
113 pub(super) fn key_pressed_within(timeout: std::time::Duration) -> std::io::Result<Option<Key>> {
114 unsafe fn ensure_head_is_keydown_or_empty(handle: HANDLE) -> io::Result<bool> {
117 let mut rec: INPUT_RECORD = mem::zeroed();
118 let mut read: u32 = 0;
119
120 if PeekConsoleInputW(handle, &mut rec, 1, &mut read) == 0 {
122 return Err(io::Error::last_os_error());
123 }
124 if read == 0 {
125 return Ok(false); }
127
128 if rec.EventType == KEY_EVENT as u16 {
129 let key: KEY_EVENT_RECORD = mem::transmute(rec.Event);
131 if key.bKeyDown != 0 {
132 return Ok(true);
134 }
135 let mut took: u32 = 0;
137 if ReadConsoleInputW(handle, &mut rec, 1, &mut took) == 0 {
138 return Err(io::Error::last_os_error());
139 }
140 return Ok(false);
141 }
142
143 let mut took: u32 = 0;
145 if ReadConsoleInputW(handle, &mut rec, 1, &mut took) == 0 {
146 return Err(io::Error::last_os_error());
147 }
148 Ok(false)
149 }
150
151 let handle: HANDLE = unsafe { GetStdHandle(STD_INPUT_HANDLE) };
152 if handle == 0 as HANDLE || handle == INVALID_HANDLE_VALUE {
153 return Err(io::Error::last_os_error());
154 }
155
156 let deadline = Instant::now() + timeout;
157
158 loop {
159 if unsafe { ensure_head_is_keydown_or_empty(handle)? } {
162 return Ok(Some(read_key()?));
163 }
164
165 let now = Instant::now();
167 if now >= deadline {
168 return Ok(None);
169 }
170 let remaining_ms = ((deadline - now).as_millis()).min(u32::MAX as u128) as u32;
171
172 match unsafe { WaitForSingleObject(handle, remaining_ms) } {
174 WAIT_TIMEOUT => return Ok(None),
175 WAIT_OBJECT_0 => continue,
176 _ => return Err(io::Error::last_os_error()),
177 }
178 }
179 }
180}
181
182#[cfg(unix)]
185pub mod unix {
186 use libc::{
187 poll, pollfd, tcgetattr, tcsetattr, termios, ECHO, ICANON, POLLIN, STDIN_FILENO, TCSANOW,
188 };
189 use std::io::{self, Read};
190 use std::mem;
191 use std::time::Duration;
192
193 use super::Key;
194
195 fn get_termios(fd: i32) -> io::Result<termios> {
197 let mut t: termios = unsafe { mem::zeroed() };
199 let rc = unsafe { tcgetattr(fd, &mut t as *mut termios) };
200 if rc != 0 {
201 Err(io::Error::last_os_error())
202 } else {
203 Ok(t)
204 }
205 }
206
207 fn set_termios(fd: i32, t: &termios) -> io::Result<()> {
209 let rc = unsafe { tcsetattr(fd, TCSANOW, t as *const termios) };
210 if rc != 0 {
211 Err(io::Error::last_os_error())
212 } else {
213 Ok(())
214 }
215 }
216
217 struct RawMode {
219 fd: i32,
220 saved: termios,
221 }
222
223 impl RawMode {
224 fn new(fd: i32) -> io::Result<Self> {
225 let mut current = get_termios(fd)?;
226 let saved = current;
227
228 current.c_lflag &= !(ICANON | ECHO);
230
231 set_termios(fd, ¤t)?;
234
235 Ok(Self { fd, saved })
236 }
237 }
238
239 impl Drop for RawMode {
240 fn drop(&mut self) {
241 let _ = set_termios(self.fd, &self.saved);
243 }
244 }
245
246 fn read_key_raw() -> io::Result<Key> {
248 let mut buffer = [0u8; 3];
249
250 let n = std::io::stdin().read(&mut buffer)?;
252
253 if n == 0 {
254 return Ok(Key::Unknown);
256 }
257
258 match buffer[0] {
259 27 => {
260 if n >= 3 && buffer[1] == b'[' {
263 match buffer[2] {
264 b'A' => Ok(Key::ArrowUp),
265 b'B' => Ok(Key::ArrowDown),
266 b'C' => Ok(Key::ArrowRight),
267 b'D' => Ok(Key::ArrowLeft),
268 _ => Ok(Key::Unknown),
269 }
270 } else if n == 1 {
271 Ok(Key::Escape)
272 } else {
273 Ok(Key::Unknown)
274 }
275 }
276 b'\n' => Ok(Key::Enter),
277 b'\t' => Ok(Key::Tab),
278 127 => Ok(Key::Backspace),
279 c => Ok(Key::Char(c as char)),
280 }
281 }
282
283 pub(crate) fn read_key() -> io::Result<Key> {
285 let _rm = RawMode::new(STDIN_FILENO)?;
286 read_key_raw()
287 }
288
289 pub(super) fn key_pressed_within(timeout: Duration) -> io::Result<Option<Key>> {
292 let _rm = RawMode::new(STDIN_FILENO)?;
293
294 let mut fds = pollfd {
295 fd: STDIN_FILENO,
296 events: POLLIN,
297 revents: 0,
298 };
299
300 let ms = timeout.as_millis().min(i32::MAX as u128) as i32;
302
303 let rc = unsafe { poll(&mut fds as *mut pollfd, 1, ms) };
304
305 if rc < 0 {
306 return Err(io::Error::last_os_error());
307 }
308 if rc == 0 {
309 return Ok(None);
311 }
312
313 match read_key_raw() {
315 Ok(k) => Ok(Some(k)),
316 Err(e) if e.kind() == io::ErrorKind::WouldBlock => Ok(None),
317 Err(e) => Err(e),
318 }
319 }
320}