noct/
linux.rs

1use libc;
2
3use crate::{Key, KeyboardControllable, MouseButton, MouseControllable};
4
5use self::libc::{c_char, c_int, c_void, useconds_t};
6use std::{borrow::Cow, ffi::CString, ptr};
7
8const CURRENT_WINDOW: c_int = 0;
9const DEFAULT_DELAY: u64 = 12000;
10type Window = c_int;
11type Xdo = *const c_void;
12
13#[link(name = "xdo")]
14extern "C" {
15    fn xdo_free(xdo: Xdo);
16    fn xdo_new(display: *const c_char) -> Xdo;
17
18    fn xdo_click_window(xdo: Xdo, window: Window, button: c_int) -> c_int;
19    fn xdo_mouse_down(xdo: Xdo, window: Window, button: c_int) -> c_int;
20    fn xdo_mouse_up(xdo: Xdo, window: Window, button: c_int) -> c_int;
21    fn xdo_move_mouse(xdo: Xdo, x: c_int, y: c_int, screen: c_int) -> c_int;
22    fn xdo_move_mouse_relative(xdo: Xdo, x: c_int, y: c_int) -> c_int;
23
24    fn xdo_enter_text_window(
25        xdo: Xdo,
26        window: Window,
27        string: *const c_char,
28        delay: useconds_t,
29    ) -> c_int;
30    fn xdo_send_keysequence_window(
31        xdo: Xdo,
32        window: Window,
33        string: *const c_char,
34        delay: useconds_t,
35    ) -> c_int;
36    fn xdo_send_keysequence_window_down(
37        xdo: Xdo,
38        window: Window,
39        string: *const c_char,
40        delay: useconds_t,
41    ) -> c_int;
42    fn xdo_send_keysequence_window_up(
43        xdo: Xdo,
44        window: Window,
45        string: *const c_char,
46        delay: useconds_t,
47    ) -> c_int;
48}
49
50fn mousebutton(button: MouseButton) -> c_int {
51    match button {
52        MouseButton::Left => 1,
53        MouseButton::Middle => 2,
54        MouseButton::Right => 3,
55        MouseButton::ScrollUp => 4,
56        MouseButton::ScrollDown => 5,
57        MouseButton::ScrollLeft => 6,
58        MouseButton::ScrollRight => 7,
59    }
60}
61
62/// The main struct for handling the event emitting
63pub struct Noct {
64    xdo: Xdo,
65    delay: u64,
66}
67// This is safe, we have a unique pointer.
68// TODO: use Unique<c_char> once stable.
69unsafe impl Send for Noct {}
70
71impl Default for Noct {
72    /// Create a new Noct instance
73    fn default() -> Self {
74        Self {
75            xdo: unsafe { xdo_new(ptr::null()) },
76            delay: DEFAULT_DELAY,
77        }
78    }
79}
80impl Noct {
81    /// Get the delay per keypress.
82    /// Default value is 12000.
83    /// This is Linux-specific.
84    pub fn delay(&self) -> u64 {
85        self.delay
86    }
87    /// Set the delay per keypress.
88    /// This is Linux-specific.
89    pub fn set_delay(&mut self, delay: u64) {
90        self.delay = delay;
91    }
92}
93impl Drop for Noct {
94    fn drop(&mut self) {
95        unsafe {
96            xdo_free(self.xdo);
97        }
98    }
99}
100impl MouseControllable for Noct {
101    fn mouse_move_to(&mut self, x: i32, y: i32) {
102        unsafe {
103            xdo_move_mouse(self.xdo, x as c_int, y as c_int, 0);
104        }
105    }
106    fn mouse_move_relative(&mut self, x: i32, y: i32) {
107        unsafe {
108            xdo_move_mouse_relative(self.xdo, x as c_int, y as c_int);
109        }
110    }
111    fn mouse_down(&mut self, button: MouseButton) {
112        unsafe {
113            xdo_mouse_down(self.xdo, CURRENT_WINDOW, mousebutton(button));
114        }
115    }
116    fn mouse_up(&mut self, button: MouseButton) {
117        unsafe {
118            xdo_mouse_up(self.xdo, CURRENT_WINDOW, mousebutton(button));
119        }
120    }
121    fn mouse_click(&mut self, button: MouseButton) {
122        unsafe {
123            xdo_click_window(self.xdo, CURRENT_WINDOW, mousebutton(button));
124        }
125    }
126    fn mouse_scroll_x(&mut self, length: i32) {
127        let button;
128        let mut length = length;
129
130        if length < 0 {
131            button = MouseButton::ScrollLeft;
132        } else {
133            button = MouseButton::ScrollRight;
134        }
135
136        if length < 0 {
137            length = -length;
138        }
139
140        for _ in 0..length {
141            self.mouse_click(button);
142        }
143    }
144    fn mouse_scroll_y(&mut self, length: i32) {
145        let button;
146        let mut length = length;
147
148        if length < 0 {
149            button = MouseButton::ScrollUp;
150        } else {
151            button = MouseButton::ScrollDown;
152        }
153
154        if length < 0 {
155            length = -length;
156        }
157
158        for _ in 0..length {
159            self.mouse_click(button);
160        }
161    }
162}
163fn keysequence<'a>(key: Key) -> Cow<'a, str> {
164    if let Key::Layout(c) = key {
165        return Cow::Owned(format!("U{:X}", c as u32));
166    }
167    if let Key::Raw(k) = key {
168        return Cow::Owned(format!("{}", k as u16));
169    }
170    #[allow(deprecated)]
171    // I mean duh, we still need to support deprecated keys until they're removed
172    Cow::Borrowed(match key {
173        Key::Alt => "Alt",
174        Key::Backspace => "BackSpace",
175        Key::CapsLock => "Caps_Lock",
176        Key::Control => "Control",
177        Key::Delete => "Delete",
178        Key::DownArrow => "Down",
179        Key::End => "End",
180        Key::Escape => "Escape",
181        Key::F1 => "F1",
182        Key::F10 => "F10",
183        Key::F11 => "F11",
184        Key::F12 => "F12",
185        Key::F2 => "F2",
186        Key::F3 => "F3",
187        Key::F4 => "F4",
188        Key::F5 => "F5",
189        Key::F6 => "F6",
190        Key::F7 => "F7",
191        Key::F8 => "F8",
192        Key::F9 => "F9",
193        Key::Home => "Home",
194        Key::Layout(_) => unreachable!(),
195        Key::LeftArrow => "Left",
196        Key::Option => "Option",
197        Key::PageDown => "Page_Down",
198        Key::PageUp => "Page_Up",
199        Key::Raw(_) => unreachable!(),
200        Key::Return => "Return",
201        Key::RightArrow => "Right",
202        Key::Shift => "Shift",
203        Key::Space => "space",
204        Key::Tab => "Tab",
205        Key::UpArrow => "Up",
206
207        Key::Command | Key::Super | Key::Windows | Key::Meta => "Super",
208    })
209}
210impl KeyboardControllable for Noct {
211    fn key_sequence(&mut self, sequence: &str) {
212        let string = CString::new(sequence).unwrap();
213        unsafe {
214            xdo_enter_text_window(
215                self.xdo,
216                CURRENT_WINDOW,
217                string.as_ptr(),
218                self.delay as useconds_t,
219            );
220        }
221    }
222    fn key_down(&mut self, key: Key) {
223        let string = CString::new(&*keysequence(key)).unwrap();
224        unsafe {
225            xdo_send_keysequence_window_down(
226                self.xdo,
227                CURRENT_WINDOW,
228                string.as_ptr(),
229                self.delay as useconds_t,
230            );
231        }
232    }
233    fn key_up(&mut self, key: Key) {
234        let string = CString::new(&*keysequence(key)).unwrap();
235        unsafe {
236            xdo_send_keysequence_window_up(
237                self.xdo,
238                CURRENT_WINDOW,
239                string.as_ptr(),
240                self.delay as useconds_t,
241            );
242        }
243    }
244    fn key_click(&mut self, key: Key) {
245        let string = CString::new(&*keysequence(key)).unwrap();
246        unsafe {
247            xdo_send_keysequence_window(
248                self.xdo,
249                CURRENT_WINDOW,
250                string.as_ptr(),
251                self.delay as useconds_t,
252            );
253        }
254    }
255}