lan_mouse_ipc/
connect.rs

1use crate::{ConnectionError, FrontendEvent, FrontendRequest, IpcError};
2use std::{
3    cmp::min,
4    io::{self, prelude::*, BufReader, LineWriter, Lines},
5    thread,
6    time::Duration,
7};
8
9#[cfg(unix)]
10use std::os::unix::net::UnixStream;
11
12#[cfg(windows)]
13use std::net::TcpStream;
14
15pub struct FrontendEventReader {
16    #[cfg(unix)]
17    lines: Lines<BufReader<UnixStream>>,
18    #[cfg(windows)]
19    lines: Lines<BufReader<TcpStream>>,
20}
21
22pub struct FrontendRequestWriter {
23    #[cfg(unix)]
24    line_writer: LineWriter<UnixStream>,
25    #[cfg(windows)]
26    line_writer: LineWriter<TcpStream>,
27}
28
29impl FrontendEventReader {
30    pub fn next_event(&mut self) -> Option<Result<FrontendEvent, IpcError>> {
31        match self.lines.next()? {
32            Err(e) => Some(Err(e.into())),
33            Ok(l) => Some(serde_json::from_str(l.as_str()).map_err(|e| e.into())),
34        }
35    }
36}
37
38impl FrontendRequestWriter {
39    pub fn request(&mut self, request: FrontendRequest) -> Result<(), io::Error> {
40        let mut json = serde_json::to_string(&request).unwrap();
41        log::debug!("requesting: {json}");
42        json.push('\n');
43        self.line_writer.write_all(json.as_bytes())?;
44        Ok(())
45    }
46}
47
48pub fn connect() -> Result<(FrontendEventReader, FrontendRequestWriter), ConnectionError> {
49    let rx = wait_for_service()?;
50    let tx = rx.try_clone()?;
51    let buf_reader = BufReader::new(rx);
52    let lines = buf_reader.lines();
53    let line_writer = LineWriter::new(tx);
54    let reader = FrontendEventReader { lines };
55    let writer = FrontendRequestWriter { line_writer };
56    Ok((reader, writer))
57}
58
59/// wait for the lan-mouse socket to come online
60#[cfg(unix)]
61fn wait_for_service() -> Result<UnixStream, ConnectionError> {
62    let socket_path = crate::default_socket_path()?;
63    let mut duration = Duration::from_millis(10);
64    loop {
65        if let Ok(stream) = UnixStream::connect(&socket_path) {
66            break Ok(stream);
67        }
68        // a signaling mechanism or inotify could be used to
69        // improve this
70        thread::sleep(exponential_back_off(&mut duration));
71    }
72}
73
74#[cfg(windows)]
75fn wait_for_service() -> Result<TcpStream, ConnectionError> {
76    let mut duration = Duration::from_millis(10);
77    loop {
78        if let Ok(stream) = TcpStream::connect("127.0.0.1:5252") {
79            break Ok(stream);
80        }
81        thread::sleep(exponential_back_off(&mut duration));
82    }
83}
84
85fn exponential_back_off(duration: &mut Duration) -> Duration {
86    let new = duration.saturating_mul(2);
87    *duration = min(new, Duration::from_secs(1));
88    *duration
89}