diff --git a/src/builtins/fish_key_reader.rs b/src/builtins/fish_key_reader.rs index 587e0f34e..74342a7bd 100644 --- a/src/builtins/fish_key_reader.rs +++ b/src/builtins/fish_key_reader.rs @@ -7,7 +7,7 @@ //! //! Type "exit" or "quit" to terminate the program. -use std::{io::Write, ops::ControlFlow, os::unix::prelude::OsStrExt}; +use std::{ops::ControlFlow, os::unix::prelude::OsStrExt}; use libc::{STDIN_FILENO, TCSANOW, VEOF, VINTR}; @@ -19,13 +19,11 @@ use crate::{ env::env_init, input::input_terminfo_get_name, input_common::{ - enable_kitty_progressive_enhancements, kitty_progressive_enhancements_query, - terminal_protocol_hacks, terminal_protocols_enable_ifn, CharEvent, ImplicitEvent, - InputEventQueue, InputEventQueuer, KITTY_KEYBOARD_SUPPORTED, + kitty_progressive_enhancements_query, terminal_protocol_hacks, + terminal_protocols_enable_ifn, CharEvent, InputEventQueue, InputEventQueuer, }, key::{char_to_symbol, Key}, nix::isatty, - output::Outputter, panic::panic_handler, print_help::print_help, proc::set_interactive_session, @@ -96,22 +94,12 @@ fn process_input(streams: &mut IoStreams, continuous_mode: bool, verbose: bool) let mut recent_chars2 = vec![]; streams.err.appendln("Press a key:\n"); - terminal_protocols_enable_ifn(); while (!first_char_seen || continuous_mode) && !check_exit_loop_maybe_warning(None) { + terminal_protocols_enable_ifn(); let evt = queue.readch(); - let kevt = match evt { - CharEvent::Key(kevt) => kevt, - CharEvent::Readline(_) | CharEvent::Command(_) => continue, - CharEvent::Implicit(ImplicitEvent::PrimaryDeviceAttribute) => { - if KITTY_KEYBOARD_SUPPORTED.load() { - enable_kitty_progressive_enhancements( - Outputter::stdoutput().borrow_mut().by_ref(), - ); - } - continue; - } - CharEvent::Implicit(_) => continue, + let CharEvent::Key(kevt) = evt else { + continue; }; let c = kevt.key.codepoint; if verbose { diff --git a/src/input_common.rs b/src/input_common.rs index 3557c2d2f..95a197bd0 100644 --- a/src/input_common.rs +++ b/src/input_common.rs @@ -2,7 +2,7 @@ use libc::STDOUT_FILENO; use crate::common::{ fish_reserved_codepoint, is_windows_subsystem_for_linux, read_blocked, shell_modes, - str2wcstring, write_loop, WSL, + str2wcstring, write_loop, ScopeGuard, WSL, }; use crate::env::{EnvStack, Environment}; use crate::fd_readable_set::{FdReadableSet, Timeout}; @@ -24,7 +24,7 @@ use std::ops::ControlFlow; use std::os::fd::RawFd; use std::os::unix::ffi::OsStrExt; use std::ptr; -use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; +use std::sync::atomic::{AtomicBool, AtomicU8, AtomicUsize, Ordering}; // The range of key codes for inputrc-style keyboard functions. pub const R_END_INPUT_FUNCTIONS: usize = (ReadlineCmd::ReverseRepeatJump as usize) + 1; @@ -444,23 +444,24 @@ pub fn update_wait_on_sequence_key_ms(vars: &EnvStack) { } static TERMINAL_PROTOCOLS: AtomicBool = AtomicBool::new(false); +static BRACKETED_PASTE: AtomicBool = AtomicBool::new(false); pub(crate) static SCROLL_FORWARD_SUPPORTED: RelaxedAtomicBool = RelaxedAtomicBool::new(false); pub(crate) static CURSOR_UP_SUPPORTED: RelaxedAtomicBool = RelaxedAtomicBool::new(false); -pub(crate) static KITTY_KEYBOARD_SUPPORTED: RelaxedAtomicBool = RelaxedAtomicBool::new(false); +#[repr(u8)] +pub(crate) enum Capability { + Unknown, + Supported, + NotSupported, +} +pub(crate) static KITTY_KEYBOARD_SUPPORTED: AtomicU8 = AtomicU8::new(Capability::Unknown as _); pub(crate) static SYNCHRONIZED_OUTPUT_SUPPORTED: RelaxedAtomicBool = RelaxedAtomicBool::new(false); pub(crate) static CURSOR_POSITION_REPORTING_SUPPORTED: RelaxedAtomicBool = RelaxedAtomicBool::new(false); -macro_rules! kitty_progressive_enhancements { - () => { - "\x1b[=5u" - }; -} - pub fn kitty_progressive_enhancements_query() -> &'static [u8] { if std::env::var_os("TERM").is_some_and(|term| term.as_os_str().as_bytes() == b"st-256color") { return b""; @@ -468,14 +469,6 @@ pub fn kitty_progressive_enhancements_query() -> &'static [u8] { b"\x1b[?u" } -pub(crate) fn enable_kitty_progressive_enhancements(out: &mut impl std::io::Write) -> bool { - if IN_MIDNIGHT_COMMANDER_PRE_CSI_U.load() || IN_ITERM_PRE_CSI_U.load() { - return false; - } - let _ = out.write(kitty_progressive_enhancements!().as_bytes()); - true -} - static IS_TMUX: RelaxedAtomicBool = RelaxedAtomicBool::new(false); pub static IN_MIDNIGHT_COMMANDER_PRE_CSI_U: RelaxedAtomicBool = RelaxedAtomicBool::new(false); static IN_ITERM_PRE_CSI_U: RelaxedAtomicBool = RelaxedAtomicBool::new(false); @@ -515,56 +508,72 @@ fn test_parse_version() { } pub fn terminal_protocols_enable_ifn() { + let did_write = RelaxedAtomicBool::new(false); + let _save_screen_state = ScopeGuard::new((), |()| { + if did_write.load() { + reader_current_data().map(|data| data.save_screen_state()); + } + }); + if !BRACKETED_PASTE.load(Ordering::Relaxed) { + BRACKETED_PASTE.store(true, Ordering::Release); + let _ = write_loop(&STDOUT_FILENO, b"\x1b[?2004h"); + if IS_TMUX.load() { + let _ = write_loop(&STDOUT_FILENO, "\x1b[?1004h".as_bytes()); // focus reporting + } + did_write.store(true); + } + if IN_MIDNIGHT_COMMANDER_PRE_CSI_U.load() { + return; + } + let kitty_keyboard_supported = KITTY_KEYBOARD_SUPPORTED.load(Ordering::Relaxed); + if kitty_keyboard_supported == Capability::Unknown as _ { + return; + } if TERMINAL_PROTOCOLS.load(Ordering::Relaxed) { return; } TERMINAL_PROTOCOLS.store(true, Ordering::Release); - let sequences = if IN_MIDNIGHT_COMMANDER_PRE_CSI_U.load() { - "\x1b[?2004h" - } else if !KITTY_KEYBOARD_SUPPORTED.load() || IN_ITERM_PRE_CSI_U.load() { - concat!("\x1b[?2004h", "\x1b[>4;1m", "\x1b=",) + FLOG!(term_protocols, "Enabling extended keys"); + if kitty_keyboard_supported == Capability::NotSupported as _ || IN_ITERM_PRE_CSI_U.load() { + let _ = write_loop(&STDOUT_FILENO, b"\x1b[>4;1m"); // XTerm's modifyOtherKeys + let _ = write_loop(&STDOUT_FILENO, b"\x1b="); // set application keypad mode, so the keypad keys send unique codes } else { - concat!( - "\x1b[?2004h", // Bracketed paste - // "\x1b[>4;1m", // XTerm's modifyOtherKeys - no use in doing this with kitty protocol - kitty_progressive_enhancements!(), - "\x1b=", // set application keypad mode, so the keypad keys send unique codes - ) - }; - FLOG!(term_protocols, "Enabling extended keys and bracketed paste"); - let _ = write_loop(&STDOUT_FILENO, sequences.as_bytes()); - if IS_TMUX.load() { - let _ = write_loop(&STDOUT_FILENO, "\x1b[?1004h".as_bytes()); + let _ = write_loop(&STDOUT_FILENO, b"\x1b[=5u"); // kitty progressive enhancements } - reader_current_data().map(|data| data.save_screen_state()); + did_write.store(true); } pub(crate) fn terminal_protocols_disable_ifn() { + let did_write = RelaxedAtomicBool::new(false); + let _save_screen_state = is_main_thread().then(|| { + ScopeGuard::new((), |()| { + if did_write.load() { + reader_current_data().map(|data| data.save_screen_state()); + } + }) + }); + if BRACKETED_PASTE.load(Ordering::Acquire) { + BRACKETED_PASTE.store(false, Ordering::Release); + let _ = write_loop(&STDOUT_FILENO, b"\x1b[?2004l"); + if IS_TMUX.load() { + let _ = write_loop(&STDOUT_FILENO, "\x1b[?1004l".as_bytes()); + } + did_write.store(true); + } if !TERMINAL_PROTOCOLS.load(Ordering::Acquire) { return; } - let sequences = if !KITTY_KEYBOARD_SUPPORTED.load() || IN_ITERM_PRE_CSI_U.load() { - concat!("\x1b[?2004l", "\x1b[>4;0m", "\x1b>",) + FLOG_SAFE!(term_protocols, "Disabling extended keys"); + let kitty_keyboard_supported = KITTY_KEYBOARD_SUPPORTED.load(Ordering::Acquire); + assert_ne!(kitty_keyboard_supported, Capability::Unknown as _); + if kitty_keyboard_supported == Capability::NotSupported as _ || IN_ITERM_PRE_CSI_U.load() { + let _ = write_loop(&STDOUT_FILENO, b"\x1b[>4;0m"); // XTerm's modifyOtherKeys + let _ = write_loop(&STDOUT_FILENO, b"\x1b>"); // application keypad mode } else { - concat!( - "\x1b[?2004l", // Bracketed paste - // "\x1b[>4;0m", // XTerm's modifyOtherKeys - "\x1b[=0u", // CSI u with kitty progressive enhancement - "\x1b>", // application keypad mode - ) - }; - FLOG_SAFE!( - term_protocols, - "Disabling extended keys and bracketed paste" - ); - let _ = write_loop(&STDOUT_FILENO, sequences.as_bytes()); - if IS_TMUX.load() { - let _ = write_loop(&STDOUT_FILENO, "\x1b[?1004l".as_bytes()); - } - if is_main_thread() { - reader_current_data().map(|data| data.save_screen_state()); + let _ = write_loop(&STDOUT_FILENO, b"\x1b[=0u"); // kitty progressive enhancements } TERMINAL_PROTOCOLS.store(false, Ordering::Release); + did_write.store(true); } fn parse_mask(mask: u32) -> Modifiers { @@ -1160,7 +1169,7 @@ pub trait InputEventQueuer { reader, "Received kitty progressive enhancement flags, marking as supported" ); - KITTY_KEYBOARD_SUPPORTED.store(true); + KITTY_KEYBOARD_SUPPORTED.store(Capability::Supported as _, Ordering::Release); return None; } diff --git a/src/reader.rs b/src/reader.rs index edf0e4224..8c02e797b 100644 --- a/src/reader.rs +++ b/src/reader.rs @@ -81,9 +81,9 @@ use crate::history::{ SearchType, }; use crate::input::init_input; -use crate::input_common::enable_kitty_progressive_enhancements; use crate::input_common::kitty_progressive_enhancements_query; use crate::input_common::BlockingWait; +use crate::input_common::Capability; use crate::input_common::CursorPositionWait; use crate::input_common::ImplicitEvent; use crate::input_common::InputEventQueuer; @@ -2506,22 +2506,18 @@ impl<'a> Reader<'a> { match stage { Queried::NotYet => panic!(), Queried::Once => { - let mut out = Outputter::stdoutput().borrow_mut(); - out.begin_buffering(); - let mut querying = false; - if KITTY_KEYBOARD_SUPPORTED.load() { - enable_kitty_progressive_enhancements(out.by_ref()); - querying = true; + if KITTY_KEYBOARD_SUPPORTED.load(Ordering::Relaxed) + == Capability::Unknown as _ + { + KITTY_KEYBOARD_SUPPORTED + .store(Capability::NotSupported as _, Ordering::Release); } if SYNCHRONIZED_OUTPUT_SUPPORTED.load() { + let mut out = Outputter::stdoutput().borrow_mut(); + out.begin_buffering(); query_capabilities_via_dcs(out.by_ref()); - querying = true; - } - if querying { let _ = out.write(QUERY_PRIMARY_DEVICE_ATTRIBUTE); - } - out.end_buffering(); - if querying { + out.end_buffering(); self.save_screen_state(); self.blocking_wait = Some(BlockingWait::Startup(Queried::Twice)); return ControlFlow::Continue(());