Work around torn byte sequences in qemu kbd input with 1ms timeout

As reported on gitter, fish running inside a qemu console randomly fails to
recognize multi-byte sequences like "\e[D" (right); it sometimes recognizes
the first two bytes as "alt-[" and the last byte as the "D" key.

This because 8bf8b10f68 (Extended & human-friendly keys, 2024-03-30) changed
our approach to reading multi-byte key sequences.  Previously, we'd wait
forever (or rather fish_sequence_key_delay_ms) for the "D" byte.

As of  8bf8b10f68, we assume the entire sequence is already present in the
input buffer; and stop parsing the sequence if stdin is not readable.

It would be more technically correct to implement the VT state machine but
then we'd probably want to to figure out a timeout or a reset key, in case
of transport or terminal issues.

Returning early is also what we have historically done for multi-byte code
points.  Also, other terminal programs have been using it for many years
without problems.

I don't know why this happens in qemu but it seems we can work around by
setting a 1ms timeout.  This timeout should be small enough two keys "escape"
and "[" typed by a human will still be seen separate.

Refs:
https://matrix.to/#/!YLTeaulxSDauOOxBoR:matrix.org/$Cfi9wL8FGLAI6_VAQWG2mG_VxsADUPvdPB46P41Jdbs
https://matrix.to/#/!YLTeaulxSDauOOxBoR:matrix.org/$O_-LZ1W7Dk6L_4Rj0MyCry6GtO2JQlEas8fH9PrSYT8
This commit is contained in:
Johannes Altmanninger
2025-03-04 09:23:02 +01:00
parent 852cb60ebd
commit e1be842167

View File

@@ -26,6 +26,7 @@
use std::ptr;
use std::sync::atomic::{AtomicBool, AtomicU8, AtomicUsize, Ordering};
use std::sync::{Mutex, MutexGuard};
use std::time::Duration;
// The range of key codes for inputrc-style keyboard functions.
pub const R_END_INPUT_FUNCTIONS: usize = (ReadlineCmd::ReverseRepeatJump as usize) + 1;
@@ -341,7 +342,7 @@ fn readb(in_fd: RawFd, blocking: bool) -> ReadbResult {
let select_res = fdset.check_readable(if blocking {
Timeout::Forever
} else {
Timeout::ZERO
Timeout::Duration(Duration::from_millis(1))
});
if select_res < 0 {
let err = errno::errno().0;