Skip to main content

tauri_mcp/tools/
input.rs

1use crate::{Result, TauriMcpError};
2use enigo::{Enigo, Key, Settings, Button, Coordinate, Direction::Click, Keyboard, Mouse, Axis};
3use std::thread;
4use std::time::Duration;
5use tracing::{debug, info};
6
7pub struct InputSimulator {}
8
9impl InputSimulator {
10    pub fn new() -> Self {
11        Self {}
12    }
13    
14    pub async fn send_keyboard_input(&self, process_id: &str, keys: &str) -> Result<()> {
15        info!("Sending keyboard input to process {}: {}", process_id, keys);
16        
17        let keys_to_send = keys.to_string();
18        let settings = Settings::default();
19        
20        tokio::task::spawn_blocking(move || {
21            let mut enigo = Enigo::new(&settings)
22                .map_err(|e| TauriMcpError::InputError(format!("Failed to create Enigo: {:?}", e)))?;
23            
24            if keys_to_send.starts_with("cmd+") || keys_to_send.starts_with("ctrl+") {
25                Self::send_key_combination(&mut enigo, &keys_to_send)?;
26            } else {
27                for ch in keys_to_send.chars() {
28                    enigo.text(&ch.to_string())
29                        .map_err(|e| TauriMcpError::InputError(format!("Failed to send text: {:?}", e)))?;
30                    thread::sleep(Duration::from_millis(10));
31                }
32            }
33            
34            Ok::<(), TauriMcpError>(())
35        })
36        .await
37        .map_err(|e| TauriMcpError::InputError(format!("Failed to send keyboard input: {}", e)))??;
38        
39        Ok(())
40    }
41    
42    pub async fn send_mouse_click(&self, process_id: &str, x: i32, y: i32, button: &str) -> Result<()> {
43        info!("Sending mouse click to process {} at ({}, {}), button: {}", process_id, x, y, button);
44        
45        let button_to_click = match button.to_lowercase().as_str() {
46            "left" => Button::Left,
47            "right" => Button::Right,
48            "middle" => Button::Middle,
49            _ => return Err(TauriMcpError::InputError(format!("Invalid mouse button: {}", button))),
50        };
51        
52        let settings = Settings::default();
53        
54        tokio::task::spawn_blocking(move || {
55            let mut enigo = Enigo::new(&settings)
56                .map_err(|e| TauriMcpError::InputError(format!("Failed to create Enigo: {:?}", e)))?;
57            
58            enigo.move_mouse(x, y, Coordinate::Abs)
59                .map_err(|e| TauriMcpError::InputError(format!("Failed to move mouse: {:?}", e)))?;
60            thread::sleep(Duration::from_millis(50));
61            
62            enigo.button(button_to_click, Click)
63                .map_err(|e| TauriMcpError::InputError(format!("Failed to click: {:?}", e)))?;
64            
65            Ok::<(), TauriMcpError>(())
66        })
67        .await
68        .map_err(|e| TauriMcpError::InputError(format!("Failed to send mouse click: {}", e)))??;
69        
70        Ok(())
71    }
72    
73    pub async fn send_mouse_move(&self, process_id: &str, x: i32, y: i32) -> Result<()> {
74        info!("Moving mouse for process {} to ({}, {})", process_id, x, y);
75        
76        let settings = Settings::default();
77        
78        tokio::task::spawn_blocking(move || {
79            let mut enigo = Enigo::new(&settings)
80                .map_err(|e| TauriMcpError::InputError(format!("Failed to create Enigo: {:?}", e)))?;
81            enigo.move_mouse(x, y, Coordinate::Abs)
82                .map_err(|e| TauriMcpError::InputError(format!("Failed to move mouse: {:?}", e)))?;
83            Ok::<(), TauriMcpError>(())
84        })
85        .await
86        .map_err(|e| TauriMcpError::InputError(format!("Failed to move mouse: {}", e)))??;
87        
88        Ok(())
89    }
90    
91    pub async fn send_mouse_drag(&self, process_id: &str, start_x: i32, start_y: i32, end_x: i32, end_y: i32) -> Result<()> {
92        info!("Dragging mouse for process {} from ({}, {}) to ({}, {})", 
93              process_id, start_x, start_y, end_x, end_y);
94        
95        let settings = Settings::default();
96        
97        tokio::task::spawn_blocking(move || {
98            let mut enigo = Enigo::new(&settings)
99                .map_err(|e| TauriMcpError::InputError(format!("Failed to create Enigo: {:?}", e)))?;
100            
101            enigo.move_mouse(start_x, start_y, Coordinate::Abs)
102                .map_err(|e| TauriMcpError::InputError(format!("Failed to move mouse: {:?}", e)))?;
103            thread::sleep(Duration::from_millis(50));
104            
105            enigo.button(Button::Left, enigo::Direction::Press)
106                .map_err(|e| TauriMcpError::InputError(format!("Failed to press button: {:?}", e)))?;
107            thread::sleep(Duration::from_millis(50));
108            
109            let steps = 10;
110            let dx = (end_x - start_x) as f32 / steps as f32;
111            let dy = (end_y - start_y) as f32 / steps as f32;
112            
113            for i in 1..=steps {
114                let x = start_x + (dx * i as f32) as i32;
115                let y = start_y + (dy * i as f32) as i32;
116                enigo.move_mouse(x, y, Coordinate::Abs)
117                    .map_err(|e| TauriMcpError::InputError(format!("Failed to move mouse: {:?}", e)))?;
118                thread::sleep(Duration::from_millis(20));
119            }
120            
121            enigo.button(Button::Left, enigo::Direction::Release)
122                .map_err(|e| TauriMcpError::InputError(format!("Failed to release button: {:?}", e)))?;
123            
124            Ok::<(), TauriMcpError>(())
125        })
126        .await
127        .map_err(|e| TauriMcpError::InputError(format!("Failed to drag mouse: {}", e)))??;
128        
129        Ok(())
130    }
131    
132    pub async fn send_mouse_scroll(&self, process_id: &str, x: i32, y: i32, delta: i32) -> Result<()> {
133        info!("Scrolling mouse for process {} at ({}, {}), delta: {}", process_id, x, y, delta);
134        
135        let settings = Settings::default();
136        
137        tokio::task::spawn_blocking(move || {
138            let mut enigo = Enigo::new(&settings)
139                .map_err(|e| TauriMcpError::InputError(format!("Failed to create Enigo: {:?}", e)))?;
140            
141            enigo.move_mouse(x, y, Coordinate::Abs)
142                .map_err(|e| TauriMcpError::InputError(format!("Failed to move mouse: {:?}", e)))?;
143            thread::sleep(Duration::from_millis(50));
144            
145            enigo.scroll(delta, Axis::Vertical)
146                .map_err(|e| TauriMcpError::InputError(format!("Failed to scroll: {:?}", e)))?;
147            
148            Ok::<(), TauriMcpError>(())
149        })
150        .await
151        .map_err(|e| TauriMcpError::InputError(format!("Failed to scroll mouse: {}", e)))??;
152        
153        Ok(())
154    }
155    
156    fn send_key_combination(enigo: &mut Enigo, combination: &str) -> Result<()> {
157        let parts: Vec<&str> = combination.split('+').collect();
158        if parts.len() < 2 {
159            return Err(TauriMcpError::InputError(format!("Invalid key combination: {}", combination)));
160        }
161        
162        let mut modifier_keys = Vec::new();
163        let mut main_key = None;
164        
165        for (i, part) in parts.iter().enumerate() {
166            let key_str = part.trim().to_lowercase();
167            
168            if i < parts.len() - 1 {
169                match key_str.as_str() {
170                    "cmd" | "meta" => modifier_keys.push(Key::Meta),
171                    "ctrl" | "control" => modifier_keys.push(Key::Control),
172                    "alt" | "option" => modifier_keys.push(Key::Alt),
173                    "shift" => modifier_keys.push(Key::Shift),
174                    _ => return Err(TauriMcpError::InputError(format!("Unknown modifier key: {}", key_str))),
175                }
176            } else {
177                main_key = Some(Self::string_to_key(&key_str)?);
178            }
179        }
180        
181        for key in &modifier_keys {
182            enigo.key(*key, enigo::Direction::Press)
183                .map_err(|e| TauriMcpError::InputError(format!("Failed to press key: {:?}", e)))?;
184            thread::sleep(Duration::from_millis(10));
185        }
186        
187        if let Some(key) = main_key {
188            enigo.key(key, Click)
189                .map_err(|e| TauriMcpError::InputError(format!("Failed to click key: {:?}", e)))?;
190            thread::sleep(Duration::from_millis(10));
191        }
192        
193        for key in modifier_keys.iter().rev() {
194            enigo.key(*key, enigo::Direction::Release)
195                .map_err(|e| TauriMcpError::InputError(format!("Failed to release key: {:?}", e)))?;
196            thread::sleep(Duration::from_millis(10));
197        }
198        
199        Ok(())
200    }
201    
202    fn string_to_key(s: &str) -> Result<Key> {
203        match s {
204            "a" => Ok(Key::Unicode('a')),
205            "b" => Ok(Key::Unicode('b')),
206            "c" => Ok(Key::Unicode('c')),
207            "d" => Ok(Key::Unicode('d')),
208            "e" => Ok(Key::Unicode('e')),
209            "f" => Ok(Key::Unicode('f')),
210            "g" => Ok(Key::Unicode('g')),
211            "h" => Ok(Key::Unicode('h')),
212            "i" => Ok(Key::Unicode('i')),
213            "j" => Ok(Key::Unicode('j')),
214            "k" => Ok(Key::Unicode('k')),
215            "l" => Ok(Key::Unicode('l')),
216            "m" => Ok(Key::Unicode('m')),
217            "n" => Ok(Key::Unicode('n')),
218            "o" => Ok(Key::Unicode('o')),
219            "p" => Ok(Key::Unicode('p')),
220            "q" => Ok(Key::Unicode('q')),
221            "r" => Ok(Key::Unicode('r')),
222            "s" => Ok(Key::Unicode('s')),
223            "t" => Ok(Key::Unicode('t')),
224            "u" => Ok(Key::Unicode('u')),
225            "v" => Ok(Key::Unicode('v')),
226            "w" => Ok(Key::Unicode('w')),
227            "x" => Ok(Key::Unicode('x')),
228            "y" => Ok(Key::Unicode('y')),
229            "z" => Ok(Key::Unicode('z')),
230            "enter" | "return" => Ok(Key::Return),
231            "tab" => Ok(Key::Tab),
232            "space" => Ok(Key::Space),
233            "backspace" => Ok(Key::Backspace),
234            "escape" | "esc" => Ok(Key::Escape),
235            "delete" | "del" => Ok(Key::Delete),
236            "home" => Ok(Key::Home),
237            "end" => Ok(Key::End),
238            "pageup" => Ok(Key::PageUp),
239            "pagedown" => Ok(Key::PageDown),
240            "left" => Ok(Key::LeftArrow),
241            "right" => Ok(Key::RightArrow),
242            "up" => Ok(Key::UpArrow),
243            "down" => Ok(Key::DownArrow),
244            "f1" => Ok(Key::F1),
245            "f2" => Ok(Key::F2),
246            "f3" => Ok(Key::F3),
247            "f4" => Ok(Key::F4),
248            "f5" => Ok(Key::F5),
249            "f6" => Ok(Key::F6),
250            "f7" => Ok(Key::F7),
251            "f8" => Ok(Key::F8),
252            "f9" => Ok(Key::F9),
253            "f10" => Ok(Key::F10),
254            "f11" => Ok(Key::F11),
255            "f12" => Ok(Key::F12),
256            _ => Err(TauriMcpError::InputError(format!("Unknown key: {}", s))),
257        }
258    }
259}