1
2extern crate enigo;
3
4use enigo::{
5 Direction::{Click, Press, Release},
6 Enigo, Key, Keyboard, Settings,
7};
8
9#[derive(Debug, Clone, PartialEq)]
10pub struct ParseKeyError(pub String);
11
12impl std::fmt::Display for ParseKeyError {
13 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
14 write!(f, "ParseKeyError: {}", self.0)
15 }
16}
17
18impl std::error::Error for ParseKeyError {}
19
20pub fn simulate_key(key_combination: &str) -> Result<(), ParseKeyError> {
62 let mut enigo = Enigo::new(&Settings::default())
63 .map_err(|e| ParseKeyError(format!("Failed to create Enigo instance: {}", e)))?;
64
65 let parts: Vec<String> = key_combination
66 .split('+')
67 .map(|s| s.trim().to_lowercase())
68 .collect();
69
70 if parts.is_empty() {
71 return Err(ParseKeyError("Empty key combination".to_string()));
72 }
73
74 let key = parts.last().unwrap();
76 let modifiers: Vec<&str> = parts[..parts.len() - 1]
78 .iter()
79 .map(|s| s.as_str())
80 .collect();
81
82 for modifier in &modifiers {
84 let key = parse_modifier(modifier)?;
85 let _ = enigo.key(key, Press);
86 }
87
88 let main_key = parse_main_key(key)?;
90 let _ = enigo.key(main_key, Click);
91
92 for modifier in modifiers.iter().rev() {
94 let key = parse_modifier(modifier)?;
95 let _ = enigo.key(key, Release);
96 }
97
98 Ok(())
99}
100
101fn parse_modifier(modifier: &str) -> Result<Key, ParseKeyError> {
103 match modifier {
104 "ctrl" | "control" => Ok(Key::Control),
105 "shift" => Ok(Key::Shift),
106 "alt" => Ok(Key::Alt),
107 "meta" | "win" | "cmd" | "command" => Ok(Key::Meta),
108 _ => Err(ParseKeyError(format!("Unknown modifier: {}", modifier))),
109 }
110}
111
112
113fn parse_main_key(key: &str) -> Result<Key, ParseKeyError> {
114 match key.len() {
115 1 => Ok(Key::Unicode(key.chars().next().unwrap())),
116 _ => match key.to_lowercase().as_str() {
117 "enter" | "return" => Ok(Key::Return),
119 "tab" => Ok(Key::Tab),
120 "space" => Ok(Key::Space),
121 "backspace" => Ok(Key::Backspace),
122 "delete" | "del" => Ok(Key::Delete),
123 "insert" | "ins" => Ok(Key::Insert),
124 "escape" | "esc" => Ok(Key::Escape),
125
126 "home" => Ok(Key::Home),
128 "end" => Ok(Key::End),
129 "pageup" | "pgup" => Ok(Key::PageUp),
130 "pagedown" | "pgdn" => Ok(Key::PageDown),
131
132 "left" | "leftarrow" => Ok(Key::LeftArrow),
134 "right" | "rightarrow" => Ok(Key::RightArrow),
135 "up" | "uparrow" => Ok(Key::UpArrow),
136 "down" | "downarrow" => Ok(Key::DownArrow),
137
138 "f1" => Ok(Key::F1),
140 "f2" => Ok(Key::F2),
141 "f3" => Ok(Key::F3),
142 "f4" => Ok(Key::F4),
143 "f5" => Ok(Key::F5),
144 "f6" => Ok(Key::F6),
145 "f7" => Ok(Key::F7),
146 "f8" => Ok(Key::F8),
147 "f9" => Ok(Key::F9),
148 "f10" => Ok(Key::F10),
149 "f11" => Ok(Key::F11),
150 "f12" => Ok(Key::F12),
151 "f13" => Ok(Key::F13),
152 "f14" => Ok(Key::F14),
153 "f15" => Ok(Key::F15),
154 "f16" => Ok(Key::F16),
155 "f17" => Ok(Key::F17),
156 "f18" => Ok(Key::F18),
157 "f19" => Ok(Key::F19),
158 "f20" => Ok(Key::F20),
159 "f21" => Ok(Key::F21),
160 "f22" => Ok(Key::F22),
161 "f23" => Ok(Key::F23),
162 "f24" => Ok(Key::F24),
163 "f25" => Ok(Key::F25),
164 "f26" => Ok(Key::F26),
165 "f27" => Ok(Key::F27),
166 "f28" => Ok(Key::F28),
167 "f29" => Ok(Key::F29),
168 "f30" => Ok(Key::F30),
169 "f31" => Ok(Key::F31),
170 "f32" => Ok(Key::F32),
171 "f33" => Ok(Key::F33),
172 "f34" => Ok(Key::F34),
173 "f35" => Ok(Key::F35),
174
175 "capslock" | "caps" => Ok(Key::CapsLock),
177 "numlock" | "num" => Ok(Key::Numlock),
178 "scrolllock" | "scroll" => Ok(Key::ScrollLock),
179
180 "printscreen" | "prtsc" => Ok(Key::PrintScr),
182 "pause" => Ok(Key::Pause),
183
184 "volumeup" | "volup" => Ok(Key::VolumeUp),
186 "volumedown" | "voldown" => Ok(Key::VolumeDown),
187 "volumemute" | "mute" => Ok(Key::VolumeMute),
188 "mediaplay" | "play" => Ok(Key::MediaPlayPause),
189 "mediastop" | "stop" => Ok(Key::MediaStop),
190 "medianext" | "next" => Ok(Key::MediaNextTrack),
191 "mediaprev" | "prev" => Ok(Key::MediaPrevTrack),
192
193 "numpad0" => Ok(Key::Numpad0),
195 "numpad1" => Ok(Key::Numpad1),
196 "numpad2" => Ok(Key::Numpad2),
197 "numpad3" => Ok(Key::Numpad3),
198 "numpad4" => Ok(Key::Numpad4),
199 "numpad5" => Ok(Key::Numpad5),
200 "numpad6" => Ok(Key::Numpad6),
201 "numpad7" => Ok(Key::Numpad7),
202 "numpad8" => Ok(Key::Numpad8),
203 "numpad9" => Ok(Key::Numpad9),
204
205 "comma" => Ok(Key::Unicode(',')),
207 "period" => Ok(Key::Unicode('.')),
208 "semicolon" => Ok(Key::Unicode(';')),
209 "quote" => Ok(Key::Unicode('\'')),
210 "bracketleft" => Ok(Key::Unicode('[')),
211 "bracketright" => Ok(Key::Unicode(']')),
212 "backslash" => Ok(Key::Unicode('\\')),
213 "slash" => Ok(Key::Unicode('/')),
214 "equal" => Ok(Key::Unicode('=')),
215 "minus" => Ok(Key::Unicode('-')),
216 "grave" => Ok(Key::Unicode('`')),
217
218 _ => Err(ParseKeyError(format!("Unknown key: {}", key))),
219 }
220 }
221}
222
223
224
225pub fn simulate_key_hold(key_combination: &str, duration_ms: u64) -> Result<(), ParseKeyError> {
242 let mut enigo = Enigo::new(&Settings::default())
243 .map_err(|e| ParseKeyError(format!("Failed to create Enigo instance: {}", e)))?;
244
245 let parts: Vec<String> = key_combination
246 .split('+')
247 .map(|s| s.trim().to_lowercase())
248 .collect();
249
250 if parts.is_empty() {
251 return Err(ParseKeyError("Empty key combination".to_string()));
252 }
253
254 let key = parts.last().unwrap();
255 let modifiers: Vec<&str> = parts[..parts.len() - 1]
256 .iter()
257 .map(|s| s.as_str())
258 .collect();
259
260 for modifier in &modifiers {
262 let key = parse_modifier(modifier)?;
263 let _ = enigo.key(key, Press);
264 }
265
266 let main_key = parse_main_key(key)?;
268 let _ = enigo.key(main_key, Press);
269
270 std::thread::sleep(std::time::Duration::from_millis(duration_ms));
272
273 let _ = enigo.key(main_key, Release);
275
276 for modifier in modifiers.iter().rev() {
278 let key = parse_modifier(modifier)?;
279 let _ = enigo.key(key, Release);
280 }
281
282 Ok(())
283}
284
285pub fn get_supported_keys() -> Vec<&'static str> {
287 vec![
288 "ctrl", "control", "shift", "alt", "meta", "win", "cmd", "command",
290
291 "enter", "return", "tab", "space", "backspace", "delete", "del", "escape", "esc",
293
294 "home", "end", "pageup", "pgup", "pagedown", "pgdn",
296
297 "left", "right", "up", "down",
299
300 "f1", "f2", "f3", "f4", "f5", "f6", "f7", "f8", "f9", "f10", "f11", "f12",
302 "f13", "f14", "f15", "f16", "f17", "f18", "f19", "f20", "f21", "f22", "f23", "f24",
303
304 "capslock", "caps", "numlock", "num", "scrolllock", "scroll",
306
307 "pause",
309
310 "volumeup", "volup", "volumedown", "voldown", "volumemute", "mute", "mediastop", "stop",
312 ]
313}
314
315#[cfg(test)]
316mod tests {
317 use super::*;
318
319 #[test]
320 fn test_parse_modifier() {
321 assert!(parse_modifier("ctrl").is_ok());
322 assert!(parse_modifier("shift").is_ok());
323 assert!(parse_modifier("alt").is_ok());
324 assert!(parse_modifier("meta").is_ok());
325 assert!(parse_modifier("invalid").is_err());
326 }
327
328 #[test]
329 fn test_parse_main_key() {
330 assert!(parse_main_key("a").is_ok());
331 assert!(parse_main_key("enter").is_ok());
332 assert!(parse_main_key("f1").is_ok());
333 assert!(parse_main_key("invalid_key_name").is_err());
334 }
335
336 #[test]
337 fn test_simulate_key_parsing() {
338 let result = simulate_key("ctrl+c");
343 let result = simulate_key("invalid+key");
346 assert!(result.is_err());
347 }
348}