mirror of
https://github.com/feschber/lan-mouse.git
synced 2026-04-18 09:31:28 +03:00
Separate config state (#118)
* change internal api * frontend now keeps and more correctly reflects backend state
This commit is contained in:
committed by
GitHub
parent
1e4312b3ce
commit
5318f5a02d
@@ -22,7 +22,7 @@ anyhow = "1.0.71"
|
|||||||
log = "0.4.20"
|
log = "0.4.20"
|
||||||
env_logger = "0.11.3"
|
env_logger = "0.11.3"
|
||||||
serde_json = "1.0.107"
|
serde_json = "1.0.107"
|
||||||
tokio = {version = "1.32.0", features = ["io-util", "macros", "net", "rt", "sync", "signal"] }
|
tokio = {version = "1.32.0", features = ["io-util", "io-std", "macros", "net", "rt", "sync", "signal"] }
|
||||||
async-trait = "0.1.73"
|
async-trait = "0.1.73"
|
||||||
futures-core = "0.3.28"
|
futures-core = "0.3.28"
|
||||||
futures = "0.3.28"
|
futures = "0.3.28"
|
||||||
|
|||||||
@@ -119,7 +119,7 @@ async fn update_barriers(
|
|||||||
.set_pointer_barriers(session, &barriers, zones.zone_set())
|
.set_pointer_barriers(session, &barriers, zones.zone_set())
|
||||||
.await?;
|
.await?;
|
||||||
let response = response.response()?;
|
let response = response.response()?;
|
||||||
log::info!("{response:?}");
|
log::debug!("{response:?}");
|
||||||
Ok(id_map)
|
Ok(id_map)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -132,7 +132,7 @@ impl<'a> Drop for LibeiInputCapture<'a> {
|
|||||||
async fn create_session<'a>(
|
async fn create_session<'a>(
|
||||||
input_capture: &'a InputCapture<'a>,
|
input_capture: &'a InputCapture<'a>,
|
||||||
) -> Result<(Session<'a>, BitFlags<Capabilities>)> {
|
) -> Result<(Session<'a>, BitFlags<Capabilities>)> {
|
||||||
log::info!("creating input capture session");
|
log::debug!("creating input capture session");
|
||||||
let (session, capabilities) = loop {
|
let (session, capabilities) = loop {
|
||||||
match input_capture
|
match input_capture
|
||||||
.create_session(
|
.create_session(
|
||||||
@@ -154,7 +154,7 @@ async fn connect_to_eis(
|
|||||||
input_capture: &InputCapture<'_>,
|
input_capture: &InputCapture<'_>,
|
||||||
session: &Session<'_>,
|
session: &Session<'_>,
|
||||||
) -> Result<(ei::Context, EiConvertEventStream)> {
|
) -> Result<(ei::Context, EiConvertEventStream)> {
|
||||||
log::info!("connect_to_eis");
|
log::debug!("connect_to_eis");
|
||||||
let fd = input_capture.connect_to_eis(session).await?;
|
let fd = input_capture.connect_to_eis(session).await?;
|
||||||
|
|
||||||
// create unix stream from fd
|
// create unix stream from fd
|
||||||
@@ -270,7 +270,7 @@ impl<'a> LibeiInputCapture<'a> {
|
|||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
log::info!("enabling session");
|
log::debug!("enabling session");
|
||||||
input_capture.enable(&session).await?;
|
input_capture.enable(&session).await?;
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
|
|||||||
112
src/client.rs
112
src/client.rs
@@ -1,12 +1,16 @@
|
|||||||
use std::{
|
use std::{
|
||||||
collections::HashSet,
|
collections::HashSet,
|
||||||
|
error::Error,
|
||||||
fmt::Display,
|
fmt::Display,
|
||||||
net::{IpAddr, SocketAddr},
|
net::{IpAddr, SocketAddr},
|
||||||
|
str::FromStr,
|
||||||
};
|
};
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use slab::Slab;
|
use slab::Slab;
|
||||||
|
|
||||||
|
use crate::config::DEFAULT_PORT;
|
||||||
|
|
||||||
#[derive(Debug, Eq, Hash, PartialEq, Clone, Copy, Serialize, Deserialize)]
|
#[derive(Debug, Eq, Hash, PartialEq, Clone, Copy, Serialize, Deserialize)]
|
||||||
pub enum Position {
|
pub enum Position {
|
||||||
Left,
|
Left,
|
||||||
@@ -21,6 +25,33 @@ impl Default for Position {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct PositionParseError {
|
||||||
|
string: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for PositionParseError {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "not a valid position: {}", self.string)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Error for PositionParseError {}
|
||||||
|
|
||||||
|
impl FromStr for Position {
|
||||||
|
type Err = PositionParseError;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
match s {
|
||||||
|
"left" => Ok(Self::Left),
|
||||||
|
"right" => Ok(Self::Right),
|
||||||
|
"top" => Ok(Self::Top),
|
||||||
|
"bottom" => Ok(Self::Bottom),
|
||||||
|
_ => Err(PositionParseError { string: s.into() }),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Position {
|
impl Position {
|
||||||
pub fn opposite(&self) -> Self {
|
pub fn opposite(&self) -> Self {
|
||||||
match self {
|
match self {
|
||||||
@@ -62,21 +93,28 @@ impl TryFrom<&str> for Position {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Eq, PartialEq, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Eq, PartialEq, Clone, Serialize, Deserialize)]
|
||||||
pub struct Client {
|
pub struct ClientConfig {
|
||||||
/// hostname of this client
|
/// hostname of this client
|
||||||
pub hostname: Option<String>,
|
pub hostname: Option<String>,
|
||||||
/// fix ips, determined by the user
|
/// fix ips, determined by the user
|
||||||
pub fix_ips: Vec<IpAddr>,
|
pub fix_ips: Vec<IpAddr>,
|
||||||
/// all ip addresses associated with a particular client
|
|
||||||
/// e.g. Laptops usually have at least an ethernet and a wifi port
|
|
||||||
/// which have different ip addresses
|
|
||||||
pub ips: HashSet<IpAddr>,
|
|
||||||
/// both active_addr and addrs can be None / empty so port needs to be stored seperately
|
/// both active_addr and addrs can be None / empty so port needs to be stored seperately
|
||||||
pub port: u16,
|
pub port: u16,
|
||||||
/// position of a client on screen
|
/// position of a client on screen
|
||||||
pub pos: Position,
|
pub pos: Position,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Default for ClientConfig {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
port: DEFAULT_PORT,
|
||||||
|
hostname: Default::default(),
|
||||||
|
fix_ips: Default::default(),
|
||||||
|
pos: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub enum ClientEvent {
|
pub enum ClientEvent {
|
||||||
Create(ClientHandle, Position),
|
Create(ClientHandle, Position),
|
||||||
@@ -85,10 +123,8 @@ pub enum ClientEvent {
|
|||||||
|
|
||||||
pub type ClientHandle = u64;
|
pub type ClientHandle = u64;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
|
||||||
pub struct ClientState {
|
pub struct ClientState {
|
||||||
/// information about the client
|
|
||||||
pub client: Client,
|
|
||||||
/// events should be sent to and received from the client
|
/// events should be sent to and received from the client
|
||||||
pub active: bool,
|
pub active: bool,
|
||||||
/// `active` address of the client, used to send data to.
|
/// `active` address of the client, used to send data to.
|
||||||
@@ -97,12 +133,16 @@ pub struct ClientState {
|
|||||||
pub active_addr: Option<SocketAddr>,
|
pub active_addr: Option<SocketAddr>,
|
||||||
/// tracks whether or not the client is responding to pings
|
/// tracks whether or not the client is responding to pings
|
||||||
pub alive: bool,
|
pub alive: bool,
|
||||||
|
/// all ip addresses associated with a particular client
|
||||||
|
/// e.g. Laptops usually have at least an ethernet and a wifi port
|
||||||
|
/// which have different ip addresses
|
||||||
|
pub ips: HashSet<IpAddr>,
|
||||||
/// keys currently pressed by this client
|
/// keys currently pressed by this client
|
||||||
pub pressed_keys: HashSet<u32>,
|
pub pressed_keys: HashSet<u32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ClientManager {
|
pub struct ClientManager {
|
||||||
clients: Slab<ClientState>,
|
clients: Slab<(ClientConfig, ClientState)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for ClientManager {
|
impl Default for ClientManager {
|
||||||
@@ -118,32 +158,10 @@ impl ClientManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// add a new client to this manager
|
/// add a new client to this manager
|
||||||
pub fn add_client(
|
pub fn add_client(&mut self) -> ClientHandle {
|
||||||
&mut self,
|
let client_config = Default::default();
|
||||||
hostname: Option<String>,
|
let client_state = Default::default();
|
||||||
ips: HashSet<IpAddr>,
|
self.clients.insert((client_config, client_state)) as ClientHandle
|
||||||
port: u16,
|
|
||||||
pos: Position,
|
|
||||||
active: bool,
|
|
||||||
) -> ClientHandle {
|
|
||||||
// store fix ip addresses
|
|
||||||
let fix_ips = ips.iter().cloned().collect();
|
|
||||||
|
|
||||||
let client_state = ClientState {
|
|
||||||
client: Client {
|
|
||||||
hostname,
|
|
||||||
fix_ips,
|
|
||||||
ips,
|
|
||||||
port,
|
|
||||||
pos,
|
|
||||||
},
|
|
||||||
active,
|
|
||||||
active_addr: None,
|
|
||||||
alive: false,
|
|
||||||
pressed_keys: HashSet::new(),
|
|
||||||
};
|
|
||||||
|
|
||||||
self.clients.insert(client_state) as ClientHandle
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// find a client by its address
|
/// find a client by its address
|
||||||
@@ -152,8 +170,8 @@ impl ClientManager {
|
|||||||
// time this is likely faster than using a HashMap
|
// time this is likely faster than using a HashMap
|
||||||
self.clients
|
self.clients
|
||||||
.iter()
|
.iter()
|
||||||
.find_map(|(k, c)| {
|
.find_map(|(k, (_, s))| {
|
||||||
if c.active && c.client.ips.contains(&addr.ip()) {
|
if s.active && s.ips.contains(&addr.ip()) {
|
||||||
Some(k)
|
Some(k)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
@@ -165,8 +183,8 @@ impl ClientManager {
|
|||||||
pub fn find_client(&self, pos: Position) -> Option<ClientHandle> {
|
pub fn find_client(&self, pos: Position) -> Option<ClientHandle> {
|
||||||
self.clients
|
self.clients
|
||||||
.iter()
|
.iter()
|
||||||
.find_map(|(k, c)| {
|
.find_map(|(k, (c, s))| {
|
||||||
if c.active && c.client.pos == pos {
|
if s.active && c.pos == pos {
|
||||||
Some(k)
|
Some(k)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
@@ -176,28 +194,30 @@ impl ClientManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// remove a client from the list
|
/// remove a client from the list
|
||||||
pub fn remove_client(&mut self, client: ClientHandle) -> Option<ClientState> {
|
pub fn remove_client(&mut self, client: ClientHandle) -> Option<(ClientConfig, ClientState)> {
|
||||||
// remove id from occupied ids
|
// remove id from occupied ids
|
||||||
self.clients.try_remove(client as usize)
|
self.clients.try_remove(client as usize)
|
||||||
}
|
}
|
||||||
|
|
||||||
// returns an immutable reference to the client state corresponding to `client`
|
// returns an immutable reference to the client state corresponding to `client`
|
||||||
pub fn get(&self, client: ClientHandle) -> Option<&ClientState> {
|
pub fn get(&self, handle: ClientHandle) -> Option<&(ClientConfig, ClientState)> {
|
||||||
self.clients.get(client as usize)
|
self.clients.get(handle as usize)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// returns a mutable reference to the client state corresponding to `client`
|
/// returns a mutable reference to the client state corresponding to `client`
|
||||||
pub fn get_mut(&mut self, client: ClientHandle) -> Option<&mut ClientState> {
|
pub fn get_mut(&mut self, handle: ClientHandle) -> Option<&mut (ClientConfig, ClientState)> {
|
||||||
self.clients.get_mut(client as usize)
|
self.clients.get_mut(handle as usize)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_client_states(&self) -> impl Iterator<Item = (ClientHandle, &ClientState)> {
|
pub fn get_client_states(
|
||||||
|
&self,
|
||||||
|
) -> impl Iterator<Item = (ClientHandle, &(ClientConfig, ClientState))> {
|
||||||
self.clients.iter().map(|(k, v)| (k as ClientHandle, v))
|
self.clients.iter().map(|(k, v)| (k as ClientHandle, v))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_client_states_mut(
|
pub fn get_client_states_mut(
|
||||||
&mut self,
|
&mut self,
|
||||||
) -> impl Iterator<Item = (ClientHandle, &mut ClientState)> {
|
) -> impl Iterator<Item = (ClientHandle, &mut (ClientConfig, ClientState))> {
|
||||||
self.clients.iter_mut().map(|(k, v)| (k as ClientHandle, v))
|
self.clients.iter_mut().map(|(k, v)| (k as ClientHandle, v))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
use std::{cmp::min, io::ErrorKind, str, time::Duration};
|
use std::{cmp::min, io::ErrorKind, net::IpAddr, str, time::Duration};
|
||||||
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
use std::{
|
use std::{
|
||||||
@@ -23,7 +23,7 @@ use tokio::net::TcpStream;
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
client::{Client, ClientHandle, Position},
|
client::{ClientConfig, ClientHandle, ClientState, Position},
|
||||||
config::{Config, Frontend},
|
config::{Config, Frontend},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -85,10 +85,10 @@ pub fn wait_for_service() -> Result<std::net::TcpStream> {
|
|||||||
|
|
||||||
#[derive(Debug, Eq, PartialEq, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Eq, PartialEq, Clone, Serialize, Deserialize)]
|
||||||
pub enum FrontendRequest {
|
pub enum FrontendRequest {
|
||||||
/// add a new client
|
|
||||||
Create(Option<String>, u16, Position),
|
|
||||||
/// activate/deactivate client
|
/// activate/deactivate client
|
||||||
Activate(ClientHandle, bool),
|
Activate(ClientHandle, bool),
|
||||||
|
/// add a new client
|
||||||
|
Create,
|
||||||
/// change the listen port (recreate udp listener)
|
/// change the listen port (recreate udp listener)
|
||||||
ChangePort(u16),
|
ChangePort(u16),
|
||||||
/// remove a client
|
/// remove a client
|
||||||
@@ -97,24 +97,30 @@ pub enum FrontendRequest {
|
|||||||
Enumerate(),
|
Enumerate(),
|
||||||
/// service shutdown
|
/// service shutdown
|
||||||
Terminate(),
|
Terminate(),
|
||||||
/// update a client (hostname, port, position)
|
/// update hostname
|
||||||
Update(ClientHandle, Option<String>, u16, Position),
|
UpdateHostname(ClientHandle, Option<String>),
|
||||||
|
/// update port
|
||||||
|
UpdatePort(ClientHandle, u16),
|
||||||
|
/// update position
|
||||||
|
UpdatePosition(ClientHandle, Position),
|
||||||
|
/// update fix-ips
|
||||||
|
UpdateFixIps(ClientHandle, Vec<IpAddr>),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub enum FrontendEvent {
|
pub enum FrontendEvent {
|
||||||
/// the given client was activated
|
|
||||||
Activated(ClientHandle, bool),
|
|
||||||
/// a client was created
|
/// a client was created
|
||||||
Created(ClientHandle, Client),
|
Created(ClientHandle, ClientConfig, ClientState),
|
||||||
/// a client was updated
|
/// a client was updated
|
||||||
Updated(ClientHandle, Client),
|
Updated(ClientHandle, ClientConfig),
|
||||||
|
/// state changed
|
||||||
|
StateChange(ClientHandle, ClientState),
|
||||||
/// the client was deleted
|
/// the client was deleted
|
||||||
Deleted(ClientHandle),
|
Deleted(ClientHandle),
|
||||||
/// new port, reason of failure (if failed)
|
/// new port, reason of failure (if failed)
|
||||||
PortChanged(u16, Option<String>),
|
PortChanged(u16, Option<String>),
|
||||||
/// list of all clients, used for initial state synchronization
|
/// list of all clients, used for initial state synchronization
|
||||||
Enumerate(Vec<(ClientHandle, Client, bool)>),
|
Enumerate(Vec<(ClientHandle, ClientConfig, ClientState)>),
|
||||||
/// an error occured
|
/// an error occured
|
||||||
Error(String),
|
Error(String),
|
||||||
}
|
}
|
||||||
@@ -229,6 +235,7 @@ impl FrontendListener {
|
|||||||
let len = payload.len().to_be_bytes();
|
let len = payload.len().to_be_bytes();
|
||||||
log::debug!("json: {json}, len: {}", payload.len());
|
log::debug!("json: {json}, len: {}", payload.len());
|
||||||
|
|
||||||
|
log::debug!("broadcasting event to streams: {:?}", self.tx_streams);
|
||||||
let mut keep = vec![];
|
let mut keep = vec![];
|
||||||
// TODO do simultaneously
|
// TODO do simultaneously
|
||||||
for tx in self.tx_streams.iter_mut() {
|
for tx in self.tx_streams.iter_mut() {
|
||||||
|
|||||||
@@ -1,240 +1,347 @@
|
|||||||
use anyhow::{anyhow, Context, Result};
|
use anyhow::{anyhow, Result};
|
||||||
|
use tokio::{
|
||||||
use std::{
|
io::{AsyncBufReadExt, AsyncReadExt, AsyncWriteExt, BufReader},
|
||||||
io::{ErrorKind, Read, Write},
|
task::LocalSet,
|
||||||
str::SplitWhitespace,
|
|
||||||
thread,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{client::Position, config::DEFAULT_PORT};
|
#[cfg(windows)]
|
||||||
|
use tokio::net::tcp::{ReadHalf, WriteHalf};
|
||||||
|
#[cfg(unix)]
|
||||||
|
use tokio::net::unix::{ReadHalf, WriteHalf};
|
||||||
|
|
||||||
|
use std::io::{self, Write};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
client::{ClientConfig, ClientHandle, ClientState},
|
||||||
|
config::DEFAULT_PORT,
|
||||||
|
};
|
||||||
|
|
||||||
|
use self::command::{Command, CommandType};
|
||||||
|
|
||||||
use super::{FrontendEvent, FrontendRequest};
|
use super::{FrontendEvent, FrontendRequest};
|
||||||
|
|
||||||
|
mod command;
|
||||||
|
|
||||||
pub fn run() -> Result<()> {
|
pub fn run() -> Result<()> {
|
||||||
let Ok(mut tx) = super::wait_for_service() else {
|
let Ok(stream) = super::wait_for_service() else {
|
||||||
return Err(anyhow!("Could not connect to lan-mouse-socket"));
|
return Err(anyhow!("Could not connect to lan-mouse-socket"));
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut rx = tx.try_clone()?;
|
let runtime = tokio::runtime::Builder::new_current_thread()
|
||||||
|
.enable_io()
|
||||||
|
.enable_time()
|
||||||
|
.build()?;
|
||||||
|
runtime.block_on(LocalSet::new().run_until(async move {
|
||||||
|
stream.set_nonblocking(true)?;
|
||||||
|
#[cfg(unix)]
|
||||||
|
let mut stream = tokio::net::UnixStream::from_std(stream)?;
|
||||||
|
#[cfg(windows)]
|
||||||
|
let mut stream = tokio::net::TcpStream::from_std(stream)?;
|
||||||
|
let (rx, tx) = stream.split();
|
||||||
|
|
||||||
let reader = thread::Builder::new()
|
let mut cli = Cli::new(rx, tx);
|
||||||
.name("cli-frontend".to_string())
|
cli.run().await
|
||||||
.spawn(move || {
|
}))?;
|
||||||
// all further prompts
|
|
||||||
prompt();
|
|
||||||
loop {
|
|
||||||
let mut buf = String::new();
|
|
||||||
match std::io::stdin().read_line(&mut buf) {
|
|
||||||
Ok(0) => return,
|
|
||||||
Ok(len) => {
|
|
||||||
if let Some(events) = parse_cmd(buf, len) {
|
|
||||||
for event in events.iter() {
|
|
||||||
let json = serde_json::to_string(&event).unwrap();
|
|
||||||
let bytes = json.as_bytes();
|
|
||||||
let len = bytes.len().to_be_bytes();
|
|
||||||
if let Err(e) = tx.write(&len) {
|
|
||||||
log::error!("error sending message: {e}");
|
|
||||||
};
|
|
||||||
if let Err(e) = tx.write(bytes) {
|
|
||||||
log::error!("error sending message: {e}");
|
|
||||||
};
|
|
||||||
if *event == FrontendRequest::Terminate() {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// prompt is printed after the server response is received
|
|
||||||
} else {
|
|
||||||
prompt();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
if e.kind() != ErrorKind::UnexpectedEof {
|
|
||||||
log::error!("error reading from stdin: {e}");
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})?;
|
|
||||||
|
|
||||||
let _ = thread::Builder::new()
|
|
||||||
.name("cli-frontend-notify".to_string())
|
|
||||||
.spawn(move || {
|
|
||||||
loop {
|
|
||||||
// read len
|
|
||||||
let mut len = [0u8; 8];
|
|
||||||
match rx.read_exact(&mut len) {
|
|
||||||
Ok(()) => (),
|
|
||||||
Err(e) if e.kind() == ErrorKind::UnexpectedEof => break,
|
|
||||||
Err(e) => break log::error!("{e}"),
|
|
||||||
};
|
|
||||||
let len = usize::from_be_bytes(len);
|
|
||||||
|
|
||||||
// read payload
|
|
||||||
let mut buf: Vec<u8> = vec![0u8; len];
|
|
||||||
match rx.read_exact(&mut buf[..len]) {
|
|
||||||
Ok(()) => (),
|
|
||||||
Err(e) if e.kind() == ErrorKind::UnexpectedEof => break,
|
|
||||||
Err(e) => break log::error!("{e}"),
|
|
||||||
};
|
|
||||||
|
|
||||||
let notify: FrontendEvent = match serde_json::from_slice(&buf) {
|
|
||||||
Ok(n) => n,
|
|
||||||
Err(e) => break log::error!("{e}"),
|
|
||||||
};
|
|
||||||
match notify {
|
|
||||||
FrontendEvent::Activated(handle, active) => {
|
|
||||||
if active {
|
|
||||||
log::info!("client {handle} activated");
|
|
||||||
} else {
|
|
||||||
log::info!("client {handle} deactivated");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
FrontendEvent::Created(handle, client) => {
|
|
||||||
let port = client.port;
|
|
||||||
let pos = client.pos;
|
|
||||||
let hostname = client.hostname.as_deref().unwrap_or("");
|
|
||||||
log::info!("new client ({handle}): {hostname}:{port} - {pos}");
|
|
||||||
}
|
|
||||||
FrontendEvent::Updated(handle, client) => {
|
|
||||||
let port = client.port;
|
|
||||||
let pos = client.pos;
|
|
||||||
let hostname = client.hostname.as_deref().unwrap_or("");
|
|
||||||
log::info!("client ({handle}) updated: {hostname}:{port} - {pos}");
|
|
||||||
}
|
|
||||||
FrontendEvent::Deleted(client) => {
|
|
||||||
log::info!("client ({client}) deleted.");
|
|
||||||
}
|
|
||||||
FrontendEvent::Error(e) => {
|
|
||||||
log::warn!("{e}");
|
|
||||||
}
|
|
||||||
FrontendEvent::Enumerate(clients) => {
|
|
||||||
for (handle, client, active) in clients.into_iter() {
|
|
||||||
log::info!(
|
|
||||||
"client ({}) [{}]: active: {}, associated addresses: [{}]",
|
|
||||||
handle,
|
|
||||||
client.hostname.as_deref().unwrap_or(""),
|
|
||||||
if active { "yes" } else { "no" },
|
|
||||||
client
|
|
||||||
.ips
|
|
||||||
.into_iter()
|
|
||||||
.map(|a| a.to_string())
|
|
||||||
.collect::<Vec<String>>()
|
|
||||||
.join(", ")
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
FrontendEvent::PortChanged(port, msg) => match msg {
|
|
||||||
Some(msg) => log::info!("could not change port: {msg}"),
|
|
||||||
None => log::info!("port changed: {port}"),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
prompt();
|
|
||||||
}
|
|
||||||
})?;
|
|
||||||
match reader.join() {
|
|
||||||
Ok(_) => {}
|
|
||||||
Err(e) => {
|
|
||||||
let msg = match (e.downcast_ref::<&str>(), e.downcast_ref::<String>()) {
|
|
||||||
(Some(&s), _) => s,
|
|
||||||
(_, Some(s)) => s,
|
|
||||||
_ => "no panic info",
|
|
||||||
};
|
|
||||||
log::error!("reader thread paniced: {msg}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn prompt() {
|
struct Cli<'a> {
|
||||||
|
clients: Vec<(ClientHandle, ClientConfig, ClientState)>,
|
||||||
|
rx: ReadHalf<'a>,
|
||||||
|
tx: WriteHalf<'a>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Cli<'a> {
|
||||||
|
fn new(rx: ReadHalf<'a>, tx: WriteHalf<'a>) -> Cli<'a> {
|
||||||
|
Self {
|
||||||
|
clients: vec![],
|
||||||
|
rx,
|
||||||
|
tx,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn run(&mut self) -> Result<()> {
|
||||||
|
let stdin = tokio::io::stdin();
|
||||||
|
let stdin = BufReader::new(stdin);
|
||||||
|
let mut stdin = stdin.lines();
|
||||||
|
|
||||||
|
/* initial state sync */
|
||||||
|
let request = FrontendRequest::Enumerate();
|
||||||
|
self.send_request(request).await?;
|
||||||
|
|
||||||
|
self.clients = loop {
|
||||||
|
let event = self.await_event().await?;
|
||||||
|
if let FrontendEvent::Enumerate(clients) = event {
|
||||||
|
break clients;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
loop {
|
||||||
|
prompt()?;
|
||||||
|
tokio::select! {
|
||||||
|
line = stdin.next_line() => {
|
||||||
|
let Some(line) = line? else {
|
||||||
|
break Ok(());
|
||||||
|
};
|
||||||
|
let cmd: Command = match line.parse() {
|
||||||
|
Ok(cmd) => cmd,
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!("{e}");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
self.execute(cmd).await?;
|
||||||
|
}
|
||||||
|
event = self.await_event() => {
|
||||||
|
let event = event?;
|
||||||
|
self.handle_event(event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn execute(&mut self, cmd: Command) -> Result<()> {
|
||||||
|
match cmd {
|
||||||
|
Command::None => {}
|
||||||
|
Command::Connect(pos, host, port) => {
|
||||||
|
let request = FrontendRequest::Create;
|
||||||
|
self.send_request(request).await?;
|
||||||
|
let handle = loop {
|
||||||
|
let event = self.await_event().await?;
|
||||||
|
match event {
|
||||||
|
FrontendEvent::Created(h, c, s) => {
|
||||||
|
self.clients.push((h, c, s));
|
||||||
|
break h;
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
self.handle_event(event);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
for request in [
|
||||||
|
FrontendRequest::UpdateHostname(handle, Some(host.clone())),
|
||||||
|
FrontendRequest::UpdatePort(handle, port.unwrap_or(DEFAULT_PORT)),
|
||||||
|
FrontendRequest::UpdatePosition(handle, pos),
|
||||||
|
] {
|
||||||
|
self.send_request(request).await?;
|
||||||
|
loop {
|
||||||
|
let event = self.await_event().await?;
|
||||||
|
self.handle_event(event.clone());
|
||||||
|
if let FrontendEvent::Updated(_, _) = event {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Command::Disconnect(id) => {
|
||||||
|
self.send_request(FrontendRequest::Delete(id)).await?;
|
||||||
|
loop {
|
||||||
|
let event = self.await_event().await?;
|
||||||
|
self.handle_event(event.clone());
|
||||||
|
if let FrontendEvent::Deleted(_) = event {
|
||||||
|
self.handle_event(event);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Command::Activate(id) => {
|
||||||
|
self.send_request(FrontendRequest::Activate(id, true))
|
||||||
|
.await?;
|
||||||
|
loop {
|
||||||
|
let event = self.await_event().await?;
|
||||||
|
self.handle_event(event.clone());
|
||||||
|
if let FrontendEvent::StateChange(_, _) = event {
|
||||||
|
self.handle_event(event);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Command::Deactivate(id) => {
|
||||||
|
self.send_request(FrontendRequest::Activate(id, false))
|
||||||
|
.await?;
|
||||||
|
loop {
|
||||||
|
let event = self.await_event().await?;
|
||||||
|
self.handle_event(event.clone());
|
||||||
|
if let FrontendEvent::StateChange(_, _) = event {
|
||||||
|
self.handle_event(event);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Command::List => {
|
||||||
|
self.send_request(FrontendRequest::Enumerate()).await?;
|
||||||
|
loop {
|
||||||
|
let event = self.await_event().await?;
|
||||||
|
self.handle_event(event.clone());
|
||||||
|
if let FrontendEvent::Enumerate(_) = event {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Command::SetHost(handle, host) => {
|
||||||
|
let request = FrontendRequest::UpdateHostname(handle, Some(host.clone()));
|
||||||
|
self.send_request(request).await?;
|
||||||
|
loop {
|
||||||
|
let event = self.await_event().await?;
|
||||||
|
self.handle_event(event.clone());
|
||||||
|
if let FrontendEvent::Updated(_, _) = event {
|
||||||
|
self.handle_event(event);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Command::SetPort(handle, port) => {
|
||||||
|
let request = FrontendRequest::UpdatePort(handle, port.unwrap_or(DEFAULT_PORT));
|
||||||
|
self.send_request(request).await?;
|
||||||
|
loop {
|
||||||
|
let event = self.await_event().await?;
|
||||||
|
self.handle_event(event.clone());
|
||||||
|
if let FrontendEvent::Updated(_, _) = event {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Command::Help => {
|
||||||
|
for cmd_type in [
|
||||||
|
CommandType::List,
|
||||||
|
CommandType::Connect,
|
||||||
|
CommandType::Disconnect,
|
||||||
|
CommandType::Activate,
|
||||||
|
CommandType::Deactivate,
|
||||||
|
CommandType::SetHost,
|
||||||
|
CommandType::SetPort,
|
||||||
|
] {
|
||||||
|
eprintln!("{}", cmd_type.usage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn find_mut(
|
||||||
|
&mut self,
|
||||||
|
handle: ClientHandle,
|
||||||
|
) -> Option<&mut (ClientHandle, ClientConfig, ClientState)> {
|
||||||
|
self.clients.iter_mut().find(|(h, _, _)| *h == handle)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn remove(
|
||||||
|
&mut self,
|
||||||
|
handle: ClientHandle,
|
||||||
|
) -> Option<(ClientHandle, ClientConfig, ClientState)> {
|
||||||
|
let idx = self.clients.iter().position(|(h, _, _)| *h == handle);
|
||||||
|
idx.map(|i| self.clients.swap_remove(i))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_event(&mut self, event: FrontendEvent) {
|
||||||
|
match event {
|
||||||
|
FrontendEvent::Created(h, c, s) => {
|
||||||
|
eprint!("client added ({h}): ");
|
||||||
|
print_config(&c);
|
||||||
|
eprint!(" ");
|
||||||
|
print_state(&s);
|
||||||
|
eprintln!();
|
||||||
|
self.clients.push((h, c, s));
|
||||||
|
}
|
||||||
|
FrontendEvent::Updated(h, c) => {
|
||||||
|
if let Some((_, config, _)) = self.find_mut(h) {
|
||||||
|
let old_host = config.hostname.clone().unwrap_or("\"\"".into());
|
||||||
|
let new_host = c.hostname.clone().unwrap_or("\"\"".into());
|
||||||
|
if old_host != new_host {
|
||||||
|
eprintln!(
|
||||||
|
"client {h}: hostname updated ({} -> {})",
|
||||||
|
old_host, new_host
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if config.port != c.port {
|
||||||
|
eprintln!("client {h} changed port: {} -> {}", config.port, c.port);
|
||||||
|
}
|
||||||
|
if config.fix_ips != c.fix_ips {
|
||||||
|
eprintln!("client {h} ips updated: {:?}", c.fix_ips)
|
||||||
|
}
|
||||||
|
*config = c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
FrontendEvent::StateChange(h, s) => {
|
||||||
|
if let Some((_, _, state)) = self.find_mut(h) {
|
||||||
|
if state.active ^ s.active {
|
||||||
|
eprintln!(
|
||||||
|
"client {h} {}",
|
||||||
|
if s.active { "activated" } else { "deactivated" }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
*state = s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
FrontendEvent::Deleted(h) => {
|
||||||
|
if let Some((h, c, _)) = self.remove(h) {
|
||||||
|
eprint!("client {h} removed (");
|
||||||
|
print_config(&c);
|
||||||
|
eprintln!(")");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
FrontendEvent::PortChanged(p, e) => {
|
||||||
|
if let Some(e) = e {
|
||||||
|
eprintln!("failed to change port: {e}");
|
||||||
|
} else {
|
||||||
|
eprintln!("changed port to {p}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
FrontendEvent::Enumerate(clients) => {
|
||||||
|
self.clients = clients;
|
||||||
|
self.print_clients();
|
||||||
|
}
|
||||||
|
FrontendEvent::Error(e) => {
|
||||||
|
eprintln!("ERROR: {e}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn print_clients(&mut self) {
|
||||||
|
for (h, c, s) in self.clients.iter() {
|
||||||
|
eprint!("client {h}: ");
|
||||||
|
print_config(c);
|
||||||
|
eprint!(" ");
|
||||||
|
print_state(s);
|
||||||
|
eprintln!();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn send_request(&mut self, request: FrontendRequest) -> io::Result<()> {
|
||||||
|
let json = serde_json::to_string(&request).unwrap();
|
||||||
|
let bytes = json.as_bytes();
|
||||||
|
let len = bytes.len();
|
||||||
|
self.tx.write_u64(len as u64).await?;
|
||||||
|
self.tx.write_all(bytes).await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn await_event(&mut self) -> Result<FrontendEvent> {
|
||||||
|
let len = self.rx.read_u64().await?;
|
||||||
|
let mut buf = vec![0u8; len as usize];
|
||||||
|
self.rx.read_exact(&mut buf).await?;
|
||||||
|
let event: FrontendEvent = serde_json::from_slice(&buf)?;
|
||||||
|
Ok(event)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn prompt() -> io::Result<()> {
|
||||||
eprint!("lan-mouse > ");
|
eprint!("lan-mouse > ");
|
||||||
std::io::stderr().flush().unwrap();
|
std::io::stderr().flush()?;
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_cmd(s: String, len: usize) -> Option<Vec<FrontendRequest>> {
|
fn print_config(c: &ClientConfig) {
|
||||||
if len == 0 {
|
eprint!(
|
||||||
return Some(vec![FrontendRequest::Terminate()]);
|
"{}:{} ({}), ips: {:?}",
|
||||||
}
|
c.hostname.clone().unwrap_or("(no hostname)".into()),
|
||||||
let mut l = s.split_whitespace();
|
c.port,
|
||||||
let cmd = l.next()?;
|
c.pos,
|
||||||
let res = match cmd {
|
c.fix_ips
|
||||||
"help" => {
|
);
|
||||||
log::info!("list list clients");
|
|
||||||
log::info!("connect <host> left|right|top|bottom [port] add a new client");
|
|
||||||
log::info!("disconnect <client> remove a client");
|
|
||||||
log::info!("activate <client> activate a client");
|
|
||||||
log::info!("deactivate <client> deactivate a client");
|
|
||||||
log::info!("exit exit lan-mouse");
|
|
||||||
log::info!("setport <port> change port");
|
|
||||||
None
|
|
||||||
}
|
|
||||||
"exit" => return Some(vec![FrontendRequest::Terminate()]),
|
|
||||||
"list" => return Some(vec![FrontendRequest::Enumerate()]),
|
|
||||||
"connect" => Some(parse_connect(l)),
|
|
||||||
"disconnect" => Some(parse_disconnect(l)),
|
|
||||||
"activate" => Some(parse_activate(l)),
|
|
||||||
"deactivate" => Some(parse_deactivate(l)),
|
|
||||||
"setport" => Some(parse_port(l)),
|
|
||||||
_ => {
|
|
||||||
log::error!("unknown command: {s}");
|
|
||||||
None
|
|
||||||
}
|
|
||||||
};
|
|
||||||
match res {
|
|
||||||
Some(Ok(e)) => Some(e),
|
|
||||||
Some(Err(e)) => {
|
|
||||||
log::warn!("{e}");
|
|
||||||
None
|
|
||||||
}
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_connect(mut l: SplitWhitespace) -> Result<Vec<FrontendRequest>> {
|
fn print_state(s: &ClientState) {
|
||||||
let usage = "usage: connect <host> left|right|top|bottom [port]";
|
eprint!("active: {}, dns: {:?}", s.active, s.ips);
|
||||||
let host = l.next().context(usage)?.to_owned();
|
|
||||||
let pos = match l.next().context(usage)? {
|
|
||||||
"right" => Position::Right,
|
|
||||||
"top" => Position::Top,
|
|
||||||
"bottom" => Position::Bottom,
|
|
||||||
_ => Position::Left,
|
|
||||||
};
|
|
||||||
let port = if let Some(p) = l.next() {
|
|
||||||
p.parse()?
|
|
||||||
} else {
|
|
||||||
DEFAULT_PORT
|
|
||||||
};
|
|
||||||
Ok(vec![
|
|
||||||
FrontendRequest::Create(Some(host), port, pos),
|
|
||||||
FrontendRequest::Enumerate(),
|
|
||||||
])
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_disconnect(mut l: SplitWhitespace) -> Result<Vec<FrontendRequest>> {
|
|
||||||
let client = l.next().context("usage: disconnect <client_id>")?.parse()?;
|
|
||||||
Ok(vec![
|
|
||||||
FrontendRequest::Delete(client),
|
|
||||||
FrontendRequest::Enumerate(),
|
|
||||||
])
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_activate(mut l: SplitWhitespace) -> Result<Vec<FrontendRequest>> {
|
|
||||||
let client = l.next().context("usage: activate <client_id>")?.parse()?;
|
|
||||||
Ok(vec![
|
|
||||||
FrontendRequest::Activate(client, true),
|
|
||||||
FrontendRequest::Enumerate(),
|
|
||||||
])
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_deactivate(mut l: SplitWhitespace) -> Result<Vec<FrontendRequest>> {
|
|
||||||
let client = l.next().context("usage: deactivate <client_id>")?.parse()?;
|
|
||||||
Ok(vec![
|
|
||||||
FrontendRequest::Activate(client, false),
|
|
||||||
FrontendRequest::Enumerate(),
|
|
||||||
])
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_port(mut l: SplitWhitespace) -> Result<Vec<FrontendRequest>> {
|
|
||||||
let port = l.next().context("usage: setport <port>")?.parse()?;
|
|
||||||
Ok(vec![FrontendRequest::ChangePort(port)])
|
|
||||||
}
|
}
|
||||||
|
|||||||
153
src/frontend/cli/command.rs
Normal file
153
src/frontend/cli/command.rs
Normal file
@@ -0,0 +1,153 @@
|
|||||||
|
use std::{
|
||||||
|
fmt::Display,
|
||||||
|
str::{FromStr, SplitWhitespace},
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::client::{ClientHandle, Position};
|
||||||
|
|
||||||
|
pub(super) enum CommandType {
|
||||||
|
NoCommand,
|
||||||
|
Help,
|
||||||
|
Connect,
|
||||||
|
Disconnect,
|
||||||
|
Activate,
|
||||||
|
Deactivate,
|
||||||
|
List,
|
||||||
|
SetHost,
|
||||||
|
SetPort,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub(super) struct InvalidCommand {
|
||||||
|
cmd: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for InvalidCommand {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "invalid command: \"{}\"", self.cmd)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for CommandType {
|
||||||
|
type Err = InvalidCommand;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> std::prelude::v1::Result<Self, Self::Err> {
|
||||||
|
match s {
|
||||||
|
"connect" => Ok(Self::Connect),
|
||||||
|
"disconnect" => Ok(Self::Disconnect),
|
||||||
|
"activate" => Ok(Self::Activate),
|
||||||
|
"deactivate" => Ok(Self::Deactivate),
|
||||||
|
"list" => Ok(Self::List),
|
||||||
|
"set-host" => Ok(Self::SetHost),
|
||||||
|
"set-port" => Ok(Self::SetPort),
|
||||||
|
"help" => Ok(Self::Help),
|
||||||
|
_ => Err(InvalidCommand { cmd: s.to_string() }),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub(super) enum Command {
|
||||||
|
None,
|
||||||
|
Help,
|
||||||
|
Connect(Position, String, Option<u16>),
|
||||||
|
Disconnect(ClientHandle),
|
||||||
|
Activate(ClientHandle),
|
||||||
|
Deactivate(ClientHandle),
|
||||||
|
List,
|
||||||
|
SetHost(ClientHandle, String),
|
||||||
|
SetPort(ClientHandle, Option<u16>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CommandType {
|
||||||
|
pub(super) fn usage(&self) -> &'static str {
|
||||||
|
match self {
|
||||||
|
CommandType::Help => "help",
|
||||||
|
CommandType::NoCommand => "",
|
||||||
|
CommandType::Connect => "connect left|right|top|bottom <host> [<port>]",
|
||||||
|
CommandType::Disconnect => "disconnect <id>",
|
||||||
|
CommandType::Activate => "activate <id>",
|
||||||
|
CommandType::Deactivate => "deactivate <id>",
|
||||||
|
CommandType::List => "list",
|
||||||
|
CommandType::SetHost => "set-host <id> <host>",
|
||||||
|
CommandType::SetPort => "set-port <id> <host>",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) enum CommandParseError {
|
||||||
|
Usage(CommandType),
|
||||||
|
Invalid(InvalidCommand),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for CommandParseError {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
Self::Usage(cmd) => write!(f, "usage: {}", cmd.usage()),
|
||||||
|
Self::Invalid(cmd) => write!(f, "{}", cmd),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for Command {
|
||||||
|
type Err = CommandParseError;
|
||||||
|
|
||||||
|
fn from_str(cmd: &str) -> Result<Self, Self::Err> {
|
||||||
|
let mut args = cmd.split_whitespace();
|
||||||
|
let cmd_type: CommandType = match args.next() {
|
||||||
|
Some(c) => c.parse().map_err(CommandParseError::Invalid),
|
||||||
|
None => Ok(CommandType::NoCommand),
|
||||||
|
}?;
|
||||||
|
match cmd_type {
|
||||||
|
CommandType::Help => Ok(Command::Help),
|
||||||
|
CommandType::NoCommand => Ok(Command::None),
|
||||||
|
CommandType::Connect => parse_connect_cmd(args),
|
||||||
|
CommandType::Disconnect => parse_disconnect_cmd(args),
|
||||||
|
CommandType::Activate => parse_activate_cmd(args),
|
||||||
|
CommandType::Deactivate => parse_deactivate_cmd(args),
|
||||||
|
CommandType::List => Ok(Command::List),
|
||||||
|
CommandType::SetHost => parse_set_host(args),
|
||||||
|
CommandType::SetPort => parse_set_port(args),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_connect_cmd(mut args: SplitWhitespace<'_>) -> Result<Command, CommandParseError> {
|
||||||
|
const USAGE: CommandParseError = CommandParseError::Usage(CommandType::Connect);
|
||||||
|
let pos = args.next().ok_or(USAGE)?.parse().map_err(|_| USAGE)?;
|
||||||
|
let host = args.next().ok_or(USAGE)?.to_string();
|
||||||
|
let port = args.next().and_then(|p| p.parse().ok());
|
||||||
|
Ok(Command::Connect(pos, host, port))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_disconnect_cmd(mut args: SplitWhitespace<'_>) -> Result<Command, CommandParseError> {
|
||||||
|
const USAGE: CommandParseError = CommandParseError::Usage(CommandType::Disconnect);
|
||||||
|
let id = args.next().ok_or(USAGE)?.parse().map_err(|_| USAGE)?;
|
||||||
|
Ok(Command::Disconnect(id))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_activate_cmd(mut args: SplitWhitespace<'_>) -> Result<Command, CommandParseError> {
|
||||||
|
const USAGE: CommandParseError = CommandParseError::Usage(CommandType::Activate);
|
||||||
|
let id = args.next().ok_or(USAGE)?.parse().map_err(|_| USAGE)?;
|
||||||
|
Ok(Command::Activate(id))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_deactivate_cmd(mut args: SplitWhitespace<'_>) -> Result<Command, CommandParseError> {
|
||||||
|
const USAGE: CommandParseError = CommandParseError::Usage(CommandType::Deactivate);
|
||||||
|
let id = args.next().ok_or(USAGE)?.parse().map_err(|_| USAGE)?;
|
||||||
|
Ok(Command::Deactivate(id))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_set_host(mut args: SplitWhitespace<'_>) -> Result<Command, CommandParseError> {
|
||||||
|
const USAGE: CommandParseError = CommandParseError::Usage(CommandType::SetHost);
|
||||||
|
let id = args.next().ok_or(USAGE)?.parse().map_err(|_| USAGE)?;
|
||||||
|
let host = args.next().ok_or(USAGE)?.parse().map_err(|_| USAGE)?;
|
||||||
|
Ok(Command::SetHost(id, host))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_set_port(mut args: SplitWhitespace<'_>) -> Result<Command, CommandParseError> {
|
||||||
|
const USAGE: CommandParseError = CommandParseError::Usage(CommandType::SetPort);
|
||||||
|
let id = args.next().ok_or(USAGE)?.parse().map_err(|_| USAGE)?;
|
||||||
|
let port = args.next().and_then(|p| p.parse().ok());
|
||||||
|
Ok(Command::SetPort(id, port))
|
||||||
|
}
|
||||||
@@ -8,7 +8,7 @@ use std::{
|
|||||||
process, str,
|
process, str,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::frontend::gtk::window::Window;
|
use crate::frontend::{gtk::window::Window, FrontendRequest};
|
||||||
|
|
||||||
use adw::Application;
|
use adw::Application;
|
||||||
use gtk::{
|
use gtk::{
|
||||||
@@ -113,34 +113,35 @@ fn build_ui(app: &Application) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let window = Window::new(app);
|
let window = Window::new(app, tx);
|
||||||
window.imp().stream.borrow_mut().replace(tx);
|
window.request(FrontendRequest::Enumerate());
|
||||||
|
|
||||||
glib::spawn_future_local(clone!(@weak window => async move {
|
glib::spawn_future_local(clone!(@weak window => async move {
|
||||||
loop {
|
loop {
|
||||||
let notify = receiver.recv().await.unwrap_or_else(|_| process::exit(1));
|
let notify = receiver.recv().await.unwrap_or_else(|_| process::exit(1));
|
||||||
match notify {
|
match notify {
|
||||||
FrontendEvent::Activated(handle, active) => {
|
FrontendEvent::Created(handle, client, state) => {
|
||||||
window.activate_client(handle, active);
|
window.new_client(handle, client, state);
|
||||||
}
|
|
||||||
FrontendEvent::Created(handle, client) => {
|
|
||||||
window.new_client(handle, client, false);
|
|
||||||
},
|
|
||||||
FrontendEvent::Updated(handle, client) => {
|
|
||||||
window.update_client(handle, client);
|
|
||||||
}
|
|
||||||
FrontendEvent::Error(e) => {
|
|
||||||
window.show_toast(e.as_str());
|
|
||||||
},
|
},
|
||||||
FrontendEvent::Deleted(client) => {
|
FrontendEvent::Deleted(client) => {
|
||||||
window.delete_client(client);
|
window.delete_client(client);
|
||||||
}
|
}
|
||||||
|
FrontendEvent::Updated(handle, client) => {
|
||||||
|
window.update_client_config(handle, client);
|
||||||
|
}
|
||||||
|
FrontendEvent::StateChange(handle, state) => {
|
||||||
|
window.update_client_state(handle, state);
|
||||||
|
}
|
||||||
|
FrontendEvent::Error(e) => {
|
||||||
|
window.show_toast(e.as_str());
|
||||||
|
},
|
||||||
FrontendEvent::Enumerate(clients) => {
|
FrontendEvent::Enumerate(clients) => {
|
||||||
for (handle, client, active) in clients {
|
for (handle, client, state) in clients {
|
||||||
if window.client_idx(handle).is_some() {
|
if window.client_idx(handle).is_some() {
|
||||||
window.activate_client(handle, active);
|
window.update_client_config(handle, client);
|
||||||
window.update_client(handle, client);
|
window.update_client_state(handle, state);
|
||||||
} else {
|
} else {
|
||||||
window.new_client(handle, client, active);
|
window.new_client(handle, client, state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -3,20 +3,20 @@ mod imp;
|
|||||||
use adw::subclass::prelude::*;
|
use adw::subclass::prelude::*;
|
||||||
use gtk::glib::{self, Object};
|
use gtk::glib::{self, Object};
|
||||||
|
|
||||||
use crate::client::{Client, ClientHandle};
|
use crate::client::{ClientConfig, ClientHandle, ClientState};
|
||||||
|
|
||||||
glib::wrapper! {
|
glib::wrapper! {
|
||||||
pub struct ClientObject(ObjectSubclass<imp::ClientObject>);
|
pub struct ClientObject(ObjectSubclass<imp::ClientObject>);
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ClientObject {
|
impl ClientObject {
|
||||||
pub fn new(handle: ClientHandle, client: Client, active: bool) -> Self {
|
pub fn new(handle: ClientHandle, client: ClientConfig, state: ClientState) -> Self {
|
||||||
Object::builder()
|
Object::builder()
|
||||||
.property("handle", handle)
|
.property("handle", handle)
|
||||||
.property("hostname", client.hostname)
|
.property("hostname", client.hostname)
|
||||||
.property("port", client.port as u32)
|
.property("port", client.port as u32)
|
||||||
.property("position", client.pos.to_string())
|
.property("position", client.pos.to_string())
|
||||||
.property("active", active)
|
.property("active", state.active)
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,12 @@ mod imp;
|
|||||||
|
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
|
use std::os::unix::net::UnixStream;
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
use std::net::TcpStream;
|
||||||
|
|
||||||
use adw::prelude::*;
|
use adw::prelude::*;
|
||||||
use adw::subclass::prelude::*;
|
use adw::subclass::prelude::*;
|
||||||
use glib::{clone, Object};
|
use glib::{clone, Object};
|
||||||
@@ -12,7 +18,7 @@ use gtk::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
client::{Client, ClientHandle, Position},
|
client::{ClientConfig, ClientHandle, ClientState, Position},
|
||||||
config::DEFAULT_PORT,
|
config::DEFAULT_PORT,
|
||||||
frontend::{gtk::client_object::ClientObject, FrontendRequest},
|
frontend::{gtk::client_object::ClientObject, FrontendRequest},
|
||||||
};
|
};
|
||||||
@@ -27,8 +33,14 @@ glib::wrapper! {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Window {
|
impl Window {
|
||||||
pub(crate) fn new(app: &adw::Application) -> Self {
|
pub(crate) fn new(
|
||||||
Object::builder().property("application", app).build()
|
app: &adw::Application,
|
||||||
|
#[cfg(unix)] tx: UnixStream,
|
||||||
|
#[cfg(windows)] tx: TcpStream,
|
||||||
|
) -> Self {
|
||||||
|
let window: Self = Object::builder().property("application", app).build();
|
||||||
|
window.imp().stream.borrow_mut().replace(tx);
|
||||||
|
window
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn clients(&self) -> gio::ListStore {
|
pub fn clients(&self) -> gio::ListStore {
|
||||||
@@ -87,8 +99,8 @@ impl Window {
|
|||||||
row
|
row
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_client(&self, handle: ClientHandle, client: Client, active: bool) {
|
pub fn new_client(&self, handle: ClientHandle, client: ClientConfig, state: ClientState) {
|
||||||
let client = ClientObject::new(handle, client, active);
|
let client = ClientObject::new(handle, client, state);
|
||||||
self.clients().append(&client);
|
self.clients().append(&client);
|
||||||
self.set_placeholder_visible(false);
|
self.set_placeholder_visible(false);
|
||||||
}
|
}
|
||||||
@@ -115,7 +127,7 @@ impl Window {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_client(&self, handle: ClientHandle, client: Client) {
|
pub fn update_client_config(&self, handle: ClientHandle, client: ClientConfig) {
|
||||||
let Some(idx) = self.client_idx(handle) else {
|
let Some(idx) = self.client_idx(handle) else {
|
||||||
log::warn!("could not find client with handle {}", handle);
|
log::warn!("could not find client with handle {}", handle);
|
||||||
return;
|
return;
|
||||||
@@ -137,22 +149,23 @@ impl Window {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn activate_client(&self, handle: ClientHandle, active: bool) {
|
pub fn update_client_state(&self, handle: ClientHandle, state: ClientState) {
|
||||||
let Some(idx) = self.client_idx(handle) else {
|
let Some(idx) = self.client_idx(handle) else {
|
||||||
log::warn!("could not find client with handle {handle}");
|
log::warn!("could not find client with handle {}", handle);
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let client_object = self.clients().item(idx as u32).unwrap();
|
let client_object = self.clients().item(idx as u32).unwrap();
|
||||||
let client_object: &ClientObject = client_object.downcast_ref().unwrap();
|
let client_object: &ClientObject = client_object.downcast_ref().unwrap();
|
||||||
let data = client_object.get_data();
|
let data = client_object.get_data();
|
||||||
if data.active != active {
|
|
||||||
client_object.set_active(active);
|
if state.active != data.active {
|
||||||
log::debug!("set active to {active}");
|
client_object.set_active(state.active);
|
||||||
|
log::debug!("set active to {}", state.active);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn request_client_create(&self) {
|
pub fn request_client_create(&self) {
|
||||||
let event = FrontendRequest::Create(None, DEFAULT_PORT, Position::default());
|
let event = FrontendRequest::Create;
|
||||||
self.imp().set_port(DEFAULT_PORT);
|
self.imp().set_port(DEFAULT_PORT);
|
||||||
self.request(event);
|
self.request(event);
|
||||||
}
|
}
|
||||||
@@ -167,24 +180,21 @@ impl Window {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn request_client_update(&self, client: &ClientObject, active: bool) {
|
pub fn request_client_update(&self, client: &ClientObject, active: bool) {
|
||||||
|
let handle = client.handle();
|
||||||
let data = client.get_data();
|
let data = client.get_data();
|
||||||
let position = match Position::try_from(data.position.as_str()) {
|
let position = Position::try_from(data.position.as_str()).expect("invalid position");
|
||||||
Ok(pos) => pos,
|
|
||||||
_ => {
|
|
||||||
log::error!("invalid position: {}", data.position);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let hostname = data.hostname;
|
let hostname = data.hostname;
|
||||||
let port = data.port as u16;
|
let port = data.port as u16;
|
||||||
|
|
||||||
let event = FrontendRequest::Update(client.handle(), hostname, port, position);
|
for event in [
|
||||||
log::debug!("requesting update: {event:?}");
|
FrontendRequest::UpdateHostname(handle, hostname),
|
||||||
self.request(event);
|
FrontendRequest::UpdatePosition(handle, position),
|
||||||
|
FrontendRequest::UpdatePort(handle, port),
|
||||||
let event = FrontendRequest::Activate(client.handle(), active);
|
FrontendRequest::Activate(handle, active),
|
||||||
log::debug!("requesting activate: {event:?}");
|
] {
|
||||||
self.request(event);
|
log::debug!("requesting: {event:?}");
|
||||||
|
self.request(event);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn request_client_delete(&self, idx: u32) {
|
pub fn request_client_delete(&self, idx: u32) {
|
||||||
@@ -198,7 +208,7 @@ impl Window {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn request(&self, event: FrontendRequest) {
|
pub fn request(&self, event: FrontendRequest) {
|
||||||
let json = serde_json::to_string(&event).unwrap();
|
let json = serde_json::to_string(&event).unwrap();
|
||||||
log::debug!("requesting {json}");
|
log::debug!("requesting {json}");
|
||||||
let mut stream = self.imp().stream.borrow_mut();
|
let mut stream = self.imp().stream.borrow_mut();
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
use log;
|
use log;
|
||||||
use std::{
|
use std::{
|
||||||
cell::{Cell, RefCell},
|
cell::{Cell, RefCell},
|
||||||
|
collections::HashSet,
|
||||||
rc::Rc,
|
rc::Rc,
|
||||||
};
|
};
|
||||||
use tokio::signal;
|
use tokio::signal;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
client::{ClientHandle, ClientManager},
|
client::{ClientConfig, ClientHandle, ClientManager, ClientState},
|
||||||
config::Config,
|
config::Config,
|
||||||
dns,
|
dns,
|
||||||
frontend::{FrontendListener, FrontendRequest},
|
frontend::{FrontendListener, FrontendRequest},
|
||||||
@@ -49,13 +50,21 @@ impl Server {
|
|||||||
let state = Rc::new(Cell::new(State::Receiving));
|
let state = Rc::new(Cell::new(State::Receiving));
|
||||||
let port = Rc::new(Cell::new(config.port));
|
let port = Rc::new(Cell::new(config.port));
|
||||||
for config_client in config.get_clients() {
|
for config_client in config.get_clients() {
|
||||||
client_manager.borrow_mut().add_client(
|
let client = ClientConfig {
|
||||||
config_client.hostname,
|
hostname: config_client.hostname,
|
||||||
config_client.ips,
|
fix_ips: config_client.ips.into_iter().collect(),
|
||||||
config_client.port,
|
port: config_client.port,
|
||||||
config_client.pos,
|
pos: config_client.pos,
|
||||||
config_client.active,
|
};
|
||||||
);
|
let state = ClientState {
|
||||||
|
active: config_client.active,
|
||||||
|
ips: HashSet::from_iter(client.fix_ips.iter().cloned()),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
let mut client_manager = client_manager.borrow_mut();
|
||||||
|
let handle = client_manager.add_client();
|
||||||
|
let c = client_manager.get_mut(handle).expect("invalid handle");
|
||||||
|
*c = (client, state);
|
||||||
}
|
}
|
||||||
let release_bind = config.release_bind.clone();
|
let release_bind = config.release_bind.clone();
|
||||||
Self {
|
Self {
|
||||||
@@ -130,9 +139,9 @@ impl Server {
|
|||||||
.client_manager
|
.client_manager
|
||||||
.borrow()
|
.borrow()
|
||||||
.get_client_states()
|
.get_client_states()
|
||||||
.filter_map(|(h, s)| {
|
.filter_map(|(h, (c, s))| {
|
||||||
if s.active {
|
if s.active {
|
||||||
Some((h, s.client.hostname.clone()))
|
Some((h, c.hostname.clone()))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -108,7 +108,7 @@ async fn handle_capture_event(
|
|||||||
// get client state for handle
|
// get client state for handle
|
||||||
let mut client_manager = server.client_manager.borrow_mut();
|
let mut client_manager = server.client_manager.borrow_mut();
|
||||||
let client_state = match client_manager.get_mut(handle) {
|
let client_state = match client_manager.get_mut(handle) {
|
||||||
Some(state) => state,
|
Some((_, s)) => s,
|
||||||
None => {
|
None => {
|
||||||
// should not happen
|
// should not happen
|
||||||
log::warn!("unknown client!");
|
log::warn!("unknown client!");
|
||||||
|
|||||||
@@ -108,7 +108,7 @@ async fn handle_udp_rx(
|
|||||||
{
|
{
|
||||||
let mut client_manager = server.client_manager.borrow_mut();
|
let mut client_manager = server.client_manager.borrow_mut();
|
||||||
let client_state = match client_manager.get_mut(handle) {
|
let client_state = match client_manager.get_mut(handle) {
|
||||||
Some(s) => s,
|
Some((_, s)) => s,
|
||||||
None => {
|
None => {
|
||||||
log::error!("unknown handle");
|
log::error!("unknown handle");
|
||||||
return;
|
return;
|
||||||
@@ -156,13 +156,12 @@ async fn handle_udp_rx(
|
|||||||
}) = event
|
}) = event
|
||||||
{
|
{
|
||||||
let mut client_manager = server.client_manager.borrow_mut();
|
let mut client_manager = server.client_manager.borrow_mut();
|
||||||
let client_state =
|
let client_state = if let Some((_, s)) = client_manager.get_mut(handle) {
|
||||||
if let Some(client_state) = client_manager.get_mut(handle) {
|
s
|
||||||
client_state
|
} else {
|
||||||
} else {
|
log::error!("unknown handle");
|
||||||
log::error!("unknown handle");
|
return;
|
||||||
return;
|
};
|
||||||
};
|
|
||||||
if state == 0 {
|
if state == 0 {
|
||||||
// ignore release event if key not pressed
|
// ignore release event if key not pressed
|
||||||
ignore_event = !client_state.pressed_keys.remove(&key);
|
ignore_event = !client_state.pressed_keys.remove(&key);
|
||||||
@@ -213,7 +212,7 @@ async fn release_keys(
|
|||||||
.borrow_mut()
|
.borrow_mut()
|
||||||
.get_mut(client)
|
.get_mut(client)
|
||||||
.iter_mut()
|
.iter_mut()
|
||||||
.flat_map(|s| s.pressed_keys.drain())
|
.flat_map(|(_, s)| s.pressed_keys.drain())
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
for key in keys {
|
for key in keys {
|
||||||
|
|||||||
@@ -75,12 +75,11 @@ async fn handle_frontend_stream(
|
|||||||
|
|
||||||
let tx = frontend_tx.clone();
|
let tx = frontend_tx.clone();
|
||||||
tokio::task::spawn_local(async move {
|
tokio::task::spawn_local(async move {
|
||||||
let _ = tx.send(FrontendRequest::Enumerate()).await;
|
|
||||||
loop {
|
loop {
|
||||||
let event = frontend::wait_for_request(&mut stream).await;
|
let request = frontend::wait_for_request(&mut stream).await;
|
||||||
match event {
|
match request {
|
||||||
Ok(event) => {
|
Ok(request) => {
|
||||||
let _ = tx.send(event).await;
|
let _ = tx.send(request).await;
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
if let Some(e) = e.downcast_ref::<io::Error>() {
|
if let Some(e) = e.downcast_ref::<io::Error>() {
|
||||||
@@ -98,8 +97,8 @@ async fn handle_frontend_stream(
|
|||||||
|
|
||||||
async fn handle_frontend_event(
|
async fn handle_frontend_event(
|
||||||
server: &Server,
|
server: &Server,
|
||||||
capture_tx: &Sender<CaptureEvent>,
|
capture: &Sender<CaptureEvent>,
|
||||||
emulate_tx: &Sender<EmulationEvent>,
|
emulate: &Sender<EmulationEvent>,
|
||||||
resolve_tx: &Sender<DnsRequest>,
|
resolve_tx: &Sender<DnsRequest>,
|
||||||
frontend: &mut FrontendListener,
|
frontend: &mut FrontendListener,
|
||||||
port_tx: &Sender<u16>,
|
port_tx: &Sender<u16>,
|
||||||
@@ -107,99 +106,67 @@ async fn handle_frontend_event(
|
|||||||
) -> bool {
|
) -> bool {
|
||||||
log::debug!("frontend: {event:?}");
|
log::debug!("frontend: {event:?}");
|
||||||
match event {
|
match event {
|
||||||
FrontendRequest::Create(hostname, port, pos) => {
|
FrontendRequest::Create => {
|
||||||
add_client(
|
add_client(server, frontend).await;
|
||||||
server,
|
|
||||||
frontend,
|
|
||||||
resolve_tx,
|
|
||||||
hostname,
|
|
||||||
HashSet::new(),
|
|
||||||
port,
|
|
||||||
pos,
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
}
|
}
|
||||||
FrontendRequest::Activate(handle, active) => {
|
FrontendRequest::Activate(handle, active) => {
|
||||||
if active {
|
if active {
|
||||||
activate_client(server, frontend, capture_tx, emulate_tx, handle).await;
|
activate_client(server, frontend, capture, emulate, handle).await;
|
||||||
} else {
|
} else {
|
||||||
deactivate_client(server, frontend, capture_tx, emulate_tx, handle).await;
|
deactivate_client(server, frontend, capture, emulate, handle).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
FrontendRequest::ChangePort(port) => {
|
FrontendRequest::ChangePort(port) => {
|
||||||
let _ = port_tx.send(port).await;
|
let _ = port_tx.send(port).await;
|
||||||
}
|
}
|
||||||
FrontendRequest::Delete(handle) => {
|
FrontendRequest::Delete(handle) => {
|
||||||
remove_client(server, frontend, capture_tx, emulate_tx, handle).await;
|
remove_client(server, frontend, capture, emulate, handle).await;
|
||||||
}
|
}
|
||||||
FrontendRequest::Enumerate() => {
|
FrontendRequest::Enumerate() => {
|
||||||
let clients = server
|
let clients = server
|
||||||
.client_manager
|
.client_manager
|
||||||
.borrow()
|
.borrow()
|
||||||
.get_client_states()
|
.get_client_states()
|
||||||
.map(|(h, s)| (h, s.client.clone(), s.active))
|
.map(|(h, (c, s))| (h, c.clone(), s.clone()))
|
||||||
.collect();
|
.collect();
|
||||||
notify_all(frontend, FrontendEvent::Enumerate(clients)).await;
|
broadcast(frontend, FrontendEvent::Enumerate(clients)).await;
|
||||||
}
|
}
|
||||||
FrontendRequest::Terminate() => {
|
FrontendRequest::Terminate() => {
|
||||||
log::info!("terminating gracefully...");
|
log::info!("terminating gracefully...");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
FrontendRequest::Update(handle, hostname, port, pos) => {
|
FrontendRequest::UpdateFixIps(handle, fix_ips) => {
|
||||||
update_client(
|
update_fix_ips(server, resolve_tx, handle, fix_ips).await;
|
||||||
server,
|
broadcast_client_update(server, frontend, handle).await;
|
||||||
frontend,
|
}
|
||||||
capture_tx,
|
FrontendRequest::UpdateHostname(handle, hostname) => {
|
||||||
emulate_tx,
|
update_hostname(server, resolve_tx, handle, hostname).await;
|
||||||
resolve_tx,
|
broadcast_client_update(server, frontend, handle).await;
|
||||||
(handle, hostname, port, pos),
|
}
|
||||||
)
|
FrontendRequest::UpdatePort(handle, port) => {
|
||||||
.await;
|
update_port(server, handle, port).await;
|
||||||
|
broadcast_client_update(server, frontend, handle).await;
|
||||||
|
}
|
||||||
|
FrontendRequest::UpdatePosition(handle, pos) => {
|
||||||
|
update_pos(server, handle, capture, emulate, pos).await;
|
||||||
|
broadcast_client_update(server, frontend, handle).await;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn notify_all(frontend: &mut FrontendListener, event: FrontendEvent) {
|
async fn broadcast(frontend: &mut FrontendListener, event: FrontendEvent) {
|
||||||
if let Err(e) = frontend.broadcast_event(event).await {
|
if let Err(e) = frontend.broadcast_event(event).await {
|
||||||
log::error!("error notifying frontend: {e}");
|
log::error!("error notifying frontend: {e}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn add_client(
|
pub async fn add_client(server: &Server, frontend: &mut FrontendListener) {
|
||||||
server: &Server,
|
let handle = server.client_manager.borrow_mut().add_client();
|
||||||
frontend: &mut FrontendListener,
|
log::info!("added client {handle}");
|
||||||
resolver_tx: &Sender<DnsRequest>,
|
|
||||||
hostname: Option<String>,
|
|
||||||
addr: HashSet<IpAddr>,
|
|
||||||
port: u16,
|
|
||||||
pos: Position,
|
|
||||||
) {
|
|
||||||
log::info!(
|
|
||||||
"adding client [{}]{} @ {:?}",
|
|
||||||
pos,
|
|
||||||
hostname.as_deref().unwrap_or(""),
|
|
||||||
&addr
|
|
||||||
);
|
|
||||||
let handle =
|
|
||||||
server
|
|
||||||
.client_manager
|
|
||||||
.borrow_mut()
|
|
||||||
.add_client(hostname.clone(), addr, port, pos, false);
|
|
||||||
|
|
||||||
log::debug!("add_client {handle}");
|
let (c, s) = server.client_manager.borrow().get(handle).unwrap().clone();
|
||||||
|
broadcast(frontend, FrontendEvent::Created(handle, c, s)).await;
|
||||||
if let Some(hostname) = hostname {
|
|
||||||
let _ = resolver_tx.send(DnsRequest { hostname, handle }).await;
|
|
||||||
}
|
|
||||||
let client = server
|
|
||||||
.client_manager
|
|
||||||
.borrow()
|
|
||||||
.get(handle)
|
|
||||||
.unwrap()
|
|
||||||
.client
|
|
||||||
.clone();
|
|
||||||
notify_all(frontend, FrontendEvent::Created(handle, client)).await;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn deactivate_client(
|
pub async fn deactivate_client(
|
||||||
@@ -209,19 +176,19 @@ pub async fn deactivate_client(
|
|||||||
emulate: &Sender<EmulationEvent>,
|
emulate: &Sender<EmulationEvent>,
|
||||||
handle: ClientHandle,
|
handle: ClientHandle,
|
||||||
) {
|
) {
|
||||||
let (client, _) = match server.client_manager.borrow_mut().get_mut(handle) {
|
let state = match server.client_manager.borrow_mut().get_mut(handle) {
|
||||||
Some(state) => {
|
Some((_, s)) => {
|
||||||
state.active = false;
|
s.active = false;
|
||||||
(handle, state.client.pos)
|
s.clone()
|
||||||
}
|
}
|
||||||
None => return,
|
None => return,
|
||||||
};
|
};
|
||||||
|
|
||||||
let event = ClientEvent::Destroy(client);
|
let event = ClientEvent::Destroy(handle);
|
||||||
let _ = capture.send(CaptureEvent::ClientEvent(event)).await;
|
let _ = capture.send(CaptureEvent::ClientEvent(event)).await;
|
||||||
let _ = emulate.send(EmulationEvent::ClientEvent(event)).await;
|
let _ = emulate.send(EmulationEvent::ClientEvent(event)).await;
|
||||||
let event = FrontendEvent::Activated(client, false);
|
let event = FrontendEvent::StateChange(handle, state);
|
||||||
notify_all(frontend, event).await;
|
broadcast(frontend, event).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn activate_client(
|
pub async fn activate_client(
|
||||||
@@ -233,7 +200,7 @@ pub async fn activate_client(
|
|||||||
) {
|
) {
|
||||||
/* deactivate potential other client at this position */
|
/* deactivate potential other client at this position */
|
||||||
let pos = match server.client_manager.borrow().get(handle) {
|
let pos = match server.client_manager.borrow().get(handle) {
|
||||||
Some(state) => state.client.pos,
|
Some((client, _)) => client.pos,
|
||||||
None => return,
|
None => return,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -245,19 +212,19 @@ pub async fn activate_client(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* activate the client */
|
/* activate the client */
|
||||||
server
|
let state = if let Some((_, s)) = server.client_manager.borrow_mut().get_mut(handle) {
|
||||||
.client_manager
|
s.active = true;
|
||||||
.borrow_mut()
|
s.clone()
|
||||||
.get_mut(handle)
|
} else {
|
||||||
.unwrap()
|
return;
|
||||||
.active = true;
|
};
|
||||||
|
|
||||||
/* notify emulation, capture and frontends */
|
/* notify emulation, capture and frontends */
|
||||||
let event = ClientEvent::Create(handle, pos);
|
let event = ClientEvent::Create(handle, pos);
|
||||||
let _ = capture.send(CaptureEvent::ClientEvent(event)).await;
|
let _ = capture.send(CaptureEvent::ClientEvent(event)).await;
|
||||||
let _ = emulate.send(EmulationEvent::ClientEvent(event)).await;
|
let _ = emulate.send(EmulationEvent::ClientEvent(event)).await;
|
||||||
let event = FrontendEvent::Activated(handle, true);
|
let event = FrontendEvent::StateChange(handle, state);
|
||||||
notify_all(frontend, event).await;
|
broadcast(frontend, event).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn remove_client(
|
pub async fn remove_client(
|
||||||
@@ -271,7 +238,7 @@ pub async fn remove_client(
|
|||||||
.client_manager
|
.client_manager
|
||||||
.borrow_mut()
|
.borrow_mut()
|
||||||
.remove_client(handle)
|
.remove_client(handle)
|
||||||
.map(|s| s.active)
|
.map(|(_, s)| s.active)
|
||||||
else {
|
else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
@@ -283,76 +250,107 @@ pub async fn remove_client(
|
|||||||
}
|
}
|
||||||
|
|
||||||
let event = FrontendEvent::Deleted(handle);
|
let event = FrontendEvent::Deleted(handle);
|
||||||
notify_all(frontend, event).await;
|
broadcast(frontend, event).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn update_client(
|
async fn update_fix_ips(
|
||||||
server: &Server,
|
server: &Server,
|
||||||
frontend: &mut FrontendListener,
|
|
||||||
capture: &Sender<CaptureEvent>,
|
|
||||||
emulate: &Sender<EmulationEvent>,
|
|
||||||
resolve_tx: &Sender<DnsRequest>,
|
resolve_tx: &Sender<DnsRequest>,
|
||||||
client_update: (ClientHandle, Option<String>, u16, Position),
|
handle: ClientHandle,
|
||||||
|
fix_ips: Vec<IpAddr>,
|
||||||
) {
|
) {
|
||||||
let (handle, hostname, port, pos) = client_update;
|
let hostname = {
|
||||||
let mut changed = false;
|
|
||||||
let (hostname, active) = {
|
|
||||||
// retrieve state
|
|
||||||
let mut client_manager = server.client_manager.borrow_mut();
|
let mut client_manager = server.client_manager.borrow_mut();
|
||||||
let Some(state) = client_manager.get_mut(handle) else {
|
let Some((c, _)) = client_manager.get_mut(handle) else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
// update pos
|
c.fix_ips = fix_ips;
|
||||||
if state.client.pos != pos {
|
c.hostname.clone()
|
||||||
state.client.pos = pos;
|
|
||||||
changed = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// update port
|
|
||||||
if state.client.port != port {
|
|
||||||
state.client.port = port;
|
|
||||||
state.active_addr = state.active_addr.map(|a| SocketAddr::new(a.ip(), port));
|
|
||||||
changed = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// update hostname
|
|
||||||
if state.client.hostname != hostname {
|
|
||||||
state.client.ips = HashSet::new();
|
|
||||||
state.active_addr = None;
|
|
||||||
state.client.hostname = hostname;
|
|
||||||
changed = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
log::debug!("client updated: {:?}", state);
|
|
||||||
(state.client.hostname.clone(), state.active)
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// resolve dns if something changed
|
if let Some(hostname) = hostname {
|
||||||
if changed {
|
let _ = resolve_tx.send(DnsRequest { hostname, handle }).await;
|
||||||
// resolve dns
|
|
||||||
if let Some(hostname) = hostname {
|
|
||||||
let _ = resolve_tx.send(DnsRequest { hostname, handle }).await;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn update_hostname(
|
||||||
|
server: &Server,
|
||||||
|
resolve_tx: &Sender<DnsRequest>,
|
||||||
|
handle: ClientHandle,
|
||||||
|
hostname: Option<String>,
|
||||||
|
) {
|
||||||
|
let hostname = {
|
||||||
|
let mut client_manager = server.client_manager.borrow_mut();
|
||||||
|
let Some((c, s)) = client_manager.get_mut(handle) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
// update hostname
|
||||||
|
if c.hostname != hostname {
|
||||||
|
c.hostname = hostname;
|
||||||
|
s.ips = HashSet::from_iter(c.fix_ips.iter().cloned());
|
||||||
|
s.active_addr = None;
|
||||||
|
c.hostname.clone()
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// resolve to update ips in state
|
||||||
|
if let Some(hostname) = hostname {
|
||||||
|
let _ = resolve_tx.send(DnsRequest { hostname, handle }).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn update_port(server: &Server, handle: ClientHandle, port: u16) {
|
||||||
|
let mut client_manager = server.client_manager.borrow_mut();
|
||||||
|
let Some((c, s)) = client_manager.get_mut(handle) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
if c.port != port {
|
||||||
|
c.port = port;
|
||||||
|
s.active_addr = s.active_addr.map(|a| SocketAddr::new(a.ip(), port));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn update_pos(
|
||||||
|
server: &Server,
|
||||||
|
handle: ClientHandle,
|
||||||
|
capture: &Sender<CaptureEvent>,
|
||||||
|
emulate: &Sender<EmulationEvent>,
|
||||||
|
pos: Position,
|
||||||
|
) {
|
||||||
|
let (changed, active) = {
|
||||||
|
let mut client_manager = server.client_manager.borrow_mut();
|
||||||
|
let Some((c, s)) = client_manager.get_mut(handle) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
let changed = c.pos != pos;
|
||||||
|
c.pos = pos;
|
||||||
|
(changed, s.active)
|
||||||
|
};
|
||||||
|
|
||||||
// update state in event input emulator & input capture
|
// update state in event input emulator & input capture
|
||||||
if changed && active {
|
if changed {
|
||||||
// update state
|
if active {
|
||||||
let destroy = ClientEvent::Destroy(handle);
|
let destroy = ClientEvent::Destroy(handle);
|
||||||
|
let _ = capture.send(CaptureEvent::ClientEvent(destroy)).await;
|
||||||
|
let _ = emulate.send(EmulationEvent::ClientEvent(destroy)).await;
|
||||||
|
}
|
||||||
let create = ClientEvent::Create(handle, pos);
|
let create = ClientEvent::Create(handle, pos);
|
||||||
let _ = capture.send(CaptureEvent::ClientEvent(destroy)).await;
|
|
||||||
let _ = emulate.send(EmulationEvent::ClientEvent(destroy)).await;
|
|
||||||
let _ = capture.send(CaptureEvent::ClientEvent(create)).await;
|
let _ = capture.send(CaptureEvent::ClientEvent(create)).await;
|
||||||
let _ = emulate.send(EmulationEvent::ClientEvent(create)).await;
|
let _ = emulate.send(EmulationEvent::ClientEvent(create)).await;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
let client = server
|
|
||||||
.client_manager
|
async fn broadcast_client_update(
|
||||||
.borrow()
|
server: &Server,
|
||||||
.get(handle)
|
frontend: &mut FrontendListener,
|
||||||
.unwrap()
|
handle: ClientHandle,
|
||||||
.client
|
) {
|
||||||
.clone();
|
let (client, _) = server.client_manager.borrow().get(handle).unwrap().clone();
|
||||||
notify_all(frontend, FrontendEvent::Updated(handle, client)).await;
|
broadcast(frontend, FrontendEvent::Updated(handle, client)).await;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ pub fn new(
|
|||||||
// if receiving we care about clients with pressed keys
|
// if receiving we care about clients with pressed keys
|
||||||
client_manager
|
client_manager
|
||||||
.get_client_states_mut()
|
.get_client_states_mut()
|
||||||
.filter(|(_, s)| !s.pressed_keys.is_empty())
|
.filter(|(_, (_, s))| !s.pressed_keys.is_empty())
|
||||||
.map(|(h, _)| h)
|
.map(|(h, _)| h)
|
||||||
.collect()
|
.collect()
|
||||||
} else {
|
} else {
|
||||||
@@ -46,17 +46,15 @@ pub fn new(
|
|||||||
let ping_addrs: Vec<SocketAddr> = {
|
let ping_addrs: Vec<SocketAddr> = {
|
||||||
ping_clients
|
ping_clients
|
||||||
.iter()
|
.iter()
|
||||||
.flat_map(|&c| client_manager.get(c))
|
.flat_map(|&h| client_manager.get(h))
|
||||||
.flat_map(|state| {
|
.flat_map(|(c, s)| {
|
||||||
if state.alive && state.active_addr.is_some() {
|
if s.alive && s.active_addr.is_some() {
|
||||||
vec![state.active_addr.unwrap()]
|
vec![s.active_addr.unwrap()]
|
||||||
} else {
|
} else {
|
||||||
state
|
s.ips
|
||||||
.client
|
|
||||||
.ips
|
|
||||||
.iter()
|
.iter()
|
||||||
.cloned()
|
.cloned()
|
||||||
.map(|ip| SocketAddr::new(ip, state.client.port))
|
.map(|ip| SocketAddr::new(ip, c.port))
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -64,8 +62,8 @@ pub fn new(
|
|||||||
};
|
};
|
||||||
|
|
||||||
// reset alive
|
// reset alive
|
||||||
for (_, state) in client_manager.get_client_states_mut() {
|
for (_, (_, s)) in client_manager.get_client_states_mut() {
|
||||||
state.alive = false;
|
s.alive = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
(ping_clients, ping_addrs)
|
(ping_clients, ping_addrs)
|
||||||
@@ -102,8 +100,8 @@ pub fn new(
|
|||||||
let client_manager = server.client_manager.borrow();
|
let client_manager = server.client_manager.borrow();
|
||||||
ping_clients
|
ping_clients
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|&c| match client_manager.get(c) {
|
.filter_map(|&h| match client_manager.get(h) {
|
||||||
Some(state) if !state.alive => Some(c),
|
Some((_, s)) if !s.alive => Some(h),
|
||||||
_ => None,
|
_ => None,
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
@@ -112,9 +110,9 @@ pub fn new(
|
|||||||
// we may not be receiving anymore but we should respond
|
// we may not be receiving anymore but we should respond
|
||||||
// to the original state and not the "new" one
|
// to the original state and not the "new" one
|
||||||
if receiving {
|
if receiving {
|
||||||
for c in unresponsive_clients {
|
for h in unresponsive_clients {
|
||||||
log::warn!("device not responding, releasing keys!");
|
log::warn!("device not responding, releasing keys!");
|
||||||
let _ = emulate_notify.send(EmulationEvent::ReleaseKeys(c)).await;
|
let _ = emulate_notify.send(EmulationEvent::ReleaseKeys(h)).await;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// release pointer if the active client has not responded
|
// release pointer if the active client has not responded
|
||||||
|
|||||||
@@ -27,12 +27,12 @@ pub fn new(resolver: DnsResolver, server: Server) -> (JoinHandle<()>, Sender<Dns
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
if let Some(state) = server.client_manager.borrow_mut().get_mut(handle) {
|
if let Some((c, s)) = server.client_manager.borrow_mut().get_mut(handle) {
|
||||||
let mut addrs = HashSet::from_iter(state.client.fix_ips.iter().cloned());
|
let mut addrs = HashSet::from_iter(c.fix_ips.iter().cloned());
|
||||||
for ip in ips {
|
for ip in ips {
|
||||||
addrs.insert(ip);
|
addrs.insert(ip);
|
||||||
}
|
}
|
||||||
state.client.ips = addrs;
|
s.ips = addrs;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user