1use std::{
2 collections::HashSet,
3 env::VarError,
4 fmt::Display,
5 io,
6 net::{IpAddr, SocketAddr},
7 str::FromStr,
8};
9use thiserror::Error;
10
11#[cfg(unix)]
12use std::{
13 env,
14 path::{Path, PathBuf},
15};
16
17use serde::{Deserialize, Serialize};
18
19mod connect;
20mod connect_async;
21mod listen;
22
23pub use connect::{connect, FrontendEventReader, FrontendRequestWriter};
24pub use connect_async::{connect_async, AsyncFrontendEventReader, AsyncFrontendRequestWriter};
25pub use listen::AsyncFrontendListener;
26
27#[derive(Debug, Error)]
28pub enum ConnectionError {
29 #[error(transparent)]
30 SocketPath(#[from] SocketPathError),
31 #[error(transparent)]
32 Io(#[from] io::Error),
33}
34
35#[derive(Debug, Error)]
36pub enum ListenerCreationError {
37 #[error("could not determine socket-path: `{0}`")]
38 SocketPath(#[from] SocketPathError),
39 #[error("service already running!")]
40 AlreadyRunning,
41 #[error("failed to bind lan-mouse socket: `{0}`")]
42 Bind(io::Error),
43}
44
45#[derive(Debug, Error)]
46pub enum IpcError {
47 #[error("io error occured: `{0}`")]
48 Io(#[from] io::Error),
49 #[error("invalid json: `{0}`")]
50 Json(#[from] serde_json::Error),
51 #[error(transparent)]
52 Connection(#[from] ConnectionError),
53 #[error(transparent)]
54 Listen(#[from] ListenerCreationError),
55}
56
57pub const DEFAULT_PORT: u16 = 4242;
58
59#[derive(Debug, Default, Eq, Hash, PartialEq, Clone, Copy, Serialize, Deserialize)]
60pub enum Position {
61 #[default]
62 Left,
63 Right,
64 Top,
65 Bottom,
66}
67
68#[derive(Debug, Error)]
69#[error("not a valid position: {pos}")]
70pub struct PositionParseError {
71 pos: String,
72}
73
74impl FromStr for Position {
75 type Err = PositionParseError;
76
77 fn from_str(s: &str) -> Result<Self, Self::Err> {
78 match s {
79 "left" => Ok(Self::Left),
80 "right" => Ok(Self::Right),
81 "top" => Ok(Self::Top),
82 "bottom" => Ok(Self::Bottom),
83 _ => Err(PositionParseError { pos: s.into() }),
84 }
85 }
86}
87
88impl Display for Position {
89 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
90 write!(
91 f,
92 "{}",
93 match self {
94 Position::Left => "left",
95 Position::Right => "right",
96 Position::Top => "top",
97 Position::Bottom => "bottom",
98 }
99 )
100 }
101}
102
103impl TryFrom<&str> for Position {
104 type Error = ();
105
106 fn try_from(s: &str) -> Result<Self, Self::Error> {
107 match s {
108 "left" => Ok(Position::Left),
109 "right" => Ok(Position::Right),
110 "top" => Ok(Position::Top),
111 "bottom" => Ok(Position::Bottom),
112 _ => Err(()),
113 }
114 }
115}
116
117#[derive(Debug, Eq, PartialEq, Clone, Serialize, Deserialize)]
118pub struct ClientConfig {
119 pub hostname: Option<String>,
121 pub fix_ips: Vec<IpAddr>,
123 pub port: u16,
125 pub pos: Position,
127 pub cmd: Option<String>,
129}
130
131impl Default for ClientConfig {
132 fn default() -> Self {
133 Self {
134 port: DEFAULT_PORT,
135 hostname: Default::default(),
136 fix_ips: Default::default(),
137 pos: Default::default(),
138 cmd: None,
139 }
140 }
141}
142
143pub type ClientHandle = u64;
144
145#[derive(Debug, Default, Clone, Serialize, Deserialize)]
146pub struct ClientState {
147 pub active: bool,
149 pub active_addr: Option<SocketAddr>,
153 pub alive: bool,
155 pub dns_ips: Vec<IpAddr>,
157 pub ips: HashSet<IpAddr>,
161 pub has_pressed_keys: bool,
163 pub resolving: bool,
165}
166
167#[derive(Debug, Clone, Serialize, Deserialize)]
168pub enum FrontendEvent {
169 Changed(ClientHandle),
171 Created(ClientHandle, ClientConfig, ClientState),
173 NoSuchClient(ClientHandle),
175 State(ClientHandle, ClientConfig, ClientState),
177 Deleted(ClientHandle),
179 PortChanged(u16, Option<String>),
181 Enumerate(Vec<(ClientHandle, ClientConfig, ClientState)>),
183 Error(String),
185 CaptureStatus(Status),
187 EmulationStatus(Status),
189}
190
191#[derive(Debug, Eq, PartialEq, Clone, Serialize, Deserialize)]
192pub enum FrontendRequest {
193 Activate(ClientHandle, bool),
195 Create,
197 ChangePort(u16),
199 Delete(ClientHandle),
201 Enumerate(),
203 ResolveDns(ClientHandle),
205 UpdateHostname(ClientHandle, Option<String>),
207 UpdatePort(ClientHandle, u16),
209 UpdatePosition(ClientHandle, Position),
211 UpdateFixIps(ClientHandle, Vec<IpAddr>),
213 GetState(ClientHandle),
215 EnableCapture,
217 EnableEmulation,
219 Sync,
221}
222
223#[derive(Clone, Copy, Debug, Default, Serialize, Deserialize)]
224pub enum Status {
225 #[default]
226 Disabled,
227 Enabled,
228}
229
230impl From<Status> for bool {
231 fn from(status: Status) -> Self {
232 match status {
233 Status::Enabled => true,
234 Status::Disabled => false,
235 }
236 }
237}
238
239#[cfg(unix)]
240const LAN_MOUSE_SOCKET_NAME: &str = "lan-mouse-socket.sock";
241
242#[derive(Debug, Error)]
243pub enum SocketPathError {
244 #[error("could not determine $XDG_RUNTIME_DIR: `{0}`")]
245 XdgRuntimeDirNotFound(VarError),
246 #[error("could not determine $HOME: `{0}`")]
247 HomeDirNotFound(VarError),
248}
249
250#[cfg(all(unix, not(target_os = "macos")))]
251pub fn default_socket_path() -> Result<PathBuf, SocketPathError> {
252 let xdg_runtime_dir =
253 env::var("XDG_RUNTIME_DIR").map_err(SocketPathError::XdgRuntimeDirNotFound)?;
254 Ok(Path::new(xdg_runtime_dir.as_str()).join(LAN_MOUSE_SOCKET_NAME))
255}
256
257#[cfg(all(unix, target_os = "macos"))]
258pub fn default_socket_path() -> Result<PathBuf, SocketPathError> {
259 let home = env::var("HOME").map_err(SocketPathError::HomeDirNotFound)?;
260 Ok(Path::new(home.as_str())
261 .join("Library")
262 .join("Caches")
263 .join(LAN_MOUSE_SOCKET_NAME))
264}