1use enigo::{Button, Direction, Enigo, Keyboard, Mouse, Settings};
7use inputflow::prelude::*;
8
9#[derive(Default)]
10struct NativePluginRoot {
11 controller: InputFlowNative,
12}
13
14impl<'a> PluginInner<'a> for NativePluginRoot {
15 type BorrowedType = Fwd<&'a mut InputFlowNative>;
16
17 type OwnedType = InputFlowNative;
18 type OwnedTypeMut = InputFlowNative;
19
20 fn borrow_features(&'a mut self) -> Self::BorrowedType {
21 self.controller.forward_mut()
22 }
23
24 fn into_features(self) -> Self::OwnedType {
25 self.controller
26 }
27
28 fn mut_features(&'a mut self) -> &'a mut Self::OwnedTypeMut {
29 &mut self.controller
30 }
31}
32
33#[derive(Debug)]
34pub struct InputFlowNative {
35 enigo: Enigo,
36}
37
38impl Clone for InputFlowNative {
39 fn clone(&self) -> Self {
40 Self::default()
41 }
42}
43
44impl Loadable for InputFlowNative {
45 fn name(&self) -> abi_stable::std_types::RString {
46 "inputflow_native".into()
47 }
48
49 fn capabilities(&self) -> u8 {
50 IF_PLUGIN_HEAD.features.bits()
51 }
52}
53
54impl Default for InputFlowNative {
55 fn default() -> Self {
56 Self {
57 enigo: Enigo::new(&Settings {
58 release_keys_when_dropped: true,
59 ..Default::default()
60 })
61 .expect("setting up enigo"),
62 }
63 }
64}
65
66fn keycode_to_button(btn: MouseButton) -> Option<Button> {
76 match btn {
77 MouseButton::Left => Some(Button::Left),
78 MouseButton::Right => Some(Button::Right),
79 MouseButton::Middle => Some(Button::Middle),
81 MouseButton::XButton1 => Some(Button::Back),
82 MouseButton::XButton2 => Some(Button::Forward),
83 MouseButton::ScrollUp => Some(Button::ScrollUp),
84 MouseButton::ScrollDown => Some(Button::ScrollDown),
85 MouseButton::ScrollLeft => Some(Button::ScrollLeft),
86 MouseButton::ScrollRight => Some(Button::ScrollRight),
87 _ => None,
88 }
89}
90
91impl MouseWriter for InputFlowNative {
115 #[doc = r" Sends mouse button press down event"]
116 fn send_button_down(&mut self, button: MouseButton) -> Result<()> {
117 if let Some(key) = keycode_to_button(button) {
118 self.enigo
119 .button(key, Direction::Press)
120 .map_err(|_| InputFlowError::SendError)
121 } else {
122 Err(InputFlowError::InvalidKey)
123 }
124 }
125
126 #[doc = r" Releases a mouse button that was set to down previously"]
127 fn send_button_up(&mut self, button: MouseButton) -> Result<()> {
128 if let Some(button) = keycode_to_button(button) {
129 self.enigo
130 .button(button, Direction::Release)
131 .map_err(|_| InputFlowError::SendError)
132 } else {
133 Err(InputFlowError::InvalidKey)
134 }
135 }
136
137 #[doc = r" Presses a mouse button and lets it go all in one for when users do not care about specific timings"]
138 fn click_button(&mut self, button: MouseButton) -> Result<()> {
139 if let Some(button) = keycode_to_button(button) {
140 self.enigo
141 .button(button, Direction::Click)
142 .map_err(|_| InputFlowError::SendError)
143 } else {
144 Err(InputFlowError::InvalidKey)
145 }
146 }
147
148 #[doc = r" clears all active pressed mouse buttons. Useful for cleaning up multiple mouse button presses in one go."]
149 #[doc = r" Ensures that mouse writer is set back into a neutral state."]
150 fn clear_buttons(&mut self) -> Result<()> {
151 let (held_keys, held_keycodes) = self.enigo.held();
152 for key in held_keys {
153 if self.enigo.key(key, Direction::Release).is_err() {
154 println!("WARN: unable to release {key:?}");
155 };
156 }
157 for keycode in held_keycodes {
158 if self.enigo.raw(keycode, Direction::Release).is_err() {
159 println!("WARN: unable to release {keycode:?}");
160 };
161 }
162 Ok(())
163 }
164
165 #[doc = r" Sends a mouse move command to move it x dpi-pixels horizontally, and y vertically"]
166 fn mouse_move_relative(&mut self, x: i32, y: i32) -> Result<()> {
167 self.enigo
168 .move_mouse(x, y, enigo::Coordinate::Rel)
169 .map_err(|_| InputFlowError::SendError)
170 }
171}
172
173cglue_impl_group!(InputFlowNative, ControllerFeatures,{MouseWriter}, {MouseWriter} );
178#[allow(improper_ctypes_definitions)] extern "C" fn create_plugin(lib: &CArc<cglue::trait_group::c_void>, _args: *const std::ffi::c_char) -> Result<PluginInnerArcBox<'static>> {
180 Ok(trait_obj!((NativePluginRoot::default(), lib.clone()) as PluginInner))
182}
183
184#[no_mangle]
185pub static IF_PLUGIN_HEAD: PluginHeader = PluginHeader {
186 features: FeatureSupport::from_bits_retain(
187 FeatureSupport::WRITE_MOUSE.bits(),
188 ),
189 layout: ROOT_LAYOUT,
190 create: create_plugin,
191};