Skip to main content

simulate_key/
lib.rs

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
20/// Perform any key combination passed in as string
21/// 
22/// # Arguments
23/// * `key_combination` - A string in the format of a key combination
24/// 
25/// # Examples
26/// ```
27/// use simulate_key::simulate_key;
28/// 
29/// // Basic key combinations
30/// simulate_key("ctrl+c").unwrap();
31/// simulate_key("alt+tab").unwrap();
32/// simulate_key("ctrl+shift+t").unwrap();
33/// 
34/// // Function keys
35/// simulate_key("f5").unwrap();
36/// simulate_key("ctrl+f12").unwrap();
37/// 
38/// // Navigation keys
39/// simulate_key("ctrl+home").unwrap();
40/// simulate_key("shift+end").unwrap();
41/// 
42/// // Special characters and symbols
43/// simulate_key("ctrl+;").unwrap();
44/// simulate_key("alt+[").unwrap();
45/// ```
46/// 
47/// # Errors
48/// Returns `ParseKeyError` if the key combination cannot be parsed
49/// 
50/// # Supported Keys
51/// - **Modifiers**: ctrl/control, shift, alt, meta/win/cmd/command
52/// - **Function Keys**: f1-f24
53/// - **Navigation**: home, end, pageup/pgup, pagedown/pgdn, insert, delete/del
54/// - **Arrows**: left, right, up, down
55/// - **Special**: enter/return, tab, space, backspace, escape/esc, capslock, numlock, scrolllock
56/// - **Numpad**: numpad0-numpad9, numpadenter, numpadplus, numpadminus, numpadmultiply, numpaddivide, numpaddot
57/// - **Media**: volumeup, volumedown, volumemute, mediaplay, mediastop, medianext, mediaprev
58/// - **System**: printscreen/prtsc, pause, sleep, wake
59/// - **Symbols**: All standard symbols (!, @, #, $, %, etc.)
60/// - **Single Characters**: Any single character (a-z, 0-9)
61pub 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    // The last part is always the key
75    let key = parts.last().unwrap();
76    // All parts except the last one are modifiers
77    let modifiers: Vec<&str> = parts[..parts.len() - 1]
78        .iter()
79        .map(|s| s.as_str())
80        .collect();
81    
82    // Press all modifier keys
83    for modifier in &modifiers {
84        let key = parse_modifier(modifier)?;
85        let _ = enigo.key(key, Press);
86    }
87    
88    // Handle the main key
89    let main_key = parse_main_key(key)?;
90    let _ = enigo.key(main_key, Click);
91    
92    // Release all modifier keys in reverse order
93    for modifier in modifiers.iter().rev() {
94        let key = parse_modifier(modifier)?;
95        let _ = enigo.key(key, Release);
96    }
97    
98    Ok(())
99}
100
101/// Parse modifier keys
102fn 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            // Basic keys
118            "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            // Navigation
127            "home"                    => Ok(Key::Home),
128            "end"                     => Ok(Key::End),
129            "pageup" | "pgup"         => Ok(Key::PageUp),
130            "pagedown" | "pgdn"       => Ok(Key::PageDown),
131            
132            // Arrow keys
133            "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            // Function keys (F1-F35)
139            "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            // Lock keys
176            "capslock" | "caps"       => Ok(Key::CapsLock),
177            "numlock" | "num"         => Ok(Key::Numlock),
178            "scrolllock" | "scroll"   => Ok(Key::ScrollLock),
179            
180            // System keys
181            "printscreen" | "prtsc"   => Ok(Key::PrintScr),
182            "pause"                   => Ok(Key::Pause),
183            
184            // Media keys
185            "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            // Numpad keys
194            "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            // Special symbols
206            "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
225/// Simulate a key press and hold for a specified duration
226/// 
227/// # Arguments
228/// * `key_combination` - A string in the format of a key combination
229/// * `duration_ms` - Duration to hold the key in milliseconds
230/// 
231/// # Examples
232/// ```
233/// use simulate_key::simulate_key_hold;
234/// 
235/// // Hold space for 500ms
236/// simulate_key_hold("space", 500).unwrap();
237/// 
238/// // Hold Ctrl+A for 100ms
239/// simulate_key_hold("ctrl+a", 100).unwrap();
240/// ```
241pub 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    // Press all modifier keys
261    for modifier in &modifiers {
262        let key = parse_modifier(modifier)?;
263        let _ = enigo.key(key, Press);
264    }
265    
266    // Press and hold the main key
267    let main_key = parse_main_key(key)?;
268    let _ = enigo.key(main_key, Press);
269    
270    // Hold for specified duration
271    std::thread::sleep(std::time::Duration::from_millis(duration_ms));
272    
273    // Release the main key
274    let _ = enigo.key(main_key, Release);
275    
276    // Release all modifier keys in reverse order
277    for modifier in modifiers.iter().rev() {
278        let key = parse_modifier(modifier)?;
279        let _ = enigo.key(key, Release);
280    }
281    
282    Ok(())
283}
284
285/// Get a list of all supported keys
286pub fn get_supported_keys() -> Vec<&'static str> {
287    vec![
288        // Modifiers
289        "ctrl", "control", "shift", "alt", "meta", "win", "cmd", "command",
290        
291        // Basic keys
292        "enter", "return", "tab", "space", "backspace", "delete", "del", "escape", "esc",
293        
294        // Navigation
295        "home", "end", "pageup", "pgup", "pagedown", "pgdn",
296        
297        // Arrows
298        "left", "right", "up", "down",
299        
300        // Function keys
301        "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        // Lock keys
305        "capslock", "caps", "numlock", "num", "scrolllock", "scroll",
306        
307        // System
308        "pause",
309        
310        // Media (available keys)
311        "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        // These tests just verify parsing, not actual key simulation
339        // since that requires system interaction
340        
341        // Test that the function doesn't panic on valid inputs
342        let result = simulate_key("ctrl+c");
343        // We can't test the actual key press in unit tests, but we can test that parsing works
344        
345        let result = simulate_key("invalid+key");
346        assert!(result.is_err());
347    }
348}