Work around terminals that echo DCS queries

Some terminals such as conhost and putty cannot parse DCS commands,
and will echo them back.

Work around this by making sure that this echoed text will not
be visible.

Do so by temporarily enabling the alternative screen buffer when
sending DCS queries (in this case only XTGETTCAP).  The alternative
screen buffer feature seems widely supported, and easier to get right
than trying to clear individual lines etc.

The alternative screen may still be visible for a
short time.  Luckily we can use [Synchronized Output](
https://gist.github.com/christianparpart/d8a62cc1ab659194337d73e399004036)
to make sure the screen change is never visible to the user.

Querying support for that is deemed safe since it only requires a
CSI command.

Note that it seems that every terminal that supports Synchronized
Output also parses DCS commands successfully.  This means that we
could get away without the alternative screen buffer in practice.
Not sure yet.

The implementation is slightly more complex than necessary in that it
defines a redundant ImplicitEvent. This is for two reasons: 1. I have
a pending change that wants to use it, so this removes diff noise and
2. we historically have sc/input_common.rs not depend on src/output.rs.
I dont' think any are strong reasons though.
This commit is contained in:
Johannes Altmanninger
2025-01-08 10:22:00 +01:00
parent e6d57f2fb2
commit 14df28382d
3 changed files with 38 additions and 8 deletions

View File

@@ -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 @@ fn parse_csi(&mut self, buffer: &mut Vec<u8>) -> Option<Key> {
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] {