1#![allow(clippy::allow_attributes)]
2#![allow(clippy::uninlined_format_args)]
3#![allow(clippy::wildcard_imports)]
4
5use indigo_term::{KeyboardEnhancementFlags as KEF, *};
6use std::{
7 collections::VecDeque,
8 io::{self, Write},
9};
10
11pub struct Terminal {
12 tty: Tty,
13 reader: Reader,
14 events: VecDeque<Event>,
15}
16
17impl Terminal {
18 pub fn init() -> io::Result<Self> {
19 let mut this = Self {
20 tty: Tty::init()?,
21 reader: Reader::new(),
22 events: VecDeque::new(),
23 };
24
25 this.tty.enable_raw_mode()?;
26
27 if !this.enable_mode(IN_BAND_RESIZE_MODE)? {
28 drop(this);
29 panic!("terminal does not support in-band resize");
30 }
31 write!(this.tty, "{}", IN_BAND_RESIZE_SET)?;
32 this.tty.flush()?;
33
34 if !this.query_mode(SYNC_UPDATE_MODE)?.is_configurable() {
35 drop(this);
36 panic!("terminal does not support synchronized update");
37 }
38
39 if this.query_keyboard_flags()?.is_none() {
40 drop(this);
41 panic!("terminal does not support kitty keyboard protocol");
42 }
43 write!(
44 this.tty,
45 "{}",
46 KeyboardEnhancementFlagsPush(
49 KEF::DISAMBIGUATE | KEF::REPORT_EVENTS | KEF::REPORT_ALL_KEYS
50 )
51 )?;
52
53 Ok(this)
54 }
55
56 pub fn query_mode(&mut self, mode: u16) -> io::Result<ModeSetting> {
57 write!(self.tty, "{}{}", ModeQuery(mode), DA1_QUERY)?;
58 self.tty.flush()?;
59
60 let mut setting = ModeSetting::NotRecognized;
61
62 loop {
63 match self.reader.read_event(&mut self.tty)? {
66 Event::Decrpm {
67 mode: event_mode,
68 setting: event_setting,
69 } if mode == event_mode => setting = event_setting,
70 Event::Da1 => return Ok(setting),
71 event => self.events.push_back(event),
72 }
73 }
74 }
75
76 pub fn enable_mode(&mut self, mode: u16) -> io::Result<bool> {
77 #[allow(clippy::enum_glob_use)]
78 use ModeSetting::*;
79
80 match self.query_mode(mode)? {
81 NotRecognized | PermanentlyReset => Ok(false),
82 Set | PermanentlySet => Ok(true),
83 Reset => {
84 write!(self.tty, "{}", ModeSet(mode))?;
85 self.tty.flush()?;
86 Ok(true)
87 }
88 }
89 }
90
91 pub fn disable_mode(&mut self, mode: u16) -> io::Result<bool> {
92 #[allow(clippy::enum_glob_use)]
93 use ModeSetting::*;
94
95 match self.query_mode(mode)? {
96 NotRecognized | Reset | PermanentlyReset => Ok(true),
97 PermanentlySet => Ok(false),
98 Set => {
99 write!(self.tty, "{}", ModeReset(mode))?;
100 self.tty.flush()?;
101 Ok(true)
102 }
103 }
104 }
105
106 pub fn query_keyboard_flags(&mut self) -> io::Result<Option<KeyboardEnhancementFlags>> {
107 write!(
108 self.tty,
109 "{}{}",
110 KEYBOARD_ENHANCEMENT_FLAGS_QUERY, DA1_QUERY
111 )?;
112 self.tty.flush()?;
113
114 let mut flags = None;
115
116 loop {
117 match self.reader.read_event(&mut self.tty)? {
120 Event::KeyboardEnhancementFlags(event_flags) => flags = Some(event_flags),
121 Event::Da1 => return Ok(flags),
122 event => self.events.push_back(event),
123 }
124 }
125 }
126
127 pub fn push_keyboard_flags(&mut self, flags: KeyboardEnhancementFlags) -> io::Result<()> {
128 write!(self.tty, "{}", KeyboardEnhancementFlagsPush(flags))?;
129 self.tty.flush()?;
130 Ok(())
131 }
132
133 pub fn pop_keyboard_flags(&mut self) -> io::Result<()> {
134 write!(self.tty, "{}", KeyboardEnhancementFlagsPop)?;
135 self.tty.flush()?;
136 Ok(())
137 }
138
139 pub fn begin_sync_update(&mut self) -> io::Result<()> {
140 write!(self.tty, "{}", SYNC_UPDATE_SET)?;
141 self.tty.flush()?;
142 Ok(())
143 }
144
145 pub fn end_sync_update(&mut self) -> io::Result<()> {
146 write!(self.tty, "{}", SYNC_UPDATE_RESET)?;
147 self.tty.flush()?;
148 Ok(())
149 }
150
151 pub fn size(&self) -> io::Result<(u16, u16)> {
152 self.tty.size()
153 }
154
155 pub fn read_event(&mut self) -> io::Result<Event> {
156 if let Some(event) = self.events.pop_front() {
157 Ok(event)
158 } else {
159 self.reader.read_event(&mut self.tty)
160 }
161 }
162}
163
164impl Write for Terminal {
165 #[inline]
166 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
167 self.tty.write(buf)
168 }
169
170 #[inline]
171 fn flush(&mut self) -> io::Result<()> {
172 self.tty.flush()
173 }
174}
175
176impl Drop for Terminal {
177 fn drop(&mut self) {
178 let _ = write!(self.tty, "{}", SYNC_UPDATE_RESET);
179 let _ = write!(self.tty, "{}", IN_BAND_RESIZE_RESET);
180 let _ = write!(self.tty, "{}", KeyboardEnhancementFlagsPop);
181 let _ = self.tty.flush();
182 }
183}