diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 6dcafd4b8..2949e2418 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -23,8 +23,9 @@ New or improved bindings - :kbd:`ctrl-z` (undo) after executing a command will restore the previous cursor position instead of placing the cursor at the end of the command line. - The OSC 133 prompt marking feature has learned about kitty's ``click_events=1`` flag, which allows moving fish's cursor by clicking. - :kbd:`ctrl-l` no longer clears the screen but only pushes to the terminal's scrollback all text above the prompt (via a new special input function ``scrollback-push``). - This feature depends on the terminal advertising via XTGETTCAP support for the ``indn`` and ``cuu`` terminfo capabilities. - If not presesnt, the binding falls back to ``clear-screen``. + This feature depends on the terminal advertising via XTGETTCAP support for the ``indn`` and ``cuu`` terminfo capabilities, + and on the terminal supporting Synchronized Output (which is used by fish to detect features). + If any is missing, the binding falls back to ``clear-screen``. Completions ^^^^^^^^^^^ diff --git a/src/input_common.rs b/src/input_common.rs index e2b3c589d..f1abc1dd9 100644 --- a/src/input_common.rs +++ b/src/input_common.rs @@ -194,6 +194,8 @@ pub enum ImplicitEvent { MouseLeftClickContinuation(ViewportPosition, ViewportPosition), /// Push prompt to top. ScrollbackPushContinuation(usize), + /// The Synchronized Output feature is supported by the terminal. + SynchronizedOutputSupported, } #[derive(Debug, Clone)] @@ -957,8 +959,14 @@ pub trait InputEventQueuer { let key = match c { b'$' => { + // DECRPM if private_mode == Some(b'?') && next_char(self) == b'y' { - // DECRPM + if params[0][0] == 2026 && matches!(params[1][0], 1 | 2) { + self.push_front(CharEvent::Implicit( + ImplicitEvent::SynchronizedOutputSupported, + )); + } + return None; } match params[0][0] { diff --git a/src/reader.rs b/src/reader.rs index e6d7ca625..caa2ee00c 100644 --- a/src/reader.rs +++ b/src/reader.rs @@ -2098,11 +2098,8 @@ impl<'a> Reader<'a> { let _ = out.write(KITTY_PROGRESSIVE_ENHANCEMENTS_QUERY); // Query for cursor position reporting support. zelf.request_cursor_position(&mut out, CursorPositionWait::InitialFeatureProbe); - let mut xtgettcap = |cap| { - let _ = write!(&mut out, "\x1bP+q{}\x1b\\", DisplayAsHex(cap)); - }; - xtgettcap("indn"); - xtgettcap("cuu"); + // Query for synchronized output support. + let _ = out.write(b"\x1b[?2026$p"); out.end_buffering(); } @@ -2422,12 +2419,36 @@ impl<'a> Reader<'a> { self.screen.push_to_scrollback(cursor_y); self.stop_waiting_for_cursor_position(); } + ImplicitEvent::SynchronizedOutputSupported => { + synchronized_supported(); + } }, } ControlFlow::Continue(()) } } +fn xtgettcap(out: &mut impl Write, cap: &str) { + let _ = write!(out, "\x1bP+q{}\x1b\\", DisplayAsHex(cap)); +} + +fn synchronized_supported() { + static QUERIED: RelaxedAtomicBool = RelaxedAtomicBool::new(false); + if QUERIED.load() { + return; + } + QUERIED.store(true); + let mut out = Outputter::stdoutput().borrow_mut(); + out.begin_buffering(); + let _ = out.write(b"\x1b[?2026h"); // begin synchronized update + let _ = out.write(b"\x1b[?1049h"); // enable alternative screen buffer + xtgettcap(out.by_ref(), "indn"); + xtgettcap(out.by_ref(), "cuu"); + let _ = out.write(b"\x1b[?1049l"); // disable alternative screen buffer + let _ = out.write(b"\x1b[?2026l"); // end synchronized update + out.end_buffering(); +} + impl<'a> Reader<'a> { // Convenience cover to return the length of the command line. fn command_line_len(&self) -> usize {