inputflow_qmp/
lib.rs

1use std::io::Write;
2use std::net::{TcpStream, SocketAddr};
3use std::str;
4use serde_json::json;
5use inputflow::prelude::*;
6use log::error;
7
8mod args;
9
10/// The plugin that handles the input flow functionality.
11#[derive(Default)]
12struct InputFlowQmp {
13    stream: Option<TcpStream>,
14    connected: bool,
15}
16
17impl Loadable for InputFlowQmp {
18    fn name(&self) -> abi_stable::std_types::RString {
19        "inputflow_qmp".into()
20    }
21
22    fn capabilities(&self) -> u8 {
23        IF_PLUGIN_HEAD.features.bits()
24    }
25}
26
27impl InputFlowQmp {
28    /// Connect to the specified address and port.
29    fn connect(&mut self, address: &str, port: u32) -> bool {
30        if self.connected {
31            println!("Connection is already open");
32            return true;
33        }
34
35        let socket_address: SocketAddr = format!("{}:{}", address, port)
36            .parse()
37            .expect("Invalid address format");
38
39        match TcpStream::connect(socket_address) {
40            Ok(stream) => {
41                self.stream = Some(stream);
42                self.connected = true;
43                println!("Connected to {}", address);
44                true
45            }
46            Err(e) => {
47                println!("Could not connect to socket: {}", e);
48                false
49            }
50        }
51    }
52
53    /// Disconnect from the socket.
54    fn disconnect(&mut self) {
55        if let Some(stream) = self.stream.take() {
56            stream.shutdown(std::net::Shutdown::Both).expect("Shutdown failed");
57        }
58        self.connected = false;
59    }
60
61    /// Enable QMP (QEMU Machine Protocol) capabilities.
62    fn enable_commands(&mut self) -> bool {
63        if !self.connected {
64            return false;
65        }
66
67        let message = json!({
68            "execute": "qmp_capabilities"
69        });
70
71        self.send_message(message.to_string())
72    }
73
74    /// Send a button up or down event
75    fn send_button(&mut self, button: MouseButton, down: bool) -> Result<()> {
76        if !self.connected {
77            return Err(InputFlowError::Uninitialized);
78        }
79
80        let button_type = match button {
81            MouseButton::Left => "left",
82            MouseButton::Right => "right",
83            MouseButton::Middle => "middle",
84            MouseButton::XButton1 => "side",
85            MouseButton::XButton2 => "extra",
86            _ => return Err(InputFlowError::InvalidKey),
87        };
88
89        let message = json!({
90            "execute": "input-send-event",
91            "arguments": {
92                "events": [
93                    {
94                        "type": "btn",
95                        "data": {
96                            "button": button_type,
97                            "down": down
98                        }
99                    },
100                    // This is for testing if qmp needs a mouse move event to make clicks happen
101                    // {
102                    //     "type": "rel",
103                    //     "data": {
104                    //         "axis": "x",
105                    //         "value": 0
106                    //     }
107                    // },
108                    // {
109                    //     "type": "rel",
110                    //     "data": {
111                    //         "axis": "y",
112                    //         "value": 0
113                    //     }
114                    // }
115                ]
116            }
117        });
118
119        self.send_message(message.to_string())
120            .then(|| Ok(()))
121            .unwrap_or_else(|| Err(InputFlowError::SendError))?;
122        // This is for testing if qmp needs a mouse move event to make clicks happen
123        // self.move_mouse(0, 0);
124        Ok(())
125    }
126
127    /// Move the mouse by delta_x and delta_y.
128    fn move_mouse(&mut self, delta_x: i32, delta_y: i32) -> bool {
129        if !self.connected {
130            return false;
131        }
132
133        let message = json!({
134            "execute": "input-send-event",
135            "arguments": {
136                "events": [
137                    {
138                        "type": "rel",
139                        "data": {
140                            "axis": "x",
141                            "value": delta_x
142                        }
143                    },
144                    {
145                        "type": "rel",
146                        "data": {
147                            "axis": "y",
148                            "value": delta_y
149                        }
150                    }
151                ]
152            }
153        });
154
155        self.send_message(message.to_string())
156    }
157
158    /// Send a message to the connected socket.
159    fn send_message(&mut self, message: String) -> bool {
160        if let Some(stream) = &mut self.stream {
161            if let Err(e) = stream.write_all(message.as_bytes()) {
162                println!("Failed to send message: {}", e);
163                return false;
164            }
165            true
166        } else {
167            println!("No active connection.");
168            false
169        }
170    }
171}
172
173// auto disconnect on drop so the user doesn't have to
174impl Drop for InputFlowQmp {
175    fn drop(&mut self) {
176        if self.connected {
177            self.disconnect();
178        }
179    }
180}
181
182// Define a `PluginInner` to work with the InputFlow plugin framework.
183#[derive(Default)]
184struct NativePluginRoot {
185    controller: InputFlowQmp,
186}
187
188impl<'a> PluginInner<'a> for NativePluginRoot {
189    type BorrowedType = Fwd<&'a mut InputFlowQmp>;
190    type OwnedType = InputFlowQmp;
191    type OwnedTypeMut = InputFlowQmp;
192
193    fn borrow_features(&'a mut self) -> Self::BorrowedType {
194        self.controller.forward_mut()
195    }
196
197    fn into_features(self) -> Self::OwnedType {
198        self.controller
199    }
200
201    fn mut_features(&'a mut self) -> &'a mut Self::OwnedTypeMut {
202        &mut self.controller
203    }
204}
205
206// Implement the mouse functionality for the InputFlow plugin.
207impl MouseWriter for InputFlowQmp {
208    /// Sends a mouse button press down event.
209    fn send_button_down(&mut self, button: MouseButton) -> Result<()> {
210        if !self.connected {
211            return Err(InputFlowError::Uninitialized);
212        }
213
214        return self.send_button(button, true);
215    }
216
217    /// Releases a mouse button that was set to down previously.
218    fn send_button_up(&mut self, button: MouseButton) -> Result<()> {
219        if !self.connected {
220            return Err(InputFlowError::Uninitialized);
221        }
222
223        return self.send_button(button, false);
224    }
225
226    /// Presses a mouse button and lets it go all in one for when users do not care about specific timings.
227    /// WARNING: this blocks for one milisecond and also is very detectable by anti input automation systems.
228    /// It is much more recomended to press and release manually with varied delays
229    fn click_button(&mut self, button: MouseButton) -> Result<()> {
230        self.send_button_down(button)?;
231        std::thread::sleep(std::time::Duration::from_millis(1));
232        self.send_button_up(button)
233    }
234
235    /// Clears all active pressed mouse buttons. Ensures that mouse writer is set back into a neutral state.
236    fn clear_buttons(&mut self) -> Result<()> {
237        if !self.connected {
238            return Err(InputFlowError::Uninitialized);
239        }
240
241        // Send an "up" event for each button (left, right, middle, etc.)
242        let buttons = vec![
243            MouseButton::Left,
244            MouseButton::Right,
245            MouseButton::Middle,
246            MouseButton::XButton1,
247            MouseButton::XButton2,
248        ];
249
250        for button in buttons {
251            self.send_button_up(button)?;
252        }
253
254        Ok(())
255    }
256
257    /// Sends a mouse move command to move it by `x` dpi-pixels horizontally, and `y` vertically.
258    fn mouse_move_relative(&mut self, x: i32, y: i32) -> Result<()> {
259        if !self.connected {
260            return Err(InputFlowError::Uninitialized);
261        }
262
263        self.move_mouse(x, y);
264        Ok(())
265    }
266}
267
268// Plugin initialization and interface.
269cglue_impl_group!(InputFlowQmp, ControllerFeatures, {MouseWriter}, {MouseWriter});
270
271#[allow(improper_ctypes_definitions)]
272extern "C" fn create_plugin(lib: &CArc<cglue::trait_group::c_void>, args: *const std::ffi::c_char) -> Result<PluginInnerArcBox<'static>> {
273    env_logger::builder()
274    // .filter_level(log::LevelFilter::Info)
275    .init();
276
277    let args = args::parse_args(args).map_err(|e| {
278        log::error!("Invalid parameters were passed to inputflow_qmp: {e:?}.");
279        InputFlowError::Parameter
280    })?;
281
282
283    let mut new_plugin = NativePluginRoot::default();
284    if !new_plugin.controller.connect(args.address.as_str(), args.port) {
285        error!("Failed to connect to qmp at {}:{}", args.address, args.port);
286        return Err(InputFlowError::Loading);
287    }
288
289    if !new_plugin.controller.enable_commands() {
290        return Err(InputFlowError::Loading);
291    }
292    
293    Ok(trait_obj!((new_plugin, lib.clone()) as PluginInner))
294}
295
296#[no_mangle]
297pub static IF_PLUGIN_HEAD: PluginHeader = PluginHeader {
298    features: FeatureSupport::from_bits_retain(
299        FeatureSupport::WRITE_MOUSE.bits(),
300    ),
301    layout: ROOT_LAYOUT,
302    create: create_plugin,
303};