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
62pub struct Noct {
64 xdo: Xdo,
65 delay: u64,
66}
67unsafe impl Send for Noct {}
70
71impl Default for Noct {
72 fn default() -> Self {
74 Self {
75 xdo: unsafe { xdo_new(ptr::null()) },
76 delay: DEFAULT_DELAY,
77 }
78 }
79}
80impl Noct {
81 pub fn delay(&self) -> u64 {
85 self.delay
86 }
87 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 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}