Skip to content

Commit c8ea46f

Browse files
committed
WIP Return Escape immediately after one press
1 parent 0da89c5 commit c8ea46f

File tree

1 file changed

+163
-65
lines changed

1 file changed

+163
-65
lines changed

src/unix_term.rs

Lines changed: 163 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,60 @@ pub fn read_secure() -> io::Result<String> {
8080
})
8181
}
8282

83+
fn read_single_byte(fd: i32) -> io::Result<Option<u8>> {
84+
let mut readfds = core::mem::MaybeUninit::uninit();
85+
unsafe {
86+
libc::FD_SET(fd, readfds.as_mut_ptr());
87+
};
88+
let mut readfds = unsafe { readfds.assume_init() };
89+
90+
// zero timeout, i.e check if there is something to be read right now
91+
let mut timeout = libc::timeval {
92+
tv_sec: 0,
93+
tv_usec: 0,
94+
};
95+
96+
let ret = unsafe {
97+
libc::select(
98+
fd + 1,
99+
&mut readfds as *mut _,
100+
std::ptr::null_mut(),
101+
std::ptr::null_mut(),
102+
&mut timeout,
103+
)
104+
};
105+
if ret < 0 {
106+
return Err(io::Error::last_os_error());
107+
}
108+
109+
let is_ready = unsafe { libc::FD_ISSET(fd, &mut readfds as *mut _) };
110+
111+
if is_ready {
112+
//there is something to be read
113+
114+
// let mut buf: [u8; 1] = [0];
115+
// let read = unsafe { libc::read(fd, buf.as_mut_ptr() as *mut libc::c_void, 1) };
116+
117+
//read only 1 byte
118+
let mut byte: u8 = 0;
119+
let read = unsafe { libc::read(fd, &mut byte as *mut u8 as _, 1) };
120+
121+
if read < 0 {
122+
Err(io::Error::last_os_error())
123+
} else if byte == b'\x03' {
124+
Err(io::Error::new(
125+
io::ErrorKind::Interrupted,
126+
"read interrupted",
127+
))
128+
} else {
129+
Ok(Some(byte))
130+
}
131+
} else {
132+
//there is nothing to be read
133+
Ok(None)
134+
}
135+
}
136+
83137
pub fn read_single_key() -> io::Result<Key> {
84138
let tty_f;
85139
let fd = unsafe {
@@ -92,74 +146,118 @@ pub fn read_single_key() -> io::Result<Key> {
92146
};
93147
let mut buf = [0u8; 20];
94148
let mut termios = core::mem::MaybeUninit::uninit();
95-
c_result(|| unsafe { libc::tcgetattr(fd, termios.as_mut_ptr()) })?;
149+
c_result(|| unsafe { libc::tcgetattr(fd, termios.as_mut_ptr()) })?;
96150
let mut termios = unsafe { termios.assume_init() };
97151
let original = termios;
98152
unsafe { libc::cfmakeraw(&mut termios) };
99153
c_result(|| unsafe { libc::tcsetattr(fd, libc::TCSADRAIN, &termios) })?;
100-
let rv = unsafe {
101-
let read = libc::read(fd, buf.as_mut_ptr() as *mut libc::c_void, 1);
102-
if read < 0 {
103-
Err(io::Error::last_os_error())
104-
} else if buf[0] == b'\x1b' {
105-
// read 19 more bytes if the first byte was the ESC code
106-
let read = libc::read(fd, buf[1..].as_mut_ptr() as *mut libc::c_void, 19);
107-
if read < 0 {
108-
Err(io::Error::last_os_error())
109-
} else if buf[1] == b'\x03' {
110-
Err(io::Error::new(
111-
io::ErrorKind::Interrupted,
112-
"read interrupted",
113-
))
114-
} else {
115-
Ok(key_from_escape_codes(&buf[..(read + 1) as usize]))
116-
}
117-
} else if buf[0] & 224u8 == 192u8 {
118-
// a two byte unicode character
119-
let read = libc::read(fd, buf[1..].as_mut_ptr() as *mut libc::c_void, 1);
120-
if read < 0 {
121-
Err(io::Error::last_os_error())
122-
} else if buf[1] == b'\x03' {
123-
Err(io::Error::new(
124-
io::ErrorKind::Interrupted,
125-
"read interrupted",
126-
))
154+
155+
let byte = read_single_byte(fd)?;
156+
let rv = match byte {
157+
Some(b'\x1b') => {
158+
// Escape was read, keep reading in case we find a familiar key
159+
if let Some(b1) = read_single_byte(fd)? {
160+
if b1 == b'[' {
161+
if let Some(b2) = read_single_byte(fd)? {
162+
match b2 {
163+
b'A' => Ok(Key::ArrowUp),
164+
b'B' => Ok(Key::ArrowDown),
165+
b'C' => Ok(Key::ArrowRight),
166+
b'D' => Ok(Key::ArrowLeft),
167+
b'H' => Ok(Key::Home),
168+
b'F' => Ok(Key::End),
169+
b'3' => {
170+
if let Some(b'~') = read_single_byte(fd)? {
171+
Ok(Key::Del)
172+
} else {
173+
Ok(Key::Escape)
174+
}
175+
}
176+
_ => Ok(Key::Escape),
177+
}
178+
} else {
179+
// \x1b[ and no more input
180+
Ok(Key::Escape)
181+
}
182+
} else {
183+
// char after escape is not [
184+
Ok(Key::Escape)
185+
}
127186
} else {
128-
Ok(key_from_escape_codes(&buf[..2 as usize]))
187+
//nothing after escape
188+
Ok(Key::Escape)
129189
}
130-
} else if buf[0] & 240u8 == 224u8 {
131-
// a three byte unicode character
132-
let read = libc::read(fd, buf[1..].as_mut_ptr() as *mut libc::c_void, 2);
133-
if read < 0 {
134-
Err(io::Error::last_os_error())
135-
} else if buf[1] == b'\x03' {
136-
Err(io::Error::new(
137-
io::ErrorKind::Interrupted,
138-
"read interrupted",
139-
))
190+
}
191+
Some(byte) => {
192+
buf[0] = byte;
193+
if byte & 224u8 == 192u8 {
194+
// a two byte unicode character
195+
let read = unsafe { libc::read(fd, buf[1..].as_mut_ptr() as *mut libc::c_void, 1) };
196+
if read < 0 {
197+
Err(io::Error::last_os_error())
198+
} else if buf[1] == b'\x03' {
199+
Err(io::Error::new(
200+
io::ErrorKind::Interrupted,
201+
"read interrupted",
202+
))
203+
} else {
204+
Ok(key_from_escape_codes(&buf[..2 as usize]))
205+
}
206+
} else if byte & 240u8 == 224u8 {
207+
// a three byte unicode character
208+
let read = unsafe { libc::read(fd, buf[1..].as_mut_ptr() as *mut libc::c_void, 2) };
209+
if read < 0 {
210+
Err(io::Error::last_os_error())
211+
} else if buf[1] == b'\x03' {
212+
Err(io::Error::new(
213+
io::ErrorKind::Interrupted,
214+
"read interrupted",
215+
))
216+
} else {
217+
Ok(key_from_escape_codes(&buf[..3 as usize]))
218+
}
219+
} else if byte & 248u8 == 240u8 {
220+
// a four byte unicode character
221+
let read = unsafe { libc::read(fd, buf[1..].as_mut_ptr() as *mut libc::c_void, 3) };
222+
if read < 0 {
223+
Err(io::Error::last_os_error())
224+
} else if buf[1] == b'\x03' {
225+
Err(io::Error::new(
226+
io::ErrorKind::Interrupted,
227+
"read interrupted",
228+
))
229+
} else {
230+
Ok(key_from_escape_codes(&buf[..4 as usize]))
231+
}
140232
} else {
141-
Ok(key_from_escape_codes(&buf[..3 as usize]))
233+
Ok(key_from_escape_codes(&[byte]))
142234
}
143-
} else if buf[0] & 248u8 == 240u8 {
144-
// a four byte unicode character
145-
let read = libc::read(fd, buf[1..].as_mut_ptr() as *mut libc::c_void, 3);
146-
if read < 0 {
147-
Err(io::Error::last_os_error())
148-
} else if buf[1] == b'\x03' {
149-
Err(io::Error::new(
150-
io::ErrorKind::Interrupted,
151-
"read interrupted",
152-
))
153-
} else {
154-
Ok(key_from_escape_codes(&buf[..4 as usize]))
235+
}
236+
None => {
237+
//there is no subsequent byte ready to be read, block and wait for input
238+
239+
let mut readfds = core::mem::MaybeUninit::uninit();
240+
unsafe {
241+
libc::FD_SET(fd, readfds.as_mut_ptr());
242+
};
243+
let mut readfds = unsafe { readfds.assume_init() };
244+
245+
// block until there is something to be read
246+
let ret = unsafe {
247+
libc::select(
248+
fd + 1,
249+
&mut readfds as *mut _,
250+
std::ptr::null_mut(),
251+
std::ptr::null_mut(),
252+
// null timeout pointer means that it will block indefinitely
253+
std::ptr::null_mut(),
254+
)
255+
};
256+
if ret < 0 {
257+
return Err(io::Error::last_os_error());
155258
}
156-
} else if buf[0] == b'\x03' {
157-
Err(io::Error::new(
158-
io::ErrorKind::Interrupted,
159-
"read interrupted",
160-
))
161-
} else {
162-
Ok(key_from_escape_codes(&buf[..read as usize]))
259+
260+
read_single_key()
163261
}
164262
};
165263
c_result(|| unsafe { libc::tcsetattr(fd, libc::TCSADRAIN, &original) })?;
@@ -178,17 +276,17 @@ pub fn read_single_key() -> io::Result<Key> {
178276

179277
pub fn key_from_escape_codes(buf: &[u8]) -> Key {
180278
match buf {
181-
b"\x1b[D" => Key::ArrowLeft,
182-
b"\x1b[C" => Key::ArrowRight,
279+
b"\x1b" => Key::Escape,
183280
b"\x1b[A" => Key::ArrowUp,
184281
b"\x1b[B" => Key::ArrowDown,
185-
b"\n" | b"\r" => Key::Enter,
186-
b"\x1b" => Key::Escape,
187-
b"\x7f" => Key::Backspace,
282+
b"\x1b[C" => Key::ArrowRight,
283+
b"\x1b[D" => Key::ArrowLeft,
188284
b"\x1b[H" => Key::Home,
189285
b"\x1b[F" => Key::End,
190-
b"\t" => Key::Tab,
191286
b"\x1b[3~" => Key::Del,
287+
b"\n" | b"\r" => Key::Enter,
288+
b"\x7f" => Key::Backspace,
289+
b"\t" => Key::Tab,
192290
buf => {
193291
if let Ok(s) = str::from_utf8(buf) {
194292
if let Some(c) = s.chars().next() {

0 commit comments

Comments
 (0)