tastty-core 0.1.0

Sans-IO core of the tastty terminal session library: VT parser, screen buffer, and byte encoders.
//! [`TerminalMode`] and the wire-mode bit constants behind it.

pub(crate) const MODE_APPLICATION_CURSOR: u32 = 1 << 0;
pub(crate) const MODE_BRACKETED_PASTE: u32 = 1 << 1;
pub(crate) const MODE_HIDE_CURSOR: u32 = 1 << 2;
pub(crate) const MODE_ALTERNATE_SCREEN: u32 = 1 << 3;
pub(crate) const MODE_LINE_WRAP: u32 = 1 << 4;
pub(crate) const MODE_MOUSE_REPORT_CLICK: u32 = 1 << 5;
pub(crate) const MODE_MOUSE_REPORT_CELL_MOTION: u32 = 1 << 6;
pub(crate) const MODE_MOUSE_REPORT_ALL_MOTION: u32 = 1 << 7;
pub(crate) const MODE_FOCUS_IN_OUT: u32 = 1 << 8;
pub(crate) const MODE_SGR_MOUSE: u32 = 1 << 9;
pub(crate) const MODE_SYNC_UPDATE: u32 = 1 << 10;
pub(crate) const MODE_INSERT: u32 = 1 << 11;
pub(crate) const MODE_COLOR_SCHEME_UPDATES: u32 = 1 << 12;
pub(crate) const MODE_IN_BAND_RESIZE: u32 = 1 << 13;
pub(crate) const MODE_SGR_PIXEL_MOUSE: u32 = 1 << 14;
pub(crate) const MODE_GRAPHEME_CLUSTER: u32 = 1 << 15;
pub(crate) const MODE_REVERSE_VIDEO: u32 = 1 << 16;
pub(crate) const MODE_ALTERNATE_SCROLL: u32 = 1 << 17;
pub(crate) const MODE_MOUSE_X10: u32 = 1 << 18;
pub(crate) const MODE_BACKSPACE_BS: u32 = 1 << 19;
pub(crate) const MODE_LNM: u32 = 1 << 20;
// DECCOLM (DECSET 3) tracks only the on/off transition: a redundant set
// or reset must skip the screen-clear / cursor-home / scroll-region-reset
// side effects the spec mandates on a real edge. The bit is parser-private
// because no embedder reacts to the column-mode flag itself; the side
// effects are the externally visible behavior.
pub(crate) const MODE_DECCOLM: u32 = 1 << 21;
// xterm DECSET 40 (Allow 80 -> 132 Mode): a master gate on whether
// DECCOLM (?3) does anything at all. When this bit is unset, both
// `?3h` and `?3l` are complete no-ops -- no MODE_DECCOLM toggle, no
// screen-clear / cursor-home / scroll-region-reset. xterm defaults
// the gate to off, so well-behaved programs that probe column-mode
// support without first opting in via `?40h` do not surprise-clear
// the user's screen. The bit is parser-private for the same reason
// `MODE_DECCOLM` is: no embedder reacts to the configuration flag,
// only to the side effects it gates.
pub(crate) const MODE_DEC_ALLOW_80_132: u32 = 1 << 22;

// Union of every DEC private mode bit that XTSAVE / XTRESTORE may snapshot
// and restore through `set_mode_with_event`. New mode bits added above must
// be ORed in here when the underlying mode is a pure on/off flag with no
// side effects beyond setting or clearing its bit. Bits that drive grid
// swapping (alternate screen) or non-bit state (origin mode, cursor save)
// are intentionally absent: bit-level restore would skip the side effects
// that DECSET / DECRST run, so they must continue to be controlled through
// their dedicated handlers. ANSI mode 4 (`MODE_INSERT`) is excluded because
// XTSAVE / XTRESTORE only handle DEC private modes.
pub(crate) const MODE_ALL_DEC: u32 = MODE_APPLICATION_CURSOR
    | MODE_BRACKETED_PASTE
    | MODE_HIDE_CURSOR
    | MODE_LINE_WRAP
    | MODE_MOUSE_REPORT_CLICK
    | MODE_MOUSE_REPORT_CELL_MOTION
    | MODE_MOUSE_REPORT_ALL_MOTION
    | MODE_FOCUS_IN_OUT
    | MODE_SGR_MOUSE
    | MODE_SYNC_UPDATE
    | MODE_COLOR_SCHEME_UPDATES
    | MODE_IN_BAND_RESIZE
    | MODE_SGR_PIXEL_MOUSE
    | MODE_GRAPHEME_CLUSTER
    | MODE_REVERSE_VIDEO
    | MODE_ALTERNATE_SCROLL
    | MODE_MOUSE_X10
    | MODE_BACKSPACE_BS;

/// Externally meaningful terminal modes that the embedding application
/// may need to react to.
///
/// Pass any variant to [`Screen::mode`](super::Screen::mode) to query its
/// current state.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
#[non_exhaustive]
pub enum TerminalMode {
    /// Application cursor-key mode ([DECCKM, DECSET 1][xterm-ctlseqs]).
    ///
    /// [xterm-ctlseqs]: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html
    #[doc(alias = "DECSET 1")]
    ApplicationCursor,
    /// Bracketed paste mode ([DECSET 2004][xterm-ctlseqs]).
    ///
    /// [xterm-ctlseqs]: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html
    #[doc(alias = "DECSET 2004")]
    BracketedPaste,
    /// Mouse button press/release reporting.
    MouseReportClick,
    /// Mouse cell-motion reporting while a button is pressed.
    MouseReportCellMotion,
    /// Mouse all-motion reporting.
    MouseReportAllMotion,
    /// Focus in/out reporting ([DECSET 1004][xterm-ctlseqs]).
    ///
    /// [xterm-ctlseqs]: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html
    #[doc(alias = "DECSET 1004")]
    FocusInOut,
    /// SGR mouse coordinate encoding.
    SgrMouse,
    /// SGR pixel-coordinate mouse encoding.
    SgrPixelMouse,
    /// Synchronized output mode ([DECSET 2026][xterm-ctlseqs]).
    ///
    /// [xterm-ctlseqs]: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html
    #[doc(alias = "DECSET 2026")]
    SyncUpdate,
    /// Grapheme-cluster segmentation mode ([DECSET 2027][xterm-ctlseqs]).
    ///
    /// [xterm-ctlseqs]: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html
    #[doc(alias = "DECSET 2027")]
    GraphemeCluster,
    /// Color scheme update reporting.
    ColorSchemeUpdates,
    /// In-band resize reporting.
    InBandResize,
    /// Alternate screen buffer ([DECSET 1049 / 47 / 1047][xterm-ctlseqs]).
    ///
    /// [xterm-ctlseqs]: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html
    #[doc(alias = "DECSET 1049")]
    #[doc(alias = "DECSET 47")]
    #[doc(alias = "DECSET 1047")]
    AlternateScreen,
    /// Cursor visibility ([DECTCEM, DECSET 25][xterm-ctlseqs]); set when
    /// the cursor is hidden.
    ///
    /// [xterm-ctlseqs]: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html
    #[doc(alias = "DECSET 25")]
    #[doc(alias = "DECTCEM")]
    HideCursor,
    /// Global reverse-video screen mode ([DECSCNM, DECSET 5][xterm-ctlseqs]).
    ///
    /// `tastty-core` does not paint, so this bit is purely a signal:
    /// embedders that render invert their palette while it is set.
    ///
    /// [xterm-ctlseqs]: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html
    #[doc(alias = "DECSET 5")]
    #[doc(alias = "DECSCNM")]
    ReverseVideo,
    /// Alternate-scroll mode ([DECSET 1007][xterm-ctlseqs]).
    ///
    /// When set and the alternate screen is active, full-screen
    /// programs that do not request mouse reporting expect wheel
    /// events to surface as `Up` / `Down` arrow-key input so their
    /// existing key bindings drive viewport scrolling. The wire bit
    /// is captured here; `MouseEncoder` performs the translation
    /// when its sync-time conditions are met.
    ///
    /// [xterm-ctlseqs]: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html
    #[doc(alias = "DECSET 1007")]
    AlternateScroll,
    /// X10 mouse compatibility reporting ([DECSET 9][xterm-ctlseqs]).
    ///
    /// The lowest-fidelity mouse mode: button-press events only.
    /// Some legacy curses programs still enable only this bit.
    ///
    /// [xterm-ctlseqs]: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html
    #[doc(alias = "DECSET 9")]
    MouseReportX10,
    /// Backspace-sends-BS mode ([DECBKM, DECSET 67][xterm-ctlseqs]).
    ///
    /// When set, Backspace is encoded as `\x08` (BS, Ctrl-H) instead
    /// of `\x7f` (DEL); Alt+Backspace becomes `\x1b\x08` instead of
    /// `\x1b\x7f`. Programs compiled against old termcap entries
    /// with `kbs=^H` flip this bit at startup; without it they read
    /// `\x7f` as a literal escape and the backspace key has no
    /// effect on the line buffer.
    ///
    /// [xterm-ctlseqs]: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html
    #[doc(alias = "DECSET 67")]
    #[doc(alias = "DECBKM")]
    BackspaceBs,
    /// Line-feed / newline mode ([LNM, ANSI mode 20][ecma48]).
    ///
    /// When set, programs expect a received `\n` to behave as `\r\n`
    /// on the host channel and ENTER to deliver `\r\n` rather than
    /// `\r`. `tastty-core` does not translate bytes; embedders that
    /// honor LNM read this bit and adapt their input or output
    /// channels accordingly.
    ///
    /// [ecma48]: https://ecma-international.org/publications-and-standards/standards/ecma-48/
    #[doc(alias = "LNM")]
    #[doc(alias = "ANSI mode 20")]
    LineFeedNewLine,
}

impl TerminalMode {
    /// Internal mapping from a wire-mode bit to the public enum variant.
    /// Used by `set_mode_with_event` to decide whether to emit a
    /// `ScreenEvent::ModeChanged` event; only modes that go through
    /// `set_mode_with_event` (rather than the silent `set_mode`) appear
    /// here.
    pub(crate) fn from_mode_flag(flag: u32) -> Option<Self> {
        match flag {
            MODE_BRACKETED_PASTE => Some(Self::BracketedPaste),
            MODE_MOUSE_REPORT_CLICK => Some(Self::MouseReportClick),
            MODE_MOUSE_REPORT_CELL_MOTION => Some(Self::MouseReportCellMotion),
            MODE_MOUSE_REPORT_ALL_MOTION => Some(Self::MouseReportAllMotion),
            MODE_FOCUS_IN_OUT => Some(Self::FocusInOut),
            MODE_SGR_MOUSE => Some(Self::SgrMouse),
            MODE_SGR_PIXEL_MOUSE => Some(Self::SgrPixelMouse),
            MODE_COLOR_SCHEME_UPDATES => Some(Self::ColorSchemeUpdates),
            MODE_IN_BAND_RESIZE => Some(Self::InBandResize),
            MODE_REVERSE_VIDEO => Some(Self::ReverseVideo),
            MODE_ALTERNATE_SCROLL => Some(Self::AlternateScroll),
            MODE_MOUSE_X10 => Some(Self::MouseReportX10),
            MODE_BACKSPACE_BS => Some(Self::BackspaceBs),
            MODE_LNM => Some(Self::LineFeedNewLine),
            _ => None,
        }
    }

    pub(crate) fn mode_bit(self) -> u32 {
        match self {
            Self::ApplicationCursor => MODE_APPLICATION_CURSOR,
            Self::BracketedPaste => MODE_BRACKETED_PASTE,
            Self::MouseReportClick => MODE_MOUSE_REPORT_CLICK,
            Self::MouseReportCellMotion => MODE_MOUSE_REPORT_CELL_MOTION,
            Self::MouseReportAllMotion => MODE_MOUSE_REPORT_ALL_MOTION,
            Self::FocusInOut => MODE_FOCUS_IN_OUT,
            Self::SgrMouse => MODE_SGR_MOUSE,
            Self::SgrPixelMouse => MODE_SGR_PIXEL_MOUSE,
            Self::SyncUpdate => MODE_SYNC_UPDATE,
            Self::GraphemeCluster => MODE_GRAPHEME_CLUSTER,
            Self::ColorSchemeUpdates => MODE_COLOR_SCHEME_UPDATES,
            Self::InBandResize => MODE_IN_BAND_RESIZE,
            Self::AlternateScreen => MODE_ALTERNATE_SCREEN,
            Self::HideCursor => MODE_HIDE_CURSOR,
            Self::ReverseVideo => MODE_REVERSE_VIDEO,
            Self::AlternateScroll => MODE_ALTERNATE_SCROLL,
            Self::MouseReportX10 => MODE_MOUSE_X10,
            Self::BackspaceBs => MODE_BACKSPACE_BS,
            Self::LineFeedNewLine => MODE_LNM,
        }
    }
}