1extern crate rand;
10
11#[cfg(target_os = "macos")]
12use core_graphics::event;
13#[cfg(target_os = "macos")]
14use core_graphics::event::{CGEvent, CGEventFlags, CGKeyCode};
15#[cfg(target_os = "macos")]
16use core_graphics::event_source::CGEventSource;
17#[cfg(target_os = "macos")]
18use core_graphics::event_source::CGEventSourceStateID::HIDSystemState;
19#[cfg(target_os = "linux")]
20use internal;
21
22use self::rand::Rng;
23
24#[derive(Copy, Clone, Debug, PartialEq)]
26pub enum Flag {
27 Shift,
28 Control,
29 Alt,
30 Meta,
31
32 Help,
34}
35
36#[derive(Copy, Clone, Debug, PartialEq)]
38pub enum KeyCode {
39 F1,
40 F2,
41 F3,
42 F4,
43 F5,
44 F6,
45 F7,
46 F8,
47 F9,
48 F10,
49 F11,
50 F12,
51 F13,
52 F14,
53 F15,
54 F16,
55 F17,
56 F18,
57 F19,
58 F20,
59 F21,
60 F22,
61 F23,
62 F24,
63 LeftArrow,
64 Control,
65 RightArrow,
66 DownArrow,
67 End,
68 UpArrow,
69 PageUp,
70 Alt,
71 Return,
72 PageDown,
73 Delete,
74 #[cfg(not(target_os = "macos"))]
76 Insert,
77 Home,
78 Escape,
79 Backspace,
80 Meta,
81 CapsLock,
82 Shift,
83 Tab,
84 Space,
85 PrintScreen,
86 ScrollLock,
87 Pause,
88 Num0,
89 Num1,
90 Num2,
91 Num3,
92 Num4,
93 Num5,
94 Num6,
95 Num7,
96 Num8,
97 Num9,
98 NumDecimal,
99 NumLock,
100 NumDivide,
101 NumMultiply,
102 NumSubtract,
103 NumAdd,
104 NumEnter,
105}
106
107pub trait KeyCodeConvertible {
108 #[cfg(target_os = "macos")]
109 fn code(&self) -> CGKeyCode;
110 #[cfg(target_os = "linux")]
111 fn code(&self) -> XKeyCode;
112 #[cfg(windows)]
113 fn code(&self) -> WinKeyCode;
114 fn character(&self) -> Option<char> {
115 None
116 }
117 fn flags(&self) -> &[Flag] {
118 &[]
119 }
120}
121
122#[derive(Copy, Clone, Debug)]
123pub struct Character(pub char);
124
125#[derive(Copy, Clone, Debug)]
126pub struct Code(pub KeyCode);
127
128pub fn type_string(string: &str, flags: &[Flag], wpm: f64, noise: f64) {
131 let cpm = wpm * 5.0;
132 let cps = cpm / 60.0;
133 let ms_per_character: u64 = if cps == 0.0 {
134 0
135 } else {
136 (1000.0 / cps).round() as u64
137 };
138 let ms_per_stroke = (ms_per_character as f64 / 2.0).round() as u64;
139
140 for c in string.chars() {
141 let tolerance = (noise * ms_per_character as f64).round() as u64;
142 let noise = if tolerance > 0 {
143 rand::thread_rng().gen_range(0, tolerance)
144 } else {
145 0
146 };
147
148 tap(&Character(c), flags, ms_per_stroke, ms_per_stroke);
149 std::thread::sleep(std::time::Duration::from_millis(ms_per_stroke + noise));
150 }
151}
152
153pub fn tap<T: KeyCodeConvertible + Copy>(
159 key: &T,
160 flags: &[Flag],
161 delay_ms: u64,
162 modifier_delay_ms: u64,
163) {
164 toggle(key, true, flags, modifier_delay_ms);
165 std::thread::sleep(std::time::Duration::from_millis(delay_ms));
166 toggle(key, false, flags, modifier_delay_ms);
167}
168
169pub fn toggle<T: KeyCodeConvertible>(key: &T, down: bool, flags: &[Flag], modifier_delay_ms: u64) {
174 let key_flags = key.character().map(flags_for_char).unwrap_or(&[]);
175 let mut appended_flags: Vec<Flag> = Vec::with_capacity(flags.len() + key_flags.len());
176 appended_flags.extend_from_slice(flags);
177 for flag in key_flags.iter() {
178 if !flags.contains(flag) {
179 appended_flags.push(*flag);
180 }
181 }
182 system_toggle(key, down, &appended_flags, modifier_delay_ms);
183}
184
185#[cfg(target_os = "macos")]
186fn char_to_key_code(character: char) -> CGKeyCode {
187 use core_graphics::event::EventField;
188 let source = CGEventSource::new(HIDSystemState).unwrap();
189 let event = CGEvent::new_keyboard_event(source, 0, true).unwrap();
190 let mut buf = [0; 2];
191 event.set_string_from_utf16_unchecked(character.encode_utf16(&mut buf));
192 event.get_integer_value_field(EventField::KEYBOARD_EVENT_KEYCODE) as CGKeyCode
193}
194
195#[cfg(target_os = "linux")]
196fn char_to_key_code(character: char) -> XKeyCode {
197 match character {
198 ' ' => XKeyCode::from(x11::keysym::XK_space),
199 '!' => XKeyCode::from(x11::keysym::XK_exclam),
200 '"' => XKeyCode::from(x11::keysym::XK_quotedbl),
201 '#' => XKeyCode::from(x11::keysym::XK_numbersign),
202 '$' => XKeyCode::from(x11::keysym::XK_dollar),
203 '%' => XKeyCode::from(x11::keysym::XK_percent),
204 '&' => XKeyCode::from(x11::keysym::XK_ampersand),
205 '(' => XKeyCode::from(x11::keysym::XK_parenleft),
206 ')' => XKeyCode::from(x11::keysym::XK_parenright),
207 '*' => XKeyCode::from(x11::keysym::XK_asterisk),
208 '+' => XKeyCode::from(x11::keysym::XK_plus),
209 ',' => XKeyCode::from(x11::keysym::XK_comma),
210 '-' => XKeyCode::from(x11::keysym::XK_minus),
211 '.' => XKeyCode::from(x11::keysym::XK_period),
212 '/' => XKeyCode::from(x11::keysym::XK_slash),
213 ':' => XKeyCode::from(x11::keysym::XK_colon),
214 ';' => XKeyCode::from(x11::keysym::XK_semicolon),
215 '<' => XKeyCode::from(x11::keysym::XK_less),
216 '=' => XKeyCode::from(x11::keysym::XK_equal),
217 '>' => XKeyCode::from(x11::keysym::XK_greater),
218 '?' => XKeyCode::from(x11::keysym::XK_question),
219 '@' => XKeyCode::from(x11::keysym::XK_at),
220 '[' => XKeyCode::from(x11::keysym::XK_bracketleft),
221 '\'' => XKeyCode::from(x11::keysym::XK_apostrophe),
222 '\\' => XKeyCode::from(x11::keysym::XK_backslash),
223 '\n' => XKeyCode::from(x11::keysym::XK_Return),
224 '\t' => XKeyCode::from(x11::keysym::XK_Tab),
225 ']' => XKeyCode::from(x11::keysym::XK_bracketright),
226 '^' => XKeyCode::from(x11::keysym::XK_asciicircum),
227 '_' => XKeyCode::from(x11::keysym::XK_underscore),
228 '`' => XKeyCode::from(x11::keysym::XK_grave),
229 '{' => XKeyCode::from(x11::keysym::XK_braceleft),
230 '|' => XKeyCode::from(x11::keysym::XK_bar),
231 '}' => XKeyCode::from(x11::keysym::XK_braceright),
232 '~' => XKeyCode::from(x11::keysym::XK_asciitilde),
233 _ => unsafe {
234 let mut buf = [0; 2];
235 x11::xlib::XStringToKeysym(
236 character.encode_utf8(&mut buf).as_ptr() as *const libc::c_char
237 ) as XKeyCode
238 },
239 }
240}
241
242#[cfg(target_os = "macos")]
243fn flags_for_char<'a>(_character: char) -> &'a [Flag] {
244 &[]
245}
246
247#[cfg(windows)]
248fn flags_for_char<'a>(_character: char) -> &'a [Flag] {
249 &[]
250}
251
252#[cfg(target_os = "linux")]
253fn flags_for_char<'a>(character: char) -> &'a [Flag] {
254 const UPPERCASE_CHARACTERS: &[char] = &[
255 '!', '#', '$', '%', '&', '(', ')', '*', '+', ':', '<', '>', '?', '@', '{', '|', '}', '~',
256 '_', '^', '"',
257 ];
258 if character.is_uppercase() || UPPERCASE_CHARACTERS.contains(&character) {
259 &[Flag::Shift]
260 } else {
261 &[]
262 }
263}
264
265impl KeyCodeConvertible for Character {
266 fn character(&self) -> Option<char> {
267 Some(self.0)
268 }
269
270 #[cfg(target_os = "macos")]
271 fn code(&self) -> CGKeyCode {
272 char_to_key_code(self.0)
273 }
274
275 #[cfg(windows)]
276 fn code(&self) -> WinKeyCode {
277 panic!("Unsupported OS")
278 }
279
280 #[cfg(target_os = "linux")]
281 fn code(&self) -> XKeyCode {
282 char_to_key_code(self.0)
283 }
284}
285
286impl KeyCodeConvertible for Code {
287 #[cfg(target_os = "macos")]
288 fn code(&self) -> CGKeyCode {
289 CGKeyCode::from(self.0)
290 }
291
292 #[cfg(windows)]
293 fn code(&self) -> WinKeyCode {
294 WinKeyCode::from(self.0)
295 }
296
297 #[cfg(target_os = "linux")]
298 fn code(&self) -> XKeyCode {
299 XKeyCode::from(self.0)
300 }
301}
302
303#[cfg(target_os = "macos")]
304impl From<Flag> for CGEventFlags {
305 fn from(flag: Flag) -> CGEventFlags {
306 match flag {
307 Flag::Shift => event::CGEventFlags::CGEventFlagShift,
308 Flag::Control => event::CGEventFlags::CGEventFlagControl,
309 Flag::Alt => event::CGEventFlags::CGEventFlagAlternate,
310 Flag::Meta => event::CGEventFlags::CGEventFlagCommand,
311 Flag::Help => event::CGEventFlags::CGEventFlagHelp,
312 }
313 }
314}
315
316#[cfg(target_os = "macos")]
318impl From<KeyCode> for CGKeyCode {
319 fn from(code: KeyCode) -> CGKeyCode {
320 match code {
321 KeyCode::F1 => event::KeyCode::F1,
322 KeyCode::F2 => event::KeyCode::F2,
323 KeyCode::F3 => event::KeyCode::F3,
324 KeyCode::F4 => event::KeyCode::F4,
325 KeyCode::F5 => event::KeyCode::F5,
326 KeyCode::F6 => event::KeyCode::F6,
327 KeyCode::F7 => event::KeyCode::F7,
328 KeyCode::F8 => event::KeyCode::F8,
329 KeyCode::F9 => event::KeyCode::F9,
330 KeyCode::F10 => event::KeyCode::F10,
331 KeyCode::F11 => event::KeyCode::F11,
332 KeyCode::F12 => event::KeyCode::F12,
333 KeyCode::F13 => event::KeyCode::F13,
334 KeyCode::F14 => event::KeyCode::F14,
335 KeyCode::F15 => event::KeyCode::F15,
336 KeyCode::F16 => event::KeyCode::F16,
337 KeyCode::F17 => event::KeyCode::F17,
338 KeyCode::F18 => event::KeyCode::F18,
339 KeyCode::F19 => event::KeyCode::F19,
340 KeyCode::F20 => event::KeyCode::F20,
341 KeyCode::F21 | KeyCode::F22 | KeyCode::F23 | KeyCode::F24 => 0,
342 KeyCode::LeftArrow => event::KeyCode::LEFT_ARROW,
343 KeyCode::Control => event::KeyCode::CONTROL,
344 KeyCode::RightArrow => event::KeyCode::RIGHT_ARROW,
345 KeyCode::DownArrow => event::KeyCode::DOWN_ARROW,
346 KeyCode::End => event::KeyCode::END,
347 KeyCode::UpArrow => event::KeyCode::UP_ARROW,
348 KeyCode::PageUp => event::KeyCode::PAGE_UP,
349 KeyCode::Alt => event::KeyCode::OPTION,
350 KeyCode::Return => event::KeyCode::RETURN,
351 KeyCode::PageDown => event::KeyCode::PAGE_DOWN,
352 KeyCode::Delete => event::KeyCode::DELETE,
353 KeyCode::Home => event::KeyCode::HOME,
354 KeyCode::Escape => event::KeyCode::ESCAPE,
355 KeyCode::Backspace => event::KeyCode::DELETE,
356 KeyCode::Meta => event::KeyCode::COMMAND,
357 KeyCode::CapsLock => event::KeyCode::CAPS_LOCK,
358 KeyCode::Shift => event::KeyCode::SHIFT,
359 KeyCode::Tab => event::KeyCode::TAB,
360 KeyCode::Space => event::KeyCode::SPACE,
361 KeyCode::PrintScreen => event::KeyCode::F13,
362 KeyCode::ScrollLock => event::KeyCode::F14,
363 KeyCode::Pause => event::KeyCode::F15,
364 KeyCode::Num0 => 0x1D,
365 KeyCode::Num1 => 0x12,
366 KeyCode::Num2 => 0x13,
367 KeyCode::Num3 => 0x14,
368 KeyCode::Num4 => 0x15,
369 KeyCode::Num5 => 0x17,
370 KeyCode::Num6 => 0x16,
371 KeyCode::Num7 => 0x1A,
372 KeyCode::Num8 => 0x1C,
373 KeyCode::Num9 => 0x19,
374 KeyCode::NumDecimal => 0x41,
375 KeyCode::NumLock => 0x47,
376 KeyCode::NumDivide => 0x4B,
377 KeyCode::NumMultiply => 0x43,
378 KeyCode::NumSubtract => 0x4E,
379 KeyCode::NumAdd => 0x45,
380 KeyCode::NumEnter => 0x4C,
381 }
382 }
383}
384
385#[cfg(target_os = "macos")]
386fn cg_event_mask_for_flags(flags: &[Flag]) -> CGEventFlags {
387 flags
388 .iter()
389 .map(|&x| CGEventFlags::from(x))
390 .fold(event::CGEventFlags::CGEventFlagNull, |x, y| {
391 x | y as CGEventFlags
392 })
393}
394
395#[cfg(target_os = "macos")]
396fn system_toggle<T: KeyCodeConvertible>(
397 key: &T,
398 down: bool,
399 flags: &[Flag],
400 _modifier_delay_ms: u64,
401) {
402 use core_graphics::event::CGEventType::*;
403 use core_graphics::event::{CGEventTapLocation, CGEventType};
404 let source = CGEventSource::new(HIDSystemState).unwrap();
405
406 if flags.is_empty() {
407 if let Some(character) = key.character() {
408 let mut buf = [0; 2];
409 let event = CGEvent::new_keyboard_event(source, 0, down).unwrap();
410 event.set_string_from_utf16_unchecked(character.encode_utf16(&mut buf));
411 event.post(CGEventTapLocation::HID);
412 return;
413 }
414 }
415
416 let code = key.code();
417 if code != 0 {
418 let event = CGEvent::new_keyboard_event(source, code, down).unwrap();
419 let event_type: CGEventType = if down { KeyDown } else { KeyUp };
420 event.set_type(event_type);
421 event.set_flags(cg_event_mask_for_flags(flags));
422 event.post(CGEventTapLocation::HID);
423 }
424}
425
426#[cfg(windows)]
427type WinKeyCode = i32;
428
429#[cfg(windows)]
430impl From<Flag> for WinKeyCode {
431 fn from(flag: Flag) -> WinKeyCode {
432 use winapi::um::winuser;
433 let win_code = match flag {
434 Flag::Shift => winuser::VK_SHIFT,
435 Flag::Control => winuser::VK_CONTROL,
436 Flag::Alt => winuser::VK_MENU,
437 Flag::Meta => winuser::VK_LWIN,
438 Flag::Help => winuser::VK_HELP,
439 };
440 win_code as WinKeyCode
441 }
442}
443
444#[cfg(windows)]
445impl From<KeyCode> for WinKeyCode {
446 fn from(code: KeyCode) -> WinKeyCode {
447 use winapi::um::winuser;
448 let win_code = match code {
449 KeyCode::F1 => winuser::VK_F1,
450 KeyCode::F2 => winuser::VK_F2,
451 KeyCode::F3 => winuser::VK_F3,
452 KeyCode::F4 => winuser::VK_F4,
453 KeyCode::F5 => winuser::VK_F5,
454 KeyCode::F6 => winuser::VK_F6,
455 KeyCode::F7 => winuser::VK_F7,
456 KeyCode::F8 => winuser::VK_F8,
457 KeyCode::F9 => winuser::VK_F9,
458 KeyCode::F10 => winuser::VK_F10,
459 KeyCode::F11 => winuser::VK_F11,
460 KeyCode::F12 => winuser::VK_F12,
461 KeyCode::F13 => winuser::VK_F13,
462 KeyCode::F14 => winuser::VK_F14,
463 KeyCode::F15 => winuser::VK_F15,
464 KeyCode::F16 => winuser::VK_F16,
465 KeyCode::F17 => winuser::VK_F17,
466 KeyCode::F18 => winuser::VK_F18,
467 KeyCode::F19 => winuser::VK_F19,
468 KeyCode::F20 => winuser::VK_F20,
469 KeyCode::F21 => winuser::VK_F21,
470 KeyCode::F22 => winuser::VK_F22,
471 KeyCode::F23 => winuser::VK_F23,
472 KeyCode::F24 => winuser::VK_F24,
473 KeyCode::LeftArrow => winuser::VK_LEFT,
474 KeyCode::Control => winuser::VK_CONTROL,
475 KeyCode::RightArrow => winuser::VK_RIGHT,
476 KeyCode::DownArrow => winuser::VK_DOWN,
477 KeyCode::End => winuser::VK_END,
478 KeyCode::UpArrow => winuser::VK_UP,
479 KeyCode::PageUp => winuser::VK_PRIOR,
480 KeyCode::Alt => winuser::VK_MENU,
481 KeyCode::Return => winuser::VK_RETURN,
482 KeyCode::PageDown => winuser::VK_NEXT,
483 KeyCode::Delete => winuser::VK_DELETE,
484 KeyCode::Insert => winuser::VK_INSERT,
485 KeyCode::Home => winuser::VK_HOME,
486 KeyCode::Escape => winuser::VK_ESCAPE,
487 KeyCode::Backspace => winuser::VK_BACK,
488 KeyCode::Meta => winuser::VK_LWIN,
489 KeyCode::CapsLock => winuser::VK_CAPITAL,
490 KeyCode::Shift => winuser::VK_SHIFT,
491 KeyCode::Tab => winuser::VK_TAB,
492 KeyCode::Space => winuser::VK_SPACE,
493 KeyCode::PrintScreen => winuser::VK_SNAPSHOT,
494 KeyCode::ScrollLock => winuser::VK_SCROLL,
495 KeyCode::Pause => winuser::VK_PAUSE,
496 KeyCode::Num0 => winuser::VK_NUMPAD0,
497 KeyCode::Num1 => winuser::VK_NUMPAD1,
498 KeyCode::Num2 => winuser::VK_NUMPAD2,
499 KeyCode::Num3 => winuser::VK_NUMPAD3,
500 KeyCode::Num4 => winuser::VK_NUMPAD4,
501 KeyCode::Num5 => winuser::VK_NUMPAD5,
502 KeyCode::Num6 => winuser::VK_NUMPAD6,
503 KeyCode::Num7 => winuser::VK_NUMPAD7,
504 KeyCode::Num8 => winuser::VK_NUMPAD8,
505 KeyCode::Num9 => winuser::VK_NUMPAD9,
506 KeyCode::NumDecimal => winuser::VK_DECIMAL,
507 KeyCode::NumLock => winuser::VK_NUMLOCK,
508 KeyCode::NumDivide => winuser::VK_DIVIDE,
509 KeyCode::NumMultiply => winuser::VK_MULTIPLY,
510 KeyCode::NumSubtract => winuser::VK_SUBTRACT,
511 KeyCode::NumAdd => winuser::VK_ADD,
512 KeyCode::NumEnter => winuser::VK_RETURN,
513 };
514 win_code as WinKeyCode
515 }
516}
517
518#[cfg(windows)]
519fn win_send_key_event(keycode: WinKeyCode, down: bool, delay_ms: u64) {
520 use winapi::um::winuser::{keybd_event, KEYEVENTF_KEYUP};
521 let flags = if down { 0 } else { KEYEVENTF_KEYUP };
522 unsafe { keybd_event(keycode as u8, 0, flags, 0) };
523 std::thread::sleep(std::time::Duration::from_millis(delay_ms));
524}
525
526#[cfg(windows)]
527fn system_toggle<T: KeyCodeConvertible>(
528 key: &T,
529 down: bool,
530 flags: &[Flag],
531 modifier_delay_ms: u64,
532) {
533 use winapi::um::winuser::{
534 SendInput, INPUT, INPUT_u, INPUT_KEYBOARD, KEYBDINPUT, KEYEVENTF_KEYUP, KEYEVENTF_UNICODE,
535 };
536 use std::mem::size_of;
537
538 for &flag in flags.iter() {
539 win_send_key_event(WinKeyCode::from(flag), down, modifier_delay_ms);
540 }
541 if let Some(character) = key.character() {
542 let flags = if down { 0 } else { KEYEVENTF_KEYUP };
543 let mut buf = [0; 2];
544 for word in character.encode_utf16(&mut buf) {
545 let mut input = INPUT {
546 type_: INPUT_KEYBOARD,
547 u: unsafe {
548 std::mem::transmute_copy(&(
549 KEYBDINPUT {
550 wVk: 0,
551 wScan: *word,
552 dwFlags: KEYEVENTF_UNICODE | flags,
553 time: 0,
554 dwExtraInfo: 0,
555 },
556 [0; size_of::<INPUT_u>() - size_of::<KEYBDINPUT>()],
557 ))
558 },
559 };
560 unsafe {
561 SendInput(1, &mut input, std::mem::size_of::<INPUT>() as i32);
562 }
563 }
564 } else {
565 win_send_key_event(key.code(), down, 0);
566 }
567}
568
569#[cfg(target_os = "linux")]
570type XKeyCode = u64;
571
572#[cfg(target_os = "linux")]
573impl From<Flag> for XKeyCode {
574 fn from(flag: Flag) -> XKeyCode {
575 let x_code = match flag {
576 Flag::Shift => x11::keysym::XK_Shift_L,
577 Flag::Control => x11::keysym::XK_Control_L,
578 Flag::Alt => x11::keysym::XK_Alt_L,
579 Flag::Meta => x11::keysym::XK_Meta_L,
580 Flag::Help => x11::keysym::XK_Help,
581 };
582 XKeyCode::from(x_code)
583 }
584}
585
586#[cfg(target_os = "linux")]
587impl From<KeyCode> for XKeyCode {
588 fn from(code: KeyCode) -> XKeyCode {
589 let x_code = match code {
590 KeyCode::F1 => x11::keysym::XK_F1,
591 KeyCode::F2 => x11::keysym::XK_F2,
592 KeyCode::F3 => x11::keysym::XK_F3,
593 KeyCode::F4 => x11::keysym::XK_F4,
594 KeyCode::F5 => x11::keysym::XK_F5,
595 KeyCode::F6 => x11::keysym::XK_F6,
596 KeyCode::F7 => x11::keysym::XK_F7,
597 KeyCode::F8 => x11::keysym::XK_F8,
598 KeyCode::F9 => x11::keysym::XK_F9,
599 KeyCode::F10 => x11::keysym::XK_F10,
600 KeyCode::F11 => x11::keysym::XK_F11,
601 KeyCode::F12 => x11::keysym::XK_F12,
602 KeyCode::F13 => x11::keysym::XK_F13,
603 KeyCode::F14 => x11::keysym::XK_F14,
604 KeyCode::F15 => x11::keysym::XK_F15,
605 KeyCode::F16 => x11::keysym::XK_F16,
606 KeyCode::F17 => x11::keysym::XK_F17,
607 KeyCode::F18 => x11::keysym::XK_F18,
608 KeyCode::F19 => x11::keysym::XK_F19,
609 KeyCode::F20 => x11::keysym::XK_F20,
610 KeyCode::F21 => x11::keysym::XK_F21,
611 KeyCode::F22 => x11::keysym::XK_F22,
612 KeyCode::F23 => x11::keysym::XK_F23,
613 KeyCode::F24 => x11::keysym::XK_F24,
614 KeyCode::LeftArrow => x11::keysym::XK_Left,
615 KeyCode::Control => x11::keysym::XK_Control_L,
616 KeyCode::RightArrow => x11::keysym::XK_Right,
617 KeyCode::DownArrow => x11::keysym::XK_Down,
618 KeyCode::End => x11::keysym::XK_End,
619 KeyCode::UpArrow => x11::keysym::XK_Up,
620 KeyCode::PageUp => x11::keysym::XK_Page_Up,
621 KeyCode::Alt => x11::keysym::XK_Alt_L,
622 KeyCode::Return => x11::keysym::XK_Return,
623 KeyCode::PageDown => x11::keysym::XK_Page_Down,
624 KeyCode::Delete => x11::keysym::XK_Delete,
625 KeyCode::Insert => x11::keysym::XK_Insert,
626 KeyCode::Home => x11::keysym::XK_Home,
627 KeyCode::Escape => x11::keysym::XK_Escape,
628 KeyCode::Backspace => x11::keysym::XK_BackSpace,
629 KeyCode::Meta => x11::keysym::XK_Meta_L,
630 KeyCode::CapsLock => x11::keysym::XK_Caps_Lock,
631 KeyCode::Shift => x11::keysym::XK_Shift_L,
632 KeyCode::Tab => x11::keysym::XK_Tab,
633 KeyCode::Space => x11::keysym::XK_space,
634 KeyCode::PrintScreen => x11::keysym::XK_Print,
635 KeyCode::ScrollLock => x11::keysym::XK_Scroll_Lock,
636 KeyCode::Pause => x11::keysym::XK_Pause,
637 KeyCode::Num0 => x11::keysym::XK_KP_0,
638 KeyCode::Num1 => x11::keysym::XK_KP_1,
639 KeyCode::Num2 => x11::keysym::XK_KP_2,
640 KeyCode::Num3 => x11::keysym::XK_KP_3,
641 KeyCode::Num4 => x11::keysym::XK_KP_4,
642 KeyCode::Num5 => x11::keysym::XK_KP_5,
643 KeyCode::Num6 => x11::keysym::XK_KP_6,
644 KeyCode::Num7 => x11::keysym::XK_KP_7,
645 KeyCode::Num8 => x11::keysym::XK_KP_8,
646 KeyCode::Num9 => x11::keysym::XK_KP_9,
647 KeyCode::NumDecimal => x11::keysym::XK_KP_Decimal,
648 KeyCode::NumLock => x11::keysym::XK_Num_Lock,
649 KeyCode::NumDivide => x11::keysym::XK_KP_Divide,
650 KeyCode::NumMultiply => x11::keysym::XK_KP_Multiply,
651 KeyCode::NumSubtract => x11::keysym::XK_KP_Subtract,
652 KeyCode::NumAdd => x11::keysym::XK_KP_Add,
653 KeyCode::NumEnter => x11::keysym::XK_KP_Enter,
654 };
655 XKeyCode::from(x_code)
656 }
657}
658
659#[cfg(target_os = "linux")]
660fn x_send_key_event(
661 display: *mut x11::xlib::Display,
662 keycode: XKeyCode,
663 down: bool,
664 delay_ms: u64,
665) {
666 unsafe {
667 XTestFakeKeyEvent(
668 display,
669 x11::xlib::XKeysymToKeycode(display, keycode as libc::c_ulong),
670 down as i32,
671 x11::xlib::CurrentTime,
672 );
673 x11::xlib::XFlush(display);
674 };
675
676 std::thread::sleep(std::time::Duration::from_millis(delay_ms));
677}
678
679#[cfg(target_os = "linux")]
680fn system_toggle<T: KeyCodeConvertible>(
681 key: &T,
682 down: bool,
683 flags: &[Flag],
684 modifier_delay_ms: u64,
685) {
686 internal::X_MAIN_DISPLAY.with(|display| {
687 for &flag in flags.iter() {
688 x_send_key_event(
689 display.as_ptr(),
690 XKeyCode::from(flag),
691 down,
692 modifier_delay_ms,
693 );
694 }
695 x_send_key_event(display.as_ptr(), key.code(), down, 0);
696 })
697}
698
699#[cfg(target_os = "linux")]
700extern "C" {
701 fn XTestFakeKeyEvent(
702 display: *mut x11::xlib::Display,
703 keycode: u8,
704 is_press: i32,
705 delay: x11::xlib::Time,
706 ) -> i32;
707}