x11_input_mirror/
keyboard_and_clicks.rs

1use mouse;
2
3use std::io::Read;
4use std::process::{Command, Stdio};
5use std::sync::mpsc::{channel, Receiver, Sender};
6use std::thread::{sleep, spawn};
7use std::time::Duration;
8
9#[derive(Debug)]
10pub enum EventKind {
11    KeyDown,
12    KeyUp,
13    MouseDown,
14    MouseUp,
15}
16
17#[derive(Debug)]
18pub struct Event {
19    pub kind: EventKind,
20    /// Keyboard button ID or mouse button ID.
21    pub code: u8,
22    /// Mouse position X, or 0 in case of keyboard event.
23    pub x: u16,
24    /// Mouse position Y, or 0 in case of keyboard event.
25    pub y: u16,
26}
27
28pub fn spawn_thread(interval_ms: u64) -> Receiver<Event> {
29    use self::EventKind::*;
30    let interval = Duration::from_millis(interval_ms);
31    let (tx, rx) = channel();
32    spawn(move || {
33        let r = Command::new("xinput")
34            .arg("test-xi2")
35            .arg("--root")
36            .stdout(Stdio::piped())
37            .spawn()
38            .unwrap();
39        let mut stdout = r.stdout.unwrap();
40        let mut buf = vec![0u8; 64 * 1024 * 1024];
41        let mut mode = None;
42        loop {
43            let num = stdout.read(&mut buf).unwrap();
44            if num == 0 {
45                sleep(interval);
46                continue;
47            }
48            let lines = String::from_utf8_lossy(&buf[0..num]);
49
50            for line in lines.split('\n') {
51                match mode {
52                    None => {
53                        if line.starts_with("EVENT type 2") {
54                            mode = Some(KeyDown);
55                        } else if line.starts_with("EVENT type 3") {
56                            mode = Some(KeyUp);
57                        } else if line.starts_with("EVENT type 4") {
58                            mode = Some(MouseDown);
59                        } else if line.starts_with("EVENT type 5") {
60                            mode = Some(MouseUp);
61                        } else if line.starts_with("EVENT type 15") {
62                            mode = Some(MouseDown);
63                        } else if line.starts_with("EVENT type 16") {
64                            mode = Some(MouseUp);
65                        }
66                    }
67                    Some(KeyDown) => mode = parse_keyboard(&tx, line, KeyDown),
68                    Some(KeyUp) => mode = parse_keyboard(&tx, line, KeyUp),
69                    Some(MouseDown) => mode = parse_click(&tx, line, MouseDown),
70                    Some(MouseUp) => mode = parse_click(&tx, line, MouseUp),
71                }
72            }
73        }
74    });
75    rx
76}
77
78fn parse_keyboard(tx: &Sender<Event>, line: &str, mode: EventKind) -> Option<EventKind> {
79    if line.starts_with("    detail: ") {
80        let num = &line[12..];
81        let num = num.parse::<u8>().unwrap();
82        tx.send(Event {
83            kind: mode,
84            code: num,
85            x: 0,
86            y: 0,
87        }).unwrap();
88        return None;
89    }
90    Some(mode)
91}
92
93fn parse_click(tx: &Sender<Event>, line: &str, mode: EventKind) -> Option<EventKind> {
94    if line.starts_with("    detail: ") {
95        let code = &line[12..];
96        let code = code.parse::<u8>().unwrap();
97        let mouse::Event { x, y } = mouse::get_current_mouse_location();
98        tx.send(Event {
99            kind: mode,
100            code,
101            x,
102            y,
103        }).unwrap();
104        None
105    } else {
106        Some(mode)
107    }
108}