1use {
2 crate::*,
3 crossterm::{
4 event::{
5 KeyCode,
6 KeyEvent,
7 KeyboardEnhancementFlags,
8 KeyEventKind,
9 KeyModifiers,
10 ModifierKeyCode,
11 PopKeyboardEnhancementFlags,
12 PushKeyboardEnhancementFlags,
13 },
14 execute,
15 terminal,
16 },
17 std::{
18 io,
19 ops::Drop,
20 },
21};
22
23const MAX_PRESS_COUNT: usize = 3;
27
28#[derive(Debug)]
32pub struct Combiner {
33 combining: bool,
34 keyboard_enhancement_flags_pushed: bool,
35 keyboard_enhancement_flags_externally_managed: bool,
36 mandate_modifier_for_multiple_keys: bool,
37 down_keys: Vec<KeyEvent>,
38 shift_pressed: bool,
39}
40
41impl Default for Combiner {
42 fn default() -> Self {
43 Self {
44 combining: false,
45 keyboard_enhancement_flags_pushed: false,
46 keyboard_enhancement_flags_externally_managed: false,
47 mandate_modifier_for_multiple_keys: true,
48 down_keys: Vec::new(),
49 shift_pressed: false,
50 }
51 }
52}
53
54impl Combiner {
55 pub fn enable_combining(&mut self) -> io::Result<bool> {
65 if self.combining {
66 return Ok(true);
67 }
68 if !self.keyboard_enhancement_flags_externally_managed {
69 if self.keyboard_enhancement_flags_pushed {
70 return Ok(self.combining);
71 }
72 if !terminal::supports_keyboard_enhancement()? {
73 return Ok(false);
74 }
75 push_keyboard_enhancement_flags()?;
76 self.keyboard_enhancement_flags_pushed = true;
77 }
78 self.combining = true;
79 Ok(true)
80 }
81 pub fn disable_combining(&mut self) -> io::Result<()> {
83 if !self.keyboard_enhancement_flags_externally_managed && self.keyboard_enhancement_flags_pushed {
84 pop_keyboard_enhancement_flags()?;
85 self.keyboard_enhancement_flags_pushed = false;
86 }
87 self.combining = false;
88 Ok(())
89 }
90 pub fn set_keyboard_enhancement_flags_externally_managed(&mut self) {
95 self.keyboard_enhancement_flags_externally_managed = true;
96 }
97 pub fn is_combining(&self) -> bool {
98 self.combining
99 }
100 pub fn set_mandate_modifier_for_multiple_keys(&mut self, mandate: bool) {
109 self.mandate_modifier_for_multiple_keys = mandate;
110 }
111 fn combine(&mut self, clear: bool) -> Option<KeyCombination> {
113 let mut key_combination = KeyCombination::try_from(self.down_keys.as_slice())
114 .ok(); if self.shift_pressed {
116 if let Some(ref mut key_combination) = key_combination {
117 key_combination.modifiers |= KeyModifiers::SHIFT;
118 }
119 }
120 if clear {
121 self.down_keys.clear();
122 self.shift_pressed = false;
123 }
124 key_combination
125 }
126 pub fn transform(&mut self, key: KeyEvent) -> Option<KeyCombination> {
131 if self.combining {
132 self.transform_combining(key)
133 } else {
134 self.transform_ansi(key)
135 }
136 }
137 fn transform_combining(&mut self, key: KeyEvent) -> Option<KeyCombination> {
138 if let KeyCode::Modifier(modifier) = key.code {
139 if modifier == ModifierKeyCode::LeftShift || modifier == ModifierKeyCode::RightShift {
140 self.shift_pressed = key.kind != KeyEventKind::Release;
141 }
142 return None;
145 }
146 if
147 self.mandate_modifier_for_multiple_keys
148 && is_key_simple(key)
149 && !self.shift_pressed
150 && self.down_keys.is_empty()
151 {
152 match key.kind {
154 KeyEventKind::Press | KeyEventKind::Repeat => {
155 self.down_keys.push(key);
156 self.combine(true)
157 }
158 KeyEventKind::Release => {
159 None
160 }
161 }
162 } else {
163 match key.kind {
165 KeyEventKind::Press => {
166 self.down_keys.push(key);
167 if self.down_keys.len() == MAX_PRESS_COUNT {
168 self.combine(true)
169 } else {
170 None
171 }
172 }
173 KeyEventKind::Release => {
174 self.combine(true)
176 }
177 KeyEventKind::Repeat => {
178 self.combine(false)
179 }
180 }
181 }
182 }
183 fn transform_ansi(&mut self, key: KeyEvent) -> Option<KeyCombination> {
186 match key.kind {
187 KeyEventKind::Press => Some(key.into()),
188 _ => {
189 None
192 }
193 }
194 }
195}
196
197pub fn is_key_simple(key: KeyEvent) -> bool {
200 key.modifiers.is_empty()
201 && key.code != KeyCode::Char(' ')
202}
203
204impl Drop for Combiner {
205 fn drop(&mut self) {
206 if self.keyboard_enhancement_flags_pushed {
207 let _ = pop_keyboard_enhancement_flags();
208 }
209 }
210}
211
212pub fn push_keyboard_enhancement_flags() -> io::Result<()> {
216 let mut stdout = io::stdout();
217 execute!(
218 stdout,
219 PushKeyboardEnhancementFlags(
220 KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES
221 | KeyboardEnhancementFlags::REPORT_ALL_KEYS_AS_ESCAPE_CODES
222 | KeyboardEnhancementFlags::REPORT_ALTERNATE_KEYS
223 | KeyboardEnhancementFlags::REPORT_EVENT_TYPES
224 )
225 )
226}
227
228pub fn pop_keyboard_enhancement_flags() -> io::Result<()>{
232 let mut stdout = io::stdout();
233 execute!(stdout, PopKeyboardEnhancementFlags)
234}