flatland3 0.2.2

Flatland3 terminal play client and account CLI
use std::io::{stdout, Write};
use std::panic;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Once;

use crossterm::cursor::{Hide, Show};
use crossterm::event::{
    DisableMouseCapture, EnableMouseCapture, KeyboardEnhancementFlags,
    PopKeyboardEnhancementFlags, PushKeyboardEnhancementFlags,
};
use crossterm::terminal::{
    disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen,
};
use crossterm::ExecutableCommand;

static PANIC_HOOK: Once = Once::new();
static KEYBOARD_ENHANCED: AtomicBool = AtomicBool::new(false);

/// Restore terminal even on panic (best-effort).
fn install_panic_hook() {
    PANIC_HOOK.call_once(|| {
        let default = panic::take_hook();
        panic::set_hook(Box::new(move |info| {
            let _ = restore_terminal();
            default(info);
        }));
    });
}

/// Enter raw mode + alternate screen. Returns whether keyboard enhancement protocol works.
pub fn enter() -> anyhow::Result<bool> {
    install_panic_hook();
    enable_raw_mode()?;
    let mut out = stdout();
    let enhanced = matches!(
        crossterm::terminal::supports_keyboard_enhancement(),
        Ok(true)
    );
    KEYBOARD_ENHANCED.store(enhanced, Ordering::SeqCst);
    if enhanced {
        // REPORT_EVENT_TYPES gives Press/Release for multi-key chords + sprint.
        let _ = out.execute(PushKeyboardEnhancementFlags(
            KeyboardEnhancementFlags::REPORT_EVENT_TYPES
                | KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES,
        ));
    }
    out.execute(EnterAlternateScreen)?;
    let _ = out.execute(EnableMouseCapture);
    out.execute(Hide)?;
    let _ = out.flush();
    Ok(enhanced)
}

/// Always call on exit; safe to call multiple times.
///
/// Must clear the kitty keyboard protocol. If left enabled, the shell prints raw
/// CSI-u sequences like `3;5:3u` for every keypress after the client exits.
pub fn restore_terminal() -> anyhow::Result<()> {
    let mut out = stdout();

    // Pop the enhancement stack (may be a no-op if never pushed).
    let _ = out.execute(PopKeyboardEnhancementFlags);
    // Hard-reset progressive enhancement flags — belt-and-suspenders for kitty/wezterm/ghostty.
    // CSI = 0 u  → set keyboard flags to 0 (see kitty keyboard protocol).
    let _ = write!(out, "\x1b[=0u");
    KEYBOARD_ENHANCED.store(false, Ordering::SeqCst);

    let _ = out.execute(DisableMouseCapture);
    let _ = out.execute(Show);
    let _ = out.execute(LeaveAlternateScreen);
    let _ = disable_raw_mode();
    let _ = out.flush();
    Ok(())
}

/// Restores the terminal when dropped.
pub struct Guard;

impl Drop for Guard {
    fn drop(&mut self) {
        let _ = restore_terminal();
    }
}