detect_features/
detect-features.rs

1use std::{
2    io::{self, Write as _},
3    time::Duration,
4};
5
6use termina::{
7    escape::{
8        csi::{self, Csi},
9        dcs::{self, Dcs},
10    },
11    style::RgbColor,
12    Event, PlatformTerminal, Terminal,
13};
14
15const TEST_COLOR: RgbColor = RgbColor::new(150, 150, 150);
16
17#[derive(Debug, Default, Clone, Copy)]
18struct Features {
19    kitty_keyboard: bool,
20    sychronized_output: bool,
21    true_color: bool,
22    extended_underlines: bool,
23}
24
25fn main() -> io::Result<()> {
26    let mut terminal = PlatformTerminal::new()?;
27    terminal.enter_raw_mode()?;
28
29    write!(
30        terminal,
31        "{}{}{}{}{}{}{}",
32        // Kitty keyboard
33        Csi::Keyboard(csi::Keyboard::QueryFlags),
34        // Synchronized output
35        Csi::Mode(csi::Mode::QueryDecPrivateMode(csi::DecPrivateMode::Code(
36            csi::DecPrivateModeCode::SynchronizedOutput
37        ))),
38        // True color and while we're at it, extended underlines:
39        // <https://github.com/termstandard/colors?tab=readme-ov-file#querying-the-terminal>
40        Csi::Sgr(csi::Sgr::Background(TEST_COLOR.into())),
41        Csi::Sgr(csi::Sgr::UnderlineColor(TEST_COLOR.into())),
42        Dcs::Request(dcs::DcsRequest::GraphicRendition),
43        Csi::Sgr(csi::Sgr::Reset),
44        // Finally request the primary device attributes
45        Csi::Device(csi::Device::RequestPrimaryDeviceAttributes),
46    )?;
47    terminal.flush()?;
48
49    let mut features = Features::default();
50    loop {
51        if !terminal.poll(Event::is_escape, Some(Duration::from_millis(100)))? {
52            eprintln!("Did not receive any responses to queries in 100ms\r");
53            break;
54        }
55
56        match terminal.read(Event::is_escape)? {
57            Event::Csi(Csi::Keyboard(csi::Keyboard::ReportFlags(_))) => {
58                features.kitty_keyboard = true
59            }
60            Event::Csi(Csi::Mode(csi::Mode::ReportDecPrivateMode {
61                mode: csi::DecPrivateMode::Code(csi::DecPrivateModeCode::SynchronizedOutput),
62                setting,
63            })) => {
64                features.sychronized_output = matches!(
65                    setting,
66                    csi::DecModeSetting::Set | csi::DecModeSetting::Reset
67                );
68            }
69            Event::Dcs(Dcs::Response {
70                value: dcs::DcsResponse::GraphicRendition(sgrs),
71                ..
72            }) => {
73                features.true_color = sgrs.contains(&csi::Sgr::Background(TEST_COLOR.into()));
74                features.extended_underlines =
75                    sgrs.contains(&csi::Sgr::UnderlineColor(TEST_COLOR.into()));
76            }
77            Event::Csi(Csi::Device(csi::Device::DeviceAttributes(_))) => break,
78            other => eprintln!("unexpected event: {other:?}\r"),
79        }
80    }
81    println!("Detected features: {features:?}");
82
83    Ok(())
84}