Codex Handbook
tui/src/terminal_probe.rs 966 lines
//! Short, best-effort terminal response probes for TUI startup and resume.//!//! Crossterm's public helpers wait up to two seconds for terminal responses. That is too long for//! TUI startup and resume, where unsupported terminals should simply fall back to conservative//! defaults.//! This module sends the same kinds of optional terminal queries with a caller-provided deadline,//! prefers duplicated stdio handles, falls back to the controlling terminal path when stdio is//! unavailable, and reports `None` when a response is unavailable.//!//! Probes run only while the crossterm event stream is absent or paused, so they do not share//! crossterm's internal skipped-event queue. Bytes read while looking for probe responses are//! consumed from the terminal; callers must therefore own terminal input for the duration of the//! short timeout and accept that unrelated buffered input will be discarded.use std::time::Duration;/// Default wall-clock budget for each startup probe group.pub(crate) const DEFAULT_TIMEOUT: Duration = Duration::from_millis(100);/// Default terminal foreground and background colors reported by OSC 10 and OSC 11.#[derive(Debug, Clone, Copy, Eq, PartialEq)]pub(crate) struct DefaultColors {    /// Default foreground color as an 8-bit RGB tuple.    pub(crate) fg: (u8, u8, u8),    /// Default background color as an 8-bit RGB tuple.    pub(crate) bg: (u8, u8, u8),}#[cfg(unix)]#[cfg_attr(test, allow(dead_code))]mod imp {    use super::DefaultColors;    use super::parse_default_colors;    use std::fs::File;    use std::fs::OpenOptions;    use std::io;    use std::io::Write;    use std::os::fd::AsRawFd;    use std::os::fd::FromRawFd;    use std::time::Duration;    use std::time::Instant;    use crossterm::event::KeyboardEnhancementFlags;    use ratatui::layout::Position;    /// Results from the TUI's one-shot startup terminal probe.    #[derive(Debug, Clone, Copy, Eq, PartialEq)]    pub(crate) struct StartupProbe {        pub(crate) cursor_position: Option<Position>,        pub(crate) default_colors: Option<DefaultColors>,        pub(crate) keyboard_enhancement_supported: Option<bool>,    }    /// Whether the startup probe should query keyboard enhancement support.    #[derive(Clone, Copy, Eq, PartialEq)]    pub(crate) enum StartupKeyboardEnhancementProbe {        Query,        Skip,    }    /// Temporary terminal handle used while a probe owns terminal input.    ///    /// The preferred path is duplicated stdin/stdout, because terminal replies are delivered to the    /// same input stream crossterm reads from. Some embedded or redirected environments expose a    /// controlling terminal without terminal stdio; in that case the handle falls back to    /// `/dev/tty`. Only the reader is switched to nonblocking mode, and its original file status    /// flags are restored when the handle is dropped.    struct Tty {        reader: File,        writer: File,        original_flags: libc::c_int,    }    impl Tty {        /// Opens an isolated reader and writer for terminal probes.        ///        /// The reader and writer must be separate file descriptions so switching the reader into        /// nonblocking mode does not also make writes fail with `WouldBlock` under terminal        /// backpressure. Falling back to `/dev/tty` keeps embedded or redirected environments        /// usable when they still expose a controlling terminal.        fn open() -> io::Result<Self> {            let stdio_reader = dup_file(libc::STDIN_FILENO);            let stdio_writer = dup_file(libc::STDOUT_FILENO);            match (stdio_reader, stdio_writer) {                (Ok(reader), Ok(writer)) => Self::new(reader, writer),                (reader, writer) => {                    let stdio_err = match (reader.err(), writer.err()) {                        (Some(reader_err), Some(writer_err)) => {                            format!("reader: {reader_err}; writer: {writer_err}")                        }                        (Some(reader_err), None) => format!("reader: {reader_err}"),                        (None, Some(writer_err)) => format!("writer: {writer_err}"),                        (None, None) => "unknown stdio duplicate error".to_string(),                    };                    let reader =                        OpenOptions::new()                            .read(true)                            .open("/dev/tty")                            .map_err(|fallback_err| {                                io::Error::new(                                    fallback_err.kind(),                                    format!(                                        "failed to duplicate stdio ({stdio_err}) or open /dev/tty reader ({fallback_err})"                                    ),                                )                            })?;                    let writer = OpenOptions::new().write(true).open("/dev/tty").map_err(                        |fallback_err| {                            io::Error::new(                                fallback_err.kind(),                                format!(                                    "failed to duplicate stdio ({stdio_err}) or open /dev/tty writer ({fallback_err})"                                ),                            )                        },                    )?;                    Self::new(reader, writer)                }            }        }        fn new(reader: File, writer: File) -> io::Result<Self> {            let fd = reader.as_raw_fd();            let original_flags = unsafe { libc::fcntl(fd, libc::F_GETFL) };            if original_flags == -1 {                return Err(io::Error::last_os_error());            }            if unsafe { libc::fcntl(fd, libc::F_SETFL, original_flags | libc::O_NONBLOCK) } == -1 {                return Err(io::Error::last_os_error());            }            Ok(Self {                reader,                writer,                original_flags,            })        }        fn write_all(&mut self, bytes: &[u8]) -> io::Result<()> {            self.writer.write_all(bytes)?;            self.writer.flush()        }        fn read_available(&mut self, buffer: &mut Vec<u8>) -> io::Result<()> {            let mut chunk = [0_u8; 256];            loop {                let count = unsafe {                    libc::read(                        self.reader.as_raw_fd(),                        chunk.as_mut_ptr().cast::<libc::c_void>(),                        chunk.len(),                    )                };                if count > 0 {                    buffer.extend_from_slice(&chunk[..count as usize]);                    continue;                }                if count == 0 {                    return Ok(());                }                let err = io::Error::last_os_error();                if matches!(                    err.kind(),                    io::ErrorKind::WouldBlock | io::ErrorKind::Interrupted                ) {                    return Ok(());                }                return Err(err);            }        }        fn poll_readable(&self, timeout: Duration) -> io::Result<bool> {            let mut fd = libc::pollfd {                fd: self.reader.as_raw_fd(),                events: libc::POLLIN,                revents: 0,            };            let deadline = Instant::now() + timeout;            loop {                let now = Instant::now();                if now >= deadline {                    return Ok(false);                }                let timeout_ms = deadline                    .saturating_duration_since(now)                    .as_millis()                    .min(libc::c_int::MAX as u128) as libc::c_int;                let result = unsafe {                    libc::poll(&mut fd, /*nfds*/ 1, timeout_ms)                };                if result > 0 {                    return Ok((fd.revents & libc::POLLIN) != 0);                }                if result == 0 {                    return Ok(false);                }                let err = io::Error::last_os_error();                if err.kind() != io::ErrorKind::Interrupted {                    return Err(err);                }            }        }    }    impl Drop for Tty {        fn drop(&mut self) {            let _ =                unsafe { libc::fcntl(self.reader.as_raw_fd(), libc::F_SETFL, self.original_flags) };        }    }    /// Duplicates a process stdio descriptor so probe cleanup owns only the duplicate.    fn dup_file(fd: libc::c_int) -> io::Result<File> {        let duplicated = unsafe { libc::dup(fd) };        if duplicated == -1 {            return Err(io::Error::last_os_error());        }        Ok(unsafe { File::from_raw_fd(duplicated) })    }    /// Queries OSC 10 and OSC 11 default colors under one shared deadline.    ///    /// Foreground and background are only useful as a pair for palette calculations, so a missing    /// response from either slot returns `Ok(None)`. Both queries are sent before reading so a    /// terminal that supports palette replies gets the full bounded window to return both values,    /// while unsupported terminals still pay one bounded wait instead of one wait per slot.    pub(crate) fn default_colors(timeout: Duration) -> io::Result<Option<DefaultColors>> {        let mut tty = Tty::open()?;        tty.write_all(b"\x1B]10;?\x1B\\\x1B]11;?\x1B\\")?;        let Some(colors) = read_until(&mut tty, timeout, parse_default_colors)? else {            return Ok(None);        };        Ok(Some(colors))    }    /// Queries the terminal cursor position while normal input polling is paused.    ///    /// Resume can emit a focus report immediately before the cursor-position response. Reusing    /// the startup parser lets the probe find the response without leaking either sequence into    /// the composer.    pub(crate) fn cursor_position(timeout: Duration) -> io::Result<Option<Position>> {        let mut tty = Tty::open()?;        tty.write_all(b"\x1B[6n")?;        read_until(&mut tty, timeout, parse_cursor_position)    }    /// Runs the optional terminal queries needed during TUI startup under one shared deadline.    ///    /// Keeping these queries batched avoids paying one timeout per unsupported capability before    /// the first frame can render.    pub(crate) fn startup(        timeout: Duration,        keyboard_probe: StartupKeyboardEnhancementProbe,    ) -> io::Result<StartupProbe> {        let mut tty = Tty::open()?;        match keyboard_probe {            StartupKeyboardEnhancementProbe::Query => {                tty.write_all(b"\x1B[6n\x1B]10;?\x1B\\\x1B]11;?\x1B\\\x1B[?u\x1B[c")?;            }            StartupKeyboardEnhancementProbe::Skip => {                tty.write_all(b"\x1B[6n\x1B]10;?\x1B\\\x1B]11;?\x1B\\")?;            }        }        read_startup_probe(&mut tty, timeout, keyboard_probe)    }    /// Reads available terminal bytes until `parse` recognizes a probe response or time expires.    ///    /// The accumulated buffer may include unrelated terminal input. This helper intentionally does    /// not try to replay those bytes, so callers must use it only during short, exclusive probe    /// windows before normal crossterm input polling begins or while that polling is paused.    fn read_until<T>(        tty: &mut Tty,        timeout: Duration,        mut parse: impl FnMut(&[u8]) -> Option<T>,    ) -> io::Result<Option<T>> {        let deadline = Instant::now() + timeout;        let mut buffer = Vec::new();        loop {            tty.read_available(&mut buffer)?;            if let Some(value) = parse(&buffer) {                return Ok(Some(value));            }            let now = Instant::now();            if now >= deadline {                return Ok(None);            }            if !tty.poll_readable(deadline.saturating_duration_since(now))? {                return Ok(None);            }        }    }    fn read_startup_probe(        tty: &mut Tty,        timeout: Duration,        keyboard_probe: StartupKeyboardEnhancementProbe,    ) -> io::Result<StartupProbe> {        let deadline = Instant::now() + timeout;        let mut buffer = Vec::new();        let mut probe = StartupProbe {            cursor_position: None,            default_colors: None,            keyboard_enhancement_supported: None,        };        let mut saw_supported_keyboard = false;        loop {            tty.read_available(&mut buffer)?;            update_startup_probe(                &mut probe,                &mut saw_supported_keyboard,                &buffer,                keyboard_probe,            );            if startup_probe_complete(&probe, keyboard_probe) {                return Ok(probe);            }            let now = Instant::now();            if now >= deadline {                finish_startup_probe(&mut probe, keyboard_probe, saw_supported_keyboard);                return Ok(probe);            }            if !tty.poll_readable(deadline.saturating_duration_since(now))? {                finish_startup_probe(&mut probe, keyboard_probe, saw_supported_keyboard);                return Ok(probe);            }        }    }    fn update_startup_probe(        probe: &mut StartupProbe,        saw_supported_keyboard: &mut bool,        buffer: &[u8],        keyboard_probe: StartupKeyboardEnhancementProbe,    ) {        if probe.cursor_position.is_none() {            probe.cursor_position = parse_cursor_position(buffer);        }        if probe.default_colors.is_none() {            probe.default_colors = parse_default_colors(buffer);        }        if keyboard_probe == StartupKeyboardEnhancementProbe::Skip            || probe.keyboard_enhancement_supported.is_some()        {            return;        }        match parse_keyboard_enhancement_support(buffer) {            KeyboardProbeState::SupportedAndFallback => {                probe.keyboard_enhancement_supported = Some(true);            }            KeyboardProbeState::Supported => {                *saw_supported_keyboard = true;            }            KeyboardProbeState::UnsupportedFallback => {                probe.keyboard_enhancement_supported = Some(false);            }            KeyboardProbeState::Pending => {}        }    }    fn startup_probe_complete(        probe: &StartupProbe,        keyboard_probe: StartupKeyboardEnhancementProbe,    ) -> bool {        probe.cursor_position.is_some()            && probe.default_colors.is_some()            && (keyboard_probe == StartupKeyboardEnhancementProbe::Skip                || probe.keyboard_enhancement_supported.is_some())    }    fn finish_startup_probe(        probe: &mut StartupProbe,        keyboard_probe: StartupKeyboardEnhancementProbe,        saw_supported_keyboard: bool,    ) {        if keyboard_probe == StartupKeyboardEnhancementProbe::Query            && probe.keyboard_enhancement_supported.is_none()        {            probe.keyboard_enhancement_supported = saw_supported_keyboard.then_some(true);        }    }    fn parse_cursor_position(buffer: &[u8]) -> Option<Position> {        for start in find_all_subslices(buffer, b"\x1B[") {            let rest = &buffer[start + 2..];            let Some(end) = rest.iter().position(|b| *b == b'R') else {                continue;            };            let Ok(payload) = std::str::from_utf8(&rest[..end]) else {                continue;            };            let Some((row, col)) = payload.split_once(';') else {                continue;            };            let Ok(row) = row.parse::<u16>() else {                continue;            };            let Ok(col) = col.parse::<u16>() else {                continue;            };            let row = row.saturating_sub(1);            let col = col.saturating_sub(1);            return Some(Position { x: col, y: row });        }        None    }    /// Parser state for the keyboard enhancement probe.    ///    /// `UnsupportedFallback` records that a primary-device-attributes response arrived without    /// keyboard flags. Startup treats that as unsupported immediately, matching crossterm's    /// previous behavior and avoiding a fixed delay in terminals without the keyboard protocol.    /// `Supported` records that keyboard flags arrived, but the caller should still drain the PDA    /// fallback response if it arrives before the deadline so those bytes do not leak into the    /// normal event stream.    #[derive(Debug, Clone, Copy, Eq, PartialEq)]    enum KeyboardProbeState {        Pending,        UnsupportedFallback,        Supported,        SupportedAndFallback,    }    fn parse_keyboard_enhancement_support(buffer: &[u8]) -> KeyboardProbeState {        match (            find_keyboard_flags(buffer).is_some(),            find_primary_device_attributes(buffer).is_some(),        ) {            (true, true) => KeyboardProbeState::SupportedAndFallback,            (true, false) => KeyboardProbeState::Supported,            (false, true) => KeyboardProbeState::UnsupportedFallback,            (false, false) => KeyboardProbeState::Pending,        }    }    fn find_keyboard_flags(buffer: &[u8]) -> Option<KeyboardEnhancementFlags> {        for start in find_all_subslices(buffer, b"\x1B[?") {            let rest = &buffer[start + 3..];            let Some(end) = rest.iter().position(|b| *b == b'u') else {                continue;            };            if end == 0 {                continue;            }            let Ok(bits_text) = std::str::from_utf8(&rest[..end]) else {                continue;            };            let Ok(bits) = bits_text.parse::<u8>() else {                continue;            };            let mut flags = KeyboardEnhancementFlags::empty();            if bits & 1 != 0 {                flags |= KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES;            }            if bits & 2 != 0 {                flags |= KeyboardEnhancementFlags::REPORT_EVENT_TYPES;            }            if bits & 4 != 0 {                flags |= KeyboardEnhancementFlags::REPORT_ALTERNATE_KEYS;            }            if bits & 8 != 0 {                flags |= KeyboardEnhancementFlags::REPORT_ALL_KEYS_AS_ESCAPE_CODES;            }            return Some(flags);        }        None    }    fn find_primary_device_attributes(buffer: &[u8]) -> Option<()> {        for start in find_all_subslices(buffer, b"\x1B[?") {            let rest = &buffer[start + 3..];            let Some(end) = rest.iter().position(|b| *b == b'c') else {                continue;            };            if end > 0 && rest[..end].iter().all(|b| b.is_ascii_digit() || *b == b';') {                return Some(());            }        }        None    }    fn find_all_subslices<'a>(        haystack: &'a [u8],        needle: &'a [u8],    ) -> impl Iterator<Item = usize> + 'a {        haystack            .windows(needle.len())            .enumerate()            .filter_map(move |(idx, window)| (window == needle).then_some(idx))    }    #[cfg(test)]    mod tests {        use super::*;        use pretty_assertions::assert_eq;        #[test]        fn parses_cursor_position_as_zero_based() {            assert_eq!(                parse_cursor_position(b"\x1B[20;10R"),                Some(Position { x: 9, y: 19 })            );            assert_eq!(                parse_cursor_position(b"\x1B[I\x1B[20;10R"),                Some(Position { x: 9, y: 19 })            );        }        #[test]        fn parses_keyboard_enhancement_flags_and_pda_fallback() {            assert_eq!(                parse_keyboard_enhancement_support(b"\x1B[?7u"),                KeyboardProbeState::Supported            );            assert_eq!(                parse_keyboard_enhancement_support(b"\x1B[?64;1;2c"),                KeyboardProbeState::UnsupportedFallback            );            assert_eq!(                parse_keyboard_enhancement_support(b"\x1B[?64;1;2c\x1B[?7u"),                KeyboardProbeState::SupportedAndFallback            );            assert_eq!(                parse_keyboard_enhancement_support(b"\x1B[?7u\x1B[?64;1;2c"),                KeyboardProbeState::SupportedAndFallback            );            assert_eq!(                parse_keyboard_enhancement_support(b""),                KeyboardProbeState::Pending            );        }        #[test]        fn startup_probe_parses_batched_terminal_responses() {            let mut probe = StartupProbe {                cursor_position: None,                default_colors: None,                keyboard_enhancement_supported: None,            };            let mut saw_supported_keyboard = false;            update_startup_probe(                &mut probe,                &mut saw_supported_keyboard,                b"\x1B[20;10R\x1B]11;rgb:1111/1111/1111\x07\x1B[?64;1;2c\x1B]10;rgb:eeee/eeee/eeee\x1B\\\x1B[?7u",                StartupKeyboardEnhancementProbe::Query,            );            assert_eq!(                probe,                StartupProbe {                    cursor_position: Some(Position { x: 9, y: 19 }),                    default_colors: Some(DefaultColors {                        fg: (238, 238, 238),                        bg: (17, 17, 17),                    }),                    keyboard_enhancement_supported: Some(true),                }            );            assert!(startup_probe_complete(                &probe,                StartupKeyboardEnhancementProbe::Query            ));        }    }}#[cfg(windows)]mod imp {    use super::DefaultColors;    use super::parse_default_colors;    use std::io;    use std::io::ErrorKind;    use std::time::Duration;    use std::time::Instant;    use windows_sys::Win32::Foundation::HANDLE;    use windows_sys::Win32::Foundation::INVALID_HANDLE_VALUE;    use windows_sys::Win32::Foundation::WAIT_OBJECT_0;    use windows_sys::Win32::Foundation::WAIT_TIMEOUT;    use windows_sys::Win32::Storage::FileSystem::ReadFile;    use windows_sys::Win32::Storage::FileSystem::WriteFile;    use windows_sys::Win32::System::Console::CONSOLE_SCREEN_BUFFER_INFOEX;    use windows_sys::Win32::System::Console::ENABLE_VIRTUAL_TERMINAL_INPUT;    use windows_sys::Win32::System::Console::GetConsoleMode;    use windows_sys::Win32::System::Console::GetConsoleScreenBufferInfoEx;    use windows_sys::Win32::System::Console::GetStdHandle;    use windows_sys::Win32::System::Console::STD_INPUT_HANDLE;    use windows_sys::Win32::System::Console::STD_OUTPUT_HANDLE;    use windows_sys::Win32::System::Console::SetConsoleMode;    use windows_sys::Win32::System::Threading::WaitForSingleObject;    /// Queries OSC 10 and OSC 11 default colors under one shared deadline.    ///    /// The Windows path uses raw console handles because crossterm's public color query helper is    /// currently Unix-only. Failures and missing responses are reported as `Ok(None)` by callers so    /// terminals without OSC 10/11 support keep the existing conservative palette fallback.    pub(crate) fn default_colors(timeout: Duration) -> io::Result<Option<DefaultColors>> {        let Ok(output) = std_handle(STD_OUTPUT_HANDLE) else {            return Ok(None);        };        if let Ok(input) = std_handle(STD_INPUT_HANDLE)            && let Ok(Some(colors)) = query_osc_default_colors(input, output, timeout)        {            return Ok(Some(colors));        }        Ok(query_console_default_colors(output).ok().flatten())    }    fn query_osc_default_colors(        input: HANDLE,        output: HANDLE,        timeout: Duration,    ) -> io::Result<Option<DefaultColors>> {        let _vt_input = VirtualTerminalInputMode::enable(input)?;        write_all(output, b"\x1B]10;?\x1B\\\x1B]11;?\x1B\\")?;        read_until(input, timeout, parse_default_colors)    }    fn query_console_default_colors(output: HANDLE) -> io::Result<Option<DefaultColors>> {        let mut info = unsafe { std::mem::zeroed::<CONSOLE_SCREEN_BUFFER_INFOEX>() };        info.cbSize = std::mem::size_of::<CONSOLE_SCREEN_BUFFER_INFOEX>() as u32;        if unsafe { GetConsoleScreenBufferInfoEx(output, &mut info) } == 0 {            return Err(io::Error::last_os_error());        }        Ok(Some(decode_console_default_colors(            info.wAttributes,            &info.ColorTable,        )))    }    fn decode_console_default_colors(attributes: u16, color_table: &[u32; 16]) -> DefaultColors {        let fg_index = (attributes & 0x0f) as usize;        let bg_index = ((attributes >> 4) & 0x0f) as usize;        // COMMON_LVB_REVERSE_VIDEO changes how cells render, but this probe is discovering the        // configured default colors for palette blending. Keep the attribute fg/bg indices as-is.        DefaultColors {            fg: decode_color_ref(color_table[fg_index]),            bg: decode_color_ref(color_table[bg_index]),        }    }    fn decode_color_ref(color_ref: u32) -> (u8, u8, u8) {        (            (color_ref & 0xff) as u8,            ((color_ref >> 8) & 0xff) as u8,            ((color_ref >> 16) & 0xff) as u8,        )    }    fn std_handle(kind: u32) -> io::Result<HANDLE> {        let handle = unsafe { GetStdHandle(kind) };        if handle == 0 || handle == INVALID_HANDLE_VALUE {            return Err(io::Error::last_os_error());        }        Ok(handle)    }    struct VirtualTerminalInputMode {        handle: HANDLE,        original_mode: u32,    }    impl VirtualTerminalInputMode {        fn enable(handle: HANDLE) -> io::Result<Self> {            let mut original_mode = 0;            if unsafe { GetConsoleMode(handle, &mut original_mode) } == 0 {                return Err(io::Error::last_os_error());            }            let requested_mode = original_mode | ENABLE_VIRTUAL_TERMINAL_INPUT;            if unsafe { SetConsoleMode(handle, requested_mode) } == 0 {                return Err(io::Error::last_os_error());            }            Ok(Self {                handle,                original_mode,            })        }    }    impl Drop for VirtualTerminalInputMode {        fn drop(&mut self) {            unsafe {                SetConsoleMode(self.handle, self.original_mode);            }        }    }    fn write_all(handle: HANDLE, mut bytes: &[u8]) -> io::Result<()> {        while !bytes.is_empty() {            let mut written = 0;            let ok = unsafe {                WriteFile(                    handle,                    bytes.as_ptr().cast(),                    bytes.len().min(u32::MAX as usize) as u32,                    &mut written,                    std::ptr::null_mut(),                )            };            if ok == 0 {                return Err(io::Error::last_os_error());            }            if written == 0 {                return Err(io::Error::from(ErrorKind::WriteZero));            }            bytes = &bytes[written as usize..];        }        Ok(())    }    fn read_until<T>(        handle: HANDLE,        timeout: Duration,        mut parse: impl FnMut(&[u8]) -> Option<T>,    ) -> io::Result<Option<T>> {        let deadline = Instant::now() + timeout;        let mut buffer = Vec::new();        loop {            if let Some(value) = parse(&buffer) {                return Ok(Some(value));            }            let now = Instant::now();            if now >= deadline {                return Ok(None);            }            let timeout_ms = deadline                .saturating_duration_since(now)                .as_millis()                .min(u32::MAX as u128) as u32;            match unsafe { WaitForSingleObject(handle, timeout_ms) } {                WAIT_OBJECT_0 => read_once(handle, &mut buffer)?,                WAIT_TIMEOUT => return Ok(None),                _ => return Err(io::Error::last_os_error()),            }        }    }    fn read_once(handle: HANDLE, buffer: &mut Vec<u8>) -> io::Result<()> {        let mut chunk = [0_u8; 256];        let mut read = 0;        let ok = unsafe {            ReadFile(                handle,                chunk.as_mut_ptr().cast(),                chunk.len() as u32,                &mut read,                std::ptr::null_mut(),            )        };        if ok == 0 {            return Err(io::Error::last_os_error());        }        buffer.extend_from_slice(&chunk[..read as usize]);        Ok(())    }    #[cfg(test)]    mod tests {        use super::*;        use pretty_assertions::assert_eq;        use windows_sys::Win32::System::Console::COMMON_LVB_REVERSE_VIDEO;        fn color_table() -> [u32; 16] {            [                0x00000000, 0x00000080, 0x00008000, 0x00008080, 0x00800000, 0x00800080, 0x00808000,                0x00c0c0c0, 0x00808080, 0x000000ff, 0x0000ff00, 0x0000ffff, 0x00ff0000, 0x00ff00ff,                0x00ffff00, 0x00ffffff,            ]        }        #[test]        fn decodes_console_color_attribute_indices() {            assert_eq!(                decode_console_default_colors(/*attributes*/ 0x21, &color_table()),                DefaultColors {                    fg: (128, 0, 0),                    bg: (0, 128, 0),                }            );        }        #[test]        fn decodes_console_color_intensity_indices() {            assert_eq!(                decode_console_default_colors(/*attributes*/ 0xe9, &color_table()),                DefaultColors {                    fg: (255, 0, 0),                    bg: (0, 255, 255),                }            );        }        #[test]        fn decodes_console_color_ref_byte_order() {            let mut colors = color_table();            colors[3] = 0x00112233;            colors[4] = 0x00aabbcc;            assert_eq!(                decode_console_default_colors(/*attributes*/ 0x43, &colors),                DefaultColors {                    fg: (0x33, 0x22, 0x11),                    bg: (0xcc, 0xbb, 0xaa),                }            );        }        #[test]        fn ignores_reverse_video_when_decoding_default_colors() {            assert_eq!(                decode_console_default_colors(                    /*attributes*/ COMMON_LVB_REVERSE_VIDEO | 0x21,                    &color_table(),                ),                DefaultColors {                    fg: (128, 0, 0),                    bg: (0, 128, 0),                }            );        }    }}fn parse_osc_color(buffer: &[u8], slot: u8) -> Option<(u8, u8, u8)> {    let prefix = format!("\x1B]{slot};");    let start = find_subslice(buffer, prefix.as_bytes())?;    let payload_start = start + prefix.len();    let rest = &buffer[payload_start..];    let (payload_end, _terminator_len) = osc_payload_end(rest)?;    let payload = std::str::from_utf8(&rest[..payload_end]).ok()?;    parse_osc_rgb(payload)}fn parse_default_colors(buffer: &[u8]) -> Option<DefaultColors> {    let fg = parse_osc_color(buffer, /*slot*/ 10)?;    let bg = parse_osc_color(buffer, /*slot*/ 11)?;    Some(DefaultColors { fg, bg })}fn osc_payload_end(buffer: &[u8]) -> Option<(usize, usize)> {    let mut idx = 0;    while idx < buffer.len() {        match buffer[idx] {            0x07 => return Some((idx, 1)),            0x1B if buffer.get(idx + 1) == Some(&b'\\') => return Some((idx, 2)),            _ => idx += 1,        }    }    None}fn parse_osc_rgb(payload: &str) -> Option<(u8, u8, u8)> {    let (prefix, values) = payload.trim().split_once(':')?;    if !prefix.eq_ignore_ascii_case("rgb") && !prefix.eq_ignore_ascii_case("rgba") {        return None;    }    let mut parts = values.split('/');    let r = parse_osc_component(parts.next()?)?;    let g = parse_osc_component(parts.next()?)?;    let b = parse_osc_component(parts.next()?)?;    if prefix.eq_ignore_ascii_case("rgba") {        parse_osc_component(parts.next()?)?;    }    parts.next().is_none().then_some((r, g, b))}fn parse_osc_component(component: &str) -> Option<u8> {    match component.len() {        2 => u8::from_str_radix(component, 16).ok(),        4 => u16::from_str_radix(component, 16)            .ok()            .map(|value| (value / 257) as u8),        _ => None,    }}fn find_subslice(haystack: &[u8], needle: &[u8]) -> Option<usize> {    haystack        .windows(needle.len())        .position(|window| window == needle)}#[cfg(any(unix, windows))]pub(crate) use imp::*;#[cfg(test)]mod tests {    use super::*;    use pretty_assertions::assert_eq;    #[test]    fn parses_osc_colors_with_bel_and_st() {        assert_eq!(            parse_osc_color(b"\x1B]10;rgb:ffff/8000/0000\x07", /*slot*/ 10),            Some((255, 127, 0))        );        assert_eq!(            parse_osc_color(b"\x1B]11;rgba:00/80/ff/ff\x1B\\", /*slot*/ 11),            Some((0, 128, 255))        );    }    #[test]    fn parses_two_and_four_digit_color_components() {        assert_eq!(parse_osc_rgb("rgb:00/80/ff"), Some((0, 128, 255)));        assert_eq!(            parse_osc_rgb("rgba:ffff/8000/0000/ffff"),            Some((255, 127, 0))        );    }    #[test]    fn parses_default_colors_from_one_buffer() {        assert_eq!(            parse_default_colors(b"\x1B]10;rgb:eeee/eeee/eeee\x1B\\\x1B]11;rgb:1111/1111/1111\x07"),            Some(DefaultColors {                fg: (238, 238, 238),                bg: (17, 17, 17)            })        );        assert_eq!(            parse_default_colors(b"\x1B]11;rgb:1111/1111/1111\x07\x1B]10;rgb:eeee/eeee/eeee\x1B\\"),            Some(DefaultColors {                fg: (238, 238, 238),                bg: (17, 17, 17)            })        );        assert_eq!(            parse_default_colors(b"\x1B]10;rgb:eeee/eeee/eeee\x1B\\"),            None        );    }    #[test]    fn ignores_malformed_or_partial_default_color_responses() {        assert_eq!(            parse_default_colors(b"\x1B]10;rgb:eeee/eeee/eeee\x1B\\\x1B]11;rgb:nope\x07"),            None        );        assert_eq!(            parse_default_colors(b"\x1B]10;rgb:eeee/eeee/eeee\x1B\\\x1B]11;rgb:11/11/11/11\x07"),            None        );        assert_eq!(            parse_default_colors(b"\x1B]10;rgb:eeee/eeee/eeee\x1B\\\x1B]11;rgb:1111/1111/1111"),            None        );    }    #[test]    fn parses_default_colors_with_unrelated_bytes() {        assert_eq!(            parse_default_colors(                b"typed\x1B]10;rgb:eeee/eeee/eeee\x1B\\noise\x1B]11;rgb:1111/1111/1111\x07"            ),            Some(DefaultColors {                fg: (238, 238, 238),                bg: (17, 17, 17),            })        );    }}