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#[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 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 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 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 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 ]
116 }
117 });
118
119 self.send_message(message.to_string())
120 .then(|| Ok(()))
121 .unwrap_or_else(|| Err(InputFlowError::SendError))?;
122 Ok(())
125 }
126
127 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 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
173impl Drop for InputFlowQmp {
175 fn drop(&mut self) {
176 if self.connected {
177 self.disconnect();
178 }
179 }
180}
181
182#[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
206impl MouseWriter for InputFlowQmp {
208 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 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 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 fn clear_buttons(&mut self) -> Result<()> {
237 if !self.connected {
238 return Err(InputFlowError::Uninitialized);
239 }
240
241 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 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
268cglue_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 .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};