inputbot/linux/
mod.rs

1use crate::{common::*, linux::inputs::*, public::*};
2use input::{
3    event::{
4        keyboard::{
5            KeyState, {KeyboardEvent, KeyboardEventTrait},
6        },
7        pointer::{ButtonState, PointerEvent::*},
8        Event::{self, *},
9    },
10    Libinput, LibinputInterface,
11};
12use nix::{
13    fcntl::{open, OFlag},
14    sys::stat::Mode,
15    unistd::close,
16};
17use once_cell::sync::Lazy;
18use std::{
19    mem::MaybeUninit, os::unix::io::RawFd, path::Path, ptr::null, sync::Mutex, thread::sleep,
20    time::Duration,
21};
22use uinput::event::{
23    controller::{Controller, Mouse},
24    relative::Position,
25    Event as UinputEvent,
26};
27use x11::xlib::*;
28
29mod inputs;
30
31type ButtonStatesMap = HashMap<MouseButton, bool>;
32type KeyStatesMap = HashMap<KeybdKey, bool>;
33
34static BUTTON_STATES: Lazy<Mutex<ButtonStatesMap>> =
35    Lazy::new(|| Mutex::new(ButtonStatesMap::new()));
36static KEY_STATES: Lazy<Mutex<KeyStatesMap>> = Lazy::new(|| Mutex::new(KeyStatesMap::new()));
37static SEND_DISPLAY: Lazy<AtomicPtr<Display>> = Lazy::new(|| {
38    unsafe { XInitThreads() };
39    AtomicPtr::new(unsafe { XOpenDisplay(null()) })
40});
41static FAKE_DEVICE: Lazy<Mutex<uinput::Device>> = Lazy::new(|| {
42    Mutex::new(
43        uinput::default()
44            .unwrap()
45            .name("inputbot")
46            .unwrap()
47            .event(uinput::event::Keyboard::All)
48            .unwrap()
49            .event(UinputEvent::Controller(Controller::Mouse(Mouse::Left)))
50            .unwrap()
51            .event(UinputEvent::Controller(Controller::Mouse(Mouse::Right)))
52            .unwrap()
53            .event(UinputEvent::Controller(Controller::Mouse(Mouse::Middle)))
54            .unwrap()
55            .event(UinputEvent::Controller(Controller::Mouse(Mouse::Side)))
56            .unwrap()
57            .event(UinputEvent::Controller(Controller::Mouse(Mouse::Extra)))
58            .unwrap()
59            .event(UinputEvent::Controller(Controller::Mouse(Mouse::Forward)))
60            .unwrap()
61            .event(UinputEvent::Controller(Controller::Mouse(Mouse::Back)))
62            .unwrap()
63            .event(UinputEvent::Controller(Controller::Mouse(Mouse::Task)))
64            .unwrap()
65            .event(Position::X)
66            .unwrap()
67            .event(Position::Y)
68            .unwrap()
69            .create()
70            .unwrap(),
71    )
72});
73
74/// Requests the fake device to be generated.
75///
76/// Can be called before using the fake device to prevent it from
77/// building when you first try to use it.
78pub fn init_device() {
79    FAKE_DEVICE.lock().unwrap();
80}
81
82impl KeybdKey {
83    /// Returns true if a given `KeybdKey` is currently pressed (in the down position).
84    pub fn is_pressed(self) -> bool {
85        *KEY_STATES.lock().unwrap().entry(self).or_insert(false)
86    }
87
88    /// Presses a given `KeybdKey`. Note: this means the key will remain in the down
89    /// position. You must manually call release to create a full 'press'.
90    pub fn press(self) {
91        let mut device = FAKE_DEVICE.lock().unwrap();
92
93        device.write(0x01, key_to_scan_code(self), 1).unwrap();
94        device.synchronize().unwrap();
95    }
96
97    /// Releases a given `KeybdKey`. This means the key would be in the up position.
98    pub fn release(self) {
99        let mut device = FAKE_DEVICE.lock().unwrap();
100
101        device.write(0x01, key_to_scan_code(self), 0).unwrap();
102        device.synchronize().unwrap();
103    }
104
105    /// Returns true if a keyboard key which supports toggling (ScrollLock, NumLock,
106    /// CapsLock) is on.
107    pub fn is_toggled(self) -> bool {
108        if let Some(key) = match self {
109            KeybdKey::ScrollLockKey => Some(4),
110            KeybdKey::NumLockKey => Some(2),
111            KeybdKey::CapsLockKey => Some(1),
112            _ => None,
113        } {
114            let mut state: XKeyboardState = unsafe { MaybeUninit::zeroed().assume_init() };
115            SEND_DISPLAY.with(|display| unsafe {
116                XGetKeyboardControl(display, &mut state);
117            });
118            state.led_mask & key != 0
119        } else {
120            false
121        }
122    }
123}
124
125impl MouseButton {
126    /// Returns true if a given `MouseButton` is currently pressed (in the down position).
127    pub fn is_pressed(self) -> bool {
128        *BUTTON_STATES.lock().unwrap().entry(self).or_insert(false)
129    }
130
131    /// Presses a given `MouseButton`. Note: this means the button will remain in the down
132    /// position. You must manually call release to create a full 'click'.
133    pub fn press(self) {
134        let mut device = FAKE_DEVICE.lock().unwrap();
135        device.press(&Controller::Mouse(Mouse::from(self))).unwrap();
136        device.synchronize().unwrap();
137    }
138
139    /// Releases a given `MouseButton`. This means the button would be in the up position.
140    pub fn release(self) {
141        let mut device = FAKE_DEVICE.lock().unwrap();
142        device
143            .release(&Controller::Mouse(Mouse::from(self)))
144            .unwrap();
145        device.synchronize().unwrap();
146    }
147}
148
149impl MouseCursor {
150    /// Moves the mouse relative to its current position by a given amount of pixels.
151    pub fn move_rel(x: i32, y: i32) {
152        let mut device = FAKE_DEVICE.lock().unwrap();
153
154        device.position(&Position::X, x).unwrap();
155        device.position(&Position::Y, y).unwrap();
156
157        SEND_DISPLAY.with(|display| unsafe {
158            XWarpPointer(display, 0, 0, 0, 0, 0, 0, x, y);
159        });
160        device.synchronize().unwrap();
161    }
162
163    /// Moves the mouse to a given position based on absolute coordinates. The top left
164    /// corner of the screen is (0, 0).
165    pub fn move_abs(x: i32, y: i32) {
166        let mut device = FAKE_DEVICE.lock().unwrap();
167
168        SEND_DISPLAY.with(|display| unsafe {
169            XWarpPointer(
170                display,
171                0,
172                XRootWindow(display, XDefaultScreen(display)),
173                0,
174                0,
175                0,
176                0,
177                x,
178                y,
179            );
180        });
181        device.synchronize().unwrap();
182    }
183}
184
185impl MouseWheel {
186    /// Scrolls the mouse wheel vertically by a given amount.
187    pub fn scroll_ver(y: i32) {
188        if y < 0 {
189            MouseButton::OtherButton(4).press();
190            MouseButton::OtherButton(4).release();
191        } else {
192            MouseButton::OtherButton(5).press();
193            MouseButton::OtherButton(5).release();
194        }
195    }
196
197    /// Scrolls the mouse wheel horizontally by a given amount.
198    pub fn scroll_hor(x: i32) {
199        if x < 0 {
200            MouseButton::OtherButton(6).press();
201            MouseButton::OtherButton(6).release();
202        } else {
203            MouseButton::OtherButton(7).press();
204            MouseButton::OtherButton(7).release();
205        }
206    }
207}
208
209struct LibinputInterfaceRaw;
210
211impl LibinputInterfaceRaw {
212    fn seat(&self) -> String {
213        String::from("seat0")
214    }
215}
216
217impl LibinputInterface for LibinputInterfaceRaw {
218    fn open_restricted(&mut self, path: &Path, flags: i32) -> std::result::Result<RawFd, i32> {
219        if let Ok(fd) = open(path, OFlag::from_bits_truncate(flags), Mode::empty()) {
220            Ok(fd)
221        } else {
222            Err(1)
223        }
224    }
225
226    fn close_restricted(&mut self, fd: RawFd) {
227        let _ = close(fd);
228    }
229}
230
231/// Starts listening for bound input events.
232pub fn handle_input_events() {
233    let mut libinput_context = Libinput::new_with_udev(LibinputInterfaceRaw);
234    libinput_context
235        .udev_assign_seat(&LibinputInterfaceRaw.seat())
236        .unwrap();
237
238    while !MOUSE_BINDS.lock().unwrap().is_empty() || !KEYBD_BINDS.lock().unwrap().is_empty() {
239        libinput_context.dispatch().unwrap();
240
241        for event in libinput_context.by_ref() {
242            handle_input_event(event);
243        }
244
245        sleep(Duration::from_millis(10));
246    }
247}
248
249fn handle_input_event(event: Event) {
250    match event {
251        Keyboard(KeyboardEvent::Key(keyboard_key_event)) => {
252            let key = keyboard_key_event.key();
253            if let Some(keybd_key) = scan_code_to_key(key) {
254                if keyboard_key_event.key_state() == KeyState::Pressed {
255                    KEY_STATES.lock().unwrap().insert(keybd_key, true);
256
257                    if let Some(Bind::NormalBind(cb)) = KEYBD_BINDS.lock().unwrap().get(&keybd_key)
258                    {
259                        let cb = Arc::clone(cb);
260                        spawn(move || cb());
261                    }
262                } else {
263                    KEY_STATES.lock().unwrap().insert(keybd_key, false);
264                }
265            }
266        }
267        Pointer(Button(button_event)) => {
268            let button = button_event.button();
269            if let Some(mouse_button) = match button {
270                272 => Some(MouseButton::LeftButton),
271                273 => Some(MouseButton::RightButton),
272                274 => Some(MouseButton::MiddleButton),
273                275 => Some(MouseButton::X1Button),
274                276 => Some(MouseButton::X2Button),
275                _ => None,
276            } {
277                if button_event.button_state() == ButtonState::Pressed {
278                    BUTTON_STATES.lock().unwrap().insert(mouse_button, true);
279                    if let Some(Bind::NormalBind(cb)) =
280                        MOUSE_BINDS.lock().unwrap().get(&mouse_button)
281                    {
282                        let cb = Arc::clone(cb);
283                        spawn(move || cb());
284                    };
285                } else {
286                    BUTTON_STATES.lock().unwrap().insert(mouse_button, false);
287                }
288            }
289        }
290        _ => {}
291    }
292}
293
294trait DisplayAcquirable {
295    fn with<F, Z>(&self, cb: F) -> Z
296    where
297        F: FnOnce(*mut Display) -> Z;
298}
299
300impl DisplayAcquirable for AtomicPtr<Display> {
301    fn with<F, Z>(&self, cb: F) -> Z
302    where
303        F: FnOnce(*mut Display) -> Z,
304    {
305        let display = self.load(Ordering::Relaxed);
306        unsafe {
307            XLockDisplay(display);
308        };
309        let cb_result = cb(display);
310        unsafe {
311            XFlush(display);
312            XUnlockDisplay(display);
313        };
314        cb_result
315    }
316}