Compare commits

..

6 Commits

Author SHA1 Message Date
Ferdinand Schober
196d01fe9e macos: fix key-release with repeat logic 2024-09-23 19:20:39 +02:00
Jacob Barber
b071201dcb Fix multimonitors (#202)
Co-authored-by: Jacob Barber <jacob.barber@disney.com>

closes #83
2024-09-20 20:50:37 +02:00
Nick Bolton
f52f19d2e3 Add link to Synergy (open source) (#194) 2024-09-10 19:06:09 +02:00
Ferdinand Schober
39fed0344c cleanup server code + fix a lost update case (#191) 2024-09-05 02:31:10 +02:00
Ferdinand Schober
6cd190191e cleanup main (#189) 2024-09-05 01:06:55 +02:00
Ferdinand Schober
be677d4c81 extract frontend crate (#186) 2024-09-04 17:29:29 +02:00
10 changed files with 228 additions and 166 deletions

View File

@@ -20,7 +20,7 @@ Focus lies on performance and a clean, manageable implementation that can easily
***blazingly fast™*** because it's written in rust. ***blazingly fast™*** because it's written in rust.
For an alternative (with slightly different goals) you may check out [Input Leap](https://github.com/input-leap). For an alternative (with slightly different goals) you may check out [Synergy 1 Community Edition](https://github.com/symless/synergy) or [Input Leap](https://github.com/input-leap) (Synergy fork).
> [!WARNING] > [!WARNING]

View File

@@ -1,6 +1,9 @@
use super::{error::EmulationError, Emulation, EmulationHandle}; use super::{error::EmulationError, Emulation, EmulationHandle};
use async_trait::async_trait; use async_trait::async_trait;
use core_graphics::display::{CGDisplayBounds, CGMainDisplayID, CGPoint}; use core_graphics::base::CGFloat;
use core_graphics::display::{
CGDirectDisplayID, CGDisplayBounds, CGGetDisplaysWithRect, CGPoint, CGRect, CGSize,
};
use core_graphics::event::{ use core_graphics::event::{
CGEvent, CGEventTapLocation, CGEventType, CGKeyCode, CGMouseButton, EventField, ScrollEventUnit, CGEvent, CGEventTapLocation, CGEventType, CGKeyCode, CGMouseButton, EventField, ScrollEventUnit,
}; };
@@ -8,8 +11,9 @@ use core_graphics::event_source::{CGEventSource, CGEventSourceStateID};
use input_event::{Event, KeyboardEvent, PointerEvent}; use input_event::{Event, KeyboardEvent, PointerEvent};
use keycode::{KeyMap, KeyMapping}; use keycode::{KeyMap, KeyMapping};
use std::ops::{Index, IndexMut}; use std::ops::{Index, IndexMut};
use std::sync::Arc;
use std::time::Duration; use std::time::Duration;
use tokio::task::AbortHandle; use tokio::{sync::Notify, task::JoinHandle};
use super::error::MacOSEmulationCreationError; use super::error::MacOSEmulationCreationError;
@@ -18,8 +22,9 @@ const DEFAULT_REPEAT_INTERVAL: Duration = Duration::from_millis(32);
pub(crate) struct MacOSEmulation { pub(crate) struct MacOSEmulation {
event_source: CGEventSource, event_source: CGEventSource,
repeat_task: Option<AbortHandle>, repeat_task: Option<JoinHandle<()>>,
button_state: ButtonState, button_state: ButtonState,
notify_repeat_task: Arc<Notify>,
} }
struct ButtonState { struct ButtonState {
@@ -65,6 +70,7 @@ impl MacOSEmulation {
event_source, event_source,
button_state, button_state,
repeat_task: None, repeat_task: None,
notify_repeat_task: Arc::new(Notify::new()),
}) })
} }
@@ -76,20 +82,33 @@ impl MacOSEmulation {
async fn spawn_repeat_task(&mut self, key: u16) { async fn spawn_repeat_task(&mut self, key: u16) {
// there can only be one repeating key and it's // there can only be one repeating key and it's
// always the last to be pressed // always the last to be pressed
self.kill_repeat_task(); self.cancel_repeat_task().await;
let event_source = self.event_source.clone(); let event_source = self.event_source.clone();
let notify = self.notify_repeat_task.clone();
let repeat_task = tokio::task::spawn_local(async move { let repeat_task = tokio::task::spawn_local(async move {
tokio::time::sleep(DEFAULT_REPEAT_DELAY).await; let stop = tokio::select! {
loop { _ = tokio::time::sleep(DEFAULT_REPEAT_DELAY) => false,
key_event(event_source.clone(), key, 1); _ = notify.notified() => true,
tokio::time::sleep(DEFAULT_REPEAT_INTERVAL).await; };
if !stop {
loop {
key_event(event_source.clone(), key, 1);
tokio::select! {
_ = tokio::time::sleep(DEFAULT_REPEAT_INTERVAL) => {},
_ = notify.notified() => break,
}
}
} }
// release key when cancelled
key_event(event_source.clone(), key, 0);
}); });
self.repeat_task = Some(repeat_task.abort_handle()); self.repeat_task = Some(repeat_task);
} }
fn kill_repeat_task(&mut self) {
async fn cancel_repeat_task(&mut self) {
if let Some(task) = self.repeat_task.take() { if let Some(task) = self.repeat_task.take() {
task.abort(); self.notify_repeat_task.notify_waiters();
let _ = task.await;
} }
} }
} }
@@ -105,6 +124,77 @@ fn key_event(event_source: CGEventSource, key: u16, state: u8) {
event.post(CGEventTapLocation::HID); event.post(CGEventTapLocation::HID);
} }
fn get_display_at_point(x: CGFloat, y: CGFloat) -> Option<CGDirectDisplayID> {
let mut displays: [CGDirectDisplayID; 16] = [0; 16];
let mut display_count: u32 = 0;
let rect = CGRect::new(&CGPoint::new(x, y), &CGSize::new(0.0, 0.0));
let error = unsafe {
CGGetDisplaysWithRect(
rect,
1,
displays.as_mut_ptr(),
&mut display_count as *mut u32,
)
};
if error != 0 {
log::warn!("error getting displays at point ({}, {}): {}", x, y, error);
return Option::None;
}
if display_count == 0 {
log::debug!("no displays found at point ({}, {})", x, y);
return Option::None;
}
return displays.first().copied();
}
fn get_display_bounds(display: CGDirectDisplayID) -> (CGFloat, CGFloat, CGFloat, CGFloat) {
unsafe {
let bounds = CGDisplayBounds(display);
let min_x = bounds.origin.x;
let max_x = bounds.origin.x + bounds.size.width;
let min_y = bounds.origin.y;
let max_y = bounds.origin.y + bounds.size.height;
(min_x as f64, min_y as f64, max_x as f64, max_y as f64)
}
}
fn clamp_to_screen_space(
current_x: CGFloat,
current_y: CGFloat,
dx: CGFloat,
dy: CGFloat,
) -> (CGFloat, CGFloat) {
// Check which display the mouse is currently on
// Determine what the location of the mouse would be after applying the move
// Get the display at the new location
// If the point is not on a display
// Clamp the mouse to the current display
// Else If the point is on a display
// Clamp the mouse to the new display
let current_display = match get_display_at_point(current_x, current_y) {
Some(display) => display,
None => {
log::warn!("could not get current display!");
return (current_x, current_y);
}
};
let new_x = current_x + dx;
let new_y = current_y + dy;
let final_display = get_display_at_point(new_x, new_y).unwrap_or(current_display);
let (min_x, min_y, max_x, max_y) = get_display_bounds(final_display);
(
new_x.clamp(min_x, max_x - 1.),
new_y.clamp(min_y, max_y - 1.),
)
}
#[async_trait] #[async_trait]
impl Emulation for MacOSEmulation { impl Emulation for MacOSEmulation {
async fn consume( async fn consume(
@@ -115,16 +205,6 @@ impl Emulation for MacOSEmulation {
match event { match event {
Event::Pointer(pointer_event) => match pointer_event { Event::Pointer(pointer_event) => match pointer_event {
PointerEvent::Motion { time: _, dx, dy } => { PointerEvent::Motion { time: _, dx, dy } => {
// FIXME secondary displays?
let (min_x, min_y, max_x, max_y) = unsafe {
let display = CGMainDisplayID();
let bounds = CGDisplayBounds(display);
let min_x = bounds.origin.x;
let max_x = bounds.origin.x + bounds.size.width;
let min_y = bounds.origin.y;
let max_y = bounds.origin.y + bounds.size.height;
(min_x as f64, min_y as f64, max_x as f64, max_y as f64)
};
let mut mouse_location = match self.get_mouse_location() { let mut mouse_location = match self.get_mouse_location() {
Some(l) => l, Some(l) => l,
None => { None => {
@@ -133,8 +213,11 @@ impl Emulation for MacOSEmulation {
} }
}; };
mouse_location.x = (mouse_location.x + dx).clamp(min_x, max_x - 1.); let (new_mouse_x, new_mouse_y) =
mouse_location.y = (mouse_location.y + dy).clamp(min_y, max_y - 1.); clamp_to_screen_space(mouse_location.x, mouse_location.y, dx, dy);
mouse_location.x = new_mouse_x;
mouse_location.y = new_mouse_y;
let mut event_type = CGEventType::MouseMoved; let mut event_type = CGEventType::MouseMoved;
if self.button_state.left { if self.button_state.left {
@@ -279,7 +362,7 @@ impl Emulation for MacOSEmulation {
match state { match state {
// pressed // pressed
1 => self.spawn_repeat_task(code).await, 1 => self.spawn_repeat_task(code).await,
_ => self.kill_repeat_task(), _ => self.cancel_repeat_task().await,
} }
key_event(self.event_source.clone(), code, state) key_event(self.event_source.clone(), code, state)
} }

View File

@@ -30,6 +30,7 @@ pub fn run() -> Result<(), IpcError> {
struct Cli { struct Cli {
clients: Vec<(ClientHandle, ClientConfig, ClientState)>, clients: Vec<(ClientHandle, ClientConfig, ClientState)>,
changed: Option<ClientHandle>,
rx: AsyncFrontendEventReader, rx: AsyncFrontendEventReader,
tx: AsyncFrontendRequestWriter, tx: AsyncFrontendRequestWriter,
} }
@@ -38,6 +39,7 @@ impl Cli {
fn new(rx: AsyncFrontendEventReader, tx: AsyncFrontendRequestWriter) -> Cli { fn new(rx: AsyncFrontendEventReader, tx: AsyncFrontendRequestWriter) -> Cli {
Self { Self {
clients: vec![], clients: vec![],
changed: None,
rx, rx,
tx, tx,
} }
@@ -80,9 +82,14 @@ impl Cli {
event = self.rx.next() => { event = self.rx.next() => {
if let Some(event) = event { if let Some(event) = event {
self.handle_event(event?); self.handle_event(event?);
} else {
break Ok(());
} }
} }
} }
if let Some(handle) = self.changed.take() {
self.update_client(handle).await?;
}
} }
} }
@@ -202,6 +209,7 @@ impl Cli {
fn handle_event(&mut self, event: FrontendEvent) { fn handle_event(&mut self, event: FrontendEvent) {
match event { match event {
FrontendEvent::Changed(h) => self.changed = Some(h),
FrontendEvent::Created(h, c, s) => { FrontendEvent::Created(h, c, s) => {
eprint!("client added ({h}): "); eprint!("client added ({h}): ");
print_config(&c); print_config(&c);

View File

@@ -6,7 +6,7 @@ use std::{env, process, str};
use window::Window; use window::Window;
use lan_mouse_ipc::FrontendEvent; use lan_mouse_ipc::{FrontendEvent, FrontendRequest};
use adw::Application; use adw::Application;
use gtk::{ use gtk::{
@@ -92,6 +92,9 @@ fn build_ui(app: &Application) {
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::Changed(handle) => {
window.request(FrontendRequest::GetState(handle));
}
FrontendEvent::Created(handle, client, state) => { FrontendEvent::Created(handle, client, state) => {
window.new_client(handle, client, state); window.new_client(handle, client, state);
} }

View File

@@ -247,7 +247,11 @@ impl Window {
} }
pub fn request_client_state(&self, client: &ClientObject) { pub fn request_client_state(&self, client: &ClientObject) {
self.request(FrontendRequest::GetState(client.handle())); self.request_client_state_for(client.handle());
}
pub fn request_client_state_for(&self, handle: ClientHandle) {
self.request(FrontendRequest::GetState(handle));
} }
pub fn request_client_create(&self) { pub fn request_client_create(&self) {

View File

@@ -166,6 +166,8 @@ pub struct ClientState {
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
pub enum FrontendEvent { pub enum FrontendEvent {
/// client state has changed, new state must be requested via [`FrontendRequest::GetState`]
Changed(ClientHandle),
/// a client was created /// a client was created
Created(ClientHandle, ClientConfig, ClientState), Created(ClientHandle, ClientConfig, ClientState),
/// no such client /// no such client

View File

@@ -2,22 +2,9 @@ use crate::config::Config;
use futures::StreamExt; use futures::StreamExt;
use input_capture::{self, CaptureError, CaptureEvent, InputCapture, InputCaptureError, Position}; use input_capture::{self, CaptureError, CaptureEvent, InputCapture, InputCaptureError, Position};
use input_event::{Event, KeyboardEvent}; use input_event::{Event, KeyboardEvent};
use tokio::task::LocalSet;
pub fn run() -> Result<(), InputCaptureError> { pub async fn run(config: Config) -> Result<(), InputCaptureError> {
log::info!("running input capture test"); log::info!("running input capture test");
let runtime = tokio::runtime::Builder::new_current_thread()
.enable_io()
.enable_time()
.build()
.unwrap();
let config = Config::new().unwrap();
runtime.block_on(LocalSet::new().run_until(input_capture_test(config)))
}
async fn input_capture_test(config: Config) -> Result<(), InputCaptureError> {
log::info!("creating input capture"); log::info!("creating input capture");
let backend = config.capture_backend.map(|b| b.into()); let backend = config.capture_backend.map(|b| b.into());
loop { loop {

View File

@@ -3,28 +3,17 @@ use input_emulation::{InputEmulation, InputEmulationError};
use input_event::{Event, PointerEvent}; use input_event::{Event, PointerEvent};
use std::f64::consts::PI; use std::f64::consts::PI;
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
use tokio::task::LocalSet;
pub fn run() -> Result<(), InputEmulationError> {
log::info!("running input emulation test");
let runtime = tokio::runtime::Builder::new_current_thread()
.enable_io()
.enable_time()
.build()
.unwrap();
let config = Config::new().unwrap();
runtime.block_on(LocalSet::new().run_until(input_emulation_test(config)))
}
const FREQUENCY_HZ: f64 = 1.0; const FREQUENCY_HZ: f64 = 1.0;
const RADIUS: f64 = 100.0; const RADIUS: f64 = 100.0;
async fn input_emulation_test(config: Config) -> Result<(), InputEmulationError> { pub async fn run(config: Config) -> Result<(), InputEmulationError> {
log::info!("running input emulation test");
let backend = config.emulation_backend.map(|b| b.into()); let backend = config.emulation_backend.map(|b| b.into());
let mut emulation = InputEmulation::new(backend).await?; let mut emulation = InputEmulation::new(backend).await?;
emulation.create(0).await; emulation.create(0).await;
let start = Instant::now(); let start = Instant::now();
let mut offset = (0, 0); let mut offset = (0, 0);
loop { loop {

View File

@@ -1,4 +1,6 @@
use env_logger::Env; use env_logger::Env;
use input_capture::InputCaptureError;
use input_emulation::InputEmulationError;
use lan_mouse::{ use lan_mouse::{
capture_test, capture_test,
config::{Config, ConfigError, Frontend}, config::{Config, ConfigError, Frontend},
@@ -7,12 +9,29 @@ use lan_mouse::{
}; };
use lan_mouse_ipc::IpcError; use lan_mouse_ipc::IpcError;
use std::{ use std::{
future::Future,
io, io,
process::{self, Child, Command}, process::{self, Child, Command},
}; };
use thiserror::Error; use thiserror::Error;
use tokio::task::LocalSet; use tokio::task::LocalSet;
#[derive(Debug, Error)]
enum LanMouseError {
#[error(transparent)]
Service(#[from] ServiceError),
#[error(transparent)]
IpcError(#[from] IpcError),
#[error(transparent)]
Config(#[from] ConfigError),
#[error(transparent)]
Io(#[from] io::Error),
#[error(transparent)]
Capture(#[from] InputCaptureError),
#[error(transparent)]
Emulation(#[from] InputEmulationError),
}
pub fn main() { pub fn main() {
// init logging // init logging
let env = Env::default().filter_or("LAN_MOUSE_LOG_LEVEL", "info"); let env = Env::default().filter_or("LAN_MOUSE_LOG_LEVEL", "info");
@@ -24,26 +43,6 @@ pub fn main() {
} }
} }
fn start_service() -> Result<Child, io::Error> {
let child = Command::new(std::env::current_exe()?)
.args(std::env::args().skip(1))
.arg("--daemon")
.spawn()?;
Ok(child)
}
#[derive(Debug, Error)]
enum LanMouseError {
#[error(transparent)]
Service(#[from] ServiceError),
#[error(transparent)]
IpcError(#[from] IpcError),
#[error(transparent)]
Config(#[from] ConfigError),
#[error(transparent)]
Io(#[from] io::Error),
}
fn run() -> Result<(), LanMouseError> { fn run() -> Result<(), LanMouseError> {
// parse config file + cli args // parse config file + cli args
let config = Config::new()?; let config = Config::new()?;
@@ -51,12 +50,12 @@ fn run() -> Result<(), LanMouseError> {
log::info!("release bind: {:?}", config.release_bind); log::info!("release bind: {:?}", config.release_bind);
if config.test_capture { if config.test_capture {
capture_test::run().unwrap(); run_async(capture_test::run(config))?;
} else if config.test_emulation { } else if config.test_emulation {
emulation_test::run().unwrap(); run_async(emulation_test::run(config))?;
} else if config.daemon { } else if config.daemon {
// if daemon is specified we run the service // if daemon is specified we run the service
run_service(config)?; run_async(run_service(config))?;
} else { } else {
// otherwise start the service as a child process and // otherwise start the service as a child process and
// run a frontend // run a frontend
@@ -77,7 +76,11 @@ fn run() -> Result<(), LanMouseError> {
Ok(()) Ok(())
} }
fn run_service(config: Config) -> Result<(), ServiceError> { fn run_async<F, E>(f: F) -> Result<(), LanMouseError>
where
F: Future<Output = Result<(), E>>,
LanMouseError: From<E>,
{
// create single threaded tokio runtime // create single threaded tokio runtime
let runtime = tokio::runtime::Builder::new_current_thread() let runtime = tokio::runtime::Builder::new_current_thread()
.enable_io() .enable_io()
@@ -85,16 +88,21 @@ fn run_service(config: Config) -> Result<(), ServiceError> {
.build()?; .build()?;
// run async event loop // run async event loop
runtime.block_on(LocalSet::new().run_until(async { Ok(runtime.block_on(LocalSet::new().run_until(f))?)
// run main loop }
log::info!("Press {:?} to release the mouse", config.release_bind);
let mut server = Server::new(config); fn start_service() -> Result<Child, io::Error> {
server.run().await?; let child = Command::new(std::env::current_exe()?)
.args(std::env::args().skip(1))
.arg("--daemon")
.spawn()?;
Ok(child)
}
log::debug!("service exiting"); async fn run_service(config: Config) -> Result<(), ServiceError> {
Result::<(), ServiceError>::Ok(()) log::info!("Press {:?} to release the mouse", config.release_bind);
}))?; Server::new(config).run().await?;
log::info!("service exited!");
Ok(()) Ok(())
} }

View File

@@ -58,7 +58,6 @@ pub struct Server {
notifies: Rc<Notifies>, notifies: Rc<Notifies>,
config: Rc<Config>, config: Rc<Config>,
pending_frontend_events: Rc<RefCell<VecDeque<FrontendEvent>>>, pending_frontend_events: Rc<RefCell<VecDeque<FrontendEvent>>>,
pending_dns_requests: Rc<RefCell<VecDeque<ClientHandle>>>,
capture_status: Rc<Cell<Status>>, capture_status: Rc<Cell<Status>>,
emulation_status: Rc<Cell<Status>>, emulation_status: Rc<Cell<Status>>,
} }
@@ -70,7 +69,6 @@ struct Notifies {
ping: Notify, ping: Notify,
port_changed: Notify, port_changed: Notify,
frontend_event_pending: Notify, frontend_event_pending: Notify,
dns_request_pending: Notify,
cancel: CancellationToken, cancel: CancellationToken,
} }
@@ -114,7 +112,6 @@ impl Server {
release_bind, release_bind,
notifies, notifies,
pending_frontend_events: Rc::new(RefCell::new(VecDeque::new())), pending_frontend_events: Rc::new(RefCell::new(VecDeque::new())),
pending_dns_requests: Rc::new(RefCell::new(VecDeque::new())),
capture_status: Default::default(), capture_status: Default::default(),
emulation_status: Default::default(), emulation_status: Default::default(),
} }
@@ -137,17 +134,10 @@ impl Server {
let (udp_send_tx, udp_send_rx) = channel(); /* udp sender */ let (udp_send_tx, udp_send_rx) = channel(); /* udp sender */
let (dns_tx, dns_rx) = channel(); /* dns requests */ let (dns_tx, dns_rx) = channel(); /* dns requests */
// udp task
let network = network_task::new(self.clone(), udp_recv_tx.clone(), udp_send_rx).await?; let network = network_task::new(self.clone(), udp_recv_tx.clone(), udp_send_rx).await?;
// input capture
let capture = capture_task::new(self.clone(), capture_rx, udp_send_tx.clone()); let capture = capture_task::new(self.clone(), capture_rx, udp_send_tx.clone());
// input emulation
let emulation = let emulation =
emulation_task::new(self.clone(), emulation_rx, udp_recv_rx, udp_send_tx.clone()); emulation_task::new(self.clone(), emulation_rx, udp_recv_rx, udp_send_tx.clone());
// create dns resolver
let resolver = DnsResolver::new(dns_rx)?; let resolver = DnsResolver::new(dns_rx)?;
let dns_task = tokio::task::spawn_local(resolver.run(self.clone())); let dns_task = tokio::task::spawn_local(resolver.run(self.clone()));
@@ -160,11 +150,9 @@ impl Server {
); );
for handle in self.active_clients() { for handle in self.active_clients() {
self.request_dns(handle); dns_tx.send(handle).expect("channel closed");
} }
log::info!("running service");
loop { loop {
tokio::select! { tokio::select! {
request = frontend.next() => { request = frontend.next() => {
@@ -176,9 +164,8 @@ impl Server {
} }
None => break, None => break,
}; };
log::debug!("received frontend request: {request:?}"); log::debug!("handle frontend request: {request:?}");
self.handle_request(&capture_tx.clone(), &emulation_tx.clone(), request).await; self.handle_request(&capture_tx.clone(), &emulation_tx.clone(), request, &dns_tx);
log::debug!("handled frontend request");
} }
_ = self.notifies.frontend_event_pending.notified() => { _ = self.notifies.frontend_event_pending.notified() => {
while let Some(event) = { while let Some(event) = {
@@ -189,15 +176,6 @@ impl Server {
frontend.broadcast(event).await; frontend.broadcast(event).await;
} }
}, },
_ = self.notifies.dns_request_pending.notified() => {
while let Some(request) = {
/* need to drop borrow before next iteration! */
let request = self.pending_dns_requests.borrow_mut().pop_front();
request
} {
dns_tx.send(request).expect("channel closed");
}
}
_ = self.cancelled() => break, _ = self.cancelled() => break,
r = signal::ctrl_c() => { r = signal::ctrl_c() => {
r.expect("failed to wait for CTRL+C"); r.expect("failed to wait for CTRL+C");
@@ -232,6 +210,7 @@ impl Server {
} }
fn notify_capture(&self) { fn notify_capture(&self) {
log::info!("received capture enable request");
self.notifies.capture.notify_waiters() self.notifies.capture.notify_waiters()
} }
@@ -240,6 +219,7 @@ impl Server {
} }
fn notify_emulation(&self) { fn notify_emulation(&self) {
log::info!("received emulation enable request");
self.notifies.emulation.notify_waiters() self.notifies.emulation.notify_waiters()
} }
@@ -266,10 +246,7 @@ impl Server {
} }
pub(crate) fn client_updated(&self, handle: ClientHandle) { pub(crate) fn client_updated(&self, handle: ClientHandle) {
let state = self.client_manager.borrow().get(handle).cloned(); self.notify_frontend(FrontendEvent::Changed(handle));
if let Some((config, state)) = state {
self.notify_frontend(FrontendEvent::State(handle, config, state));
}
} }
fn active_clients(&self) -> Vec<ClientHandle> { fn active_clients(&self) -> Vec<ClientHandle> {
@@ -281,55 +258,43 @@ impl Server {
.collect() .collect()
} }
fn request_dns(&self, handle: ClientHandle) { fn handle_request(
self.pending_dns_requests.borrow_mut().push_back(handle);
self.notifies.dns_request_pending.notify_one();
}
async fn handle_request(
&self, &self,
capture: &Sender<CaptureRequest>, capture: &Sender<CaptureRequest>,
emulate: &Sender<EmulationRequest>, emulate: &Sender<EmulationRequest>,
event: FrontendRequest, event: FrontendRequest,
dns: &Sender<ClientHandle>,
) -> bool { ) -> bool {
log::debug!("frontend: {event:?}"); log::debug!("frontend: {event:?}");
match event { match event {
FrontendRequest::EnableCapture => { FrontendRequest::EnableCapture => self.notify_capture(),
log::info!("received capture enable request"); FrontendRequest::EnableEmulation => self.notify_emulation(),
self.notify_capture();
}
FrontendRequest::EnableEmulation => {
log::info!("received emulation enable request");
self.notify_emulation();
}
FrontendRequest::Create => { FrontendRequest::Create => {
let handle = self.add_client().await; self.add_client();
self.request_dns(handle);
} }
FrontendRequest::Activate(handle, active) => { FrontendRequest::Activate(handle, active) => {
if active { if active {
self.activate_client(capture, emulate, handle).await; self.activate_client(capture, emulate, handle);
} else { } else {
self.deactivate_client(capture, emulate, handle).await; self.deactivate_client(capture, emulate, handle);
} }
} }
FrontendRequest::ChangePort(port) => self.request_port_change(port), FrontendRequest::ChangePort(port) => self.request_port_change(port),
FrontendRequest::Delete(handle) => { FrontendRequest::Delete(handle) => {
self.remove_client(capture, emulate, handle).await; self.remove_client(capture, emulate, handle);
self.notify_frontend(FrontendEvent::Deleted(handle)); self.notify_frontend(FrontendEvent::Deleted(handle));
} }
FrontendRequest::Enumerate() => self.enumerate(), FrontendRequest::Enumerate() => self.enumerate(),
FrontendRequest::GetState(handle) => self.broadcast_client(handle), FrontendRequest::GetState(handle) => self.broadcast_client(handle),
FrontendRequest::UpdateFixIps(handle, fix_ips) => { FrontendRequest::UpdateFixIps(handle, fix_ips) => self.update_fix_ips(handle, fix_ips),
self.update_fix_ips(handle, fix_ips); FrontendRequest::UpdateHostname(handle, host) => {
self.request_dns(handle); self.update_hostname(handle, host, dns)
} }
FrontendRequest::UpdateHostname(handle, host) => self.update_hostname(handle, host),
FrontendRequest::UpdatePort(handle, port) => self.update_port(handle, port), FrontendRequest::UpdatePort(handle, port) => self.update_port(handle, port),
FrontendRequest::UpdatePosition(handle, pos) => { FrontendRequest::UpdatePosition(handle, pos) => {
self.update_pos(handle, capture, emulate, pos).await; self.update_pos(handle, capture, emulate, pos)
} }
FrontendRequest::ResolveDns(handle) => self.request_dns(handle), FrontendRequest::ResolveDns(handle) => dns.send(handle).expect("channel closed"),
FrontendRequest::Sync => { FrontendRequest::Sync => {
self.enumerate(); self.enumerate();
self.notify_frontend(FrontendEvent::EmulationStatus(self.emulation_status.get())); self.notify_frontend(FrontendEvent::EmulationStatus(self.emulation_status.get()));
@@ -350,7 +315,7 @@ impl Server {
self.notify_frontend(FrontendEvent::Enumerate(clients)); self.notify_frontend(FrontendEvent::Enumerate(clients));
} }
async fn add_client(&self) -> ClientHandle { fn add_client(&self) -> ClientHandle {
let handle = self.client_manager.borrow_mut().add_client(); let handle = self.client_manager.borrow_mut().add_client();
log::info!("added client {handle}"); log::info!("added client {handle}");
let (c, s) = self.client_manager.borrow().get(handle).unwrap().clone(); let (c, s) = self.client_manager.borrow().get(handle).unwrap().clone();
@@ -358,41 +323,40 @@ impl Server {
handle handle
} }
async fn deactivate_client( fn deactivate_client(
&self, &self,
capture: &Sender<CaptureRequest>, capture: &Sender<CaptureRequest>,
emulate: &Sender<EmulationRequest>, emulate: &Sender<EmulationRequest>,
handle: ClientHandle, handle: ClientHandle,
) { ) {
log::debug!("deactivating client {handle}");
match self.client_manager.borrow_mut().get_mut(handle) { match self.client_manager.borrow_mut().get_mut(handle) {
Some((_, s)) => s.active = false,
None => return, None => return,
Some((_, s)) if !s.active => return,
Some((_, s)) => s.active = false,
}; };
let _ = capture.send(CaptureRequest::Destroy(handle)); let _ = capture.send(CaptureRequest::Destroy(handle));
let _ = emulate.send(EmulationRequest::Destroy(handle)); let _ = emulate.send(EmulationRequest::Destroy(handle));
log::debug!("deactivating client {handle} done"); self.client_updated(handle);
log::info!("deactivated client {handle}");
} }
async fn activate_client( fn activate_client(
&self, &self,
capture: &Sender<CaptureRequest>, capture: &Sender<CaptureRequest>,
emulate: &Sender<EmulationRequest>, emulate: &Sender<EmulationRequest>,
handle: ClientHandle, handle: ClientHandle,
) { ) {
log::debug!("activating client");
/* deactivate potential other client at this position */ /* deactivate potential other client at this position */
let pos = match self.client_manager.borrow().get(handle) { let pos = match self.client_manager.borrow().get(handle) {
Some((client, _)) => client.pos,
None => return, None => return,
Some((_, s)) if s.active => return,
Some((client, _)) => client.pos,
}; };
let other = self.client_manager.borrow_mut().find_client(pos); let other = self.client_manager.borrow_mut().find_client(pos);
if let Some(other) = other { if let Some(other) = other {
if other != handle { self.deactivate_client(capture, emulate, other);
self.deactivate_client(capture, emulate, other).await;
}
} }
/* activate the client */ /* activate the client */
@@ -405,10 +369,13 @@ impl Server {
/* notify emulation, capture and frontends */ /* notify emulation, capture and frontends */
let _ = capture.send(CaptureRequest::Create(handle, to_capture_pos(pos))); let _ = capture.send(CaptureRequest::Create(handle, to_capture_pos(pos)));
let _ = emulate.send(EmulationRequest::Create(handle)); let _ = emulate.send(EmulationRequest::Create(handle));
log::debug!("activating client {handle} done");
self.client_updated(handle);
log::info!("activated client {handle} ({pos})");
} }
async fn remove_client( fn remove_client(
&self, &self,
capture: &Sender<CaptureRequest>, capture: &Sender<CaptureRequest>,
emulate: &Sender<EmulationRequest>, emulate: &Sender<EmulationRequest>,
@@ -440,6 +407,7 @@ impl Server {
c.fix_ips = fix_ips; c.fix_ips = fix_ips;
}; };
self.update_ips(handle); self.update_ips(handle);
self.client_updated(handle);
} }
pub(crate) fn update_dns_ips(&self, handle: ClientHandle, dns_ips: Vec<IpAddr>) { pub(crate) fn update_dns_ips(&self, handle: ClientHandle, dns_ips: Vec<IpAddr>) {
@@ -447,6 +415,7 @@ impl Server {
s.dns_ips = dns_ips; s.dns_ips = dns_ips;
}; };
self.update_ips(handle); self.update_ips(handle);
self.client_updated(handle);
} }
fn update_ips(&self, handle: ClientHandle) { fn update_ips(&self, handle: ClientHandle) {
@@ -460,7 +429,12 @@ impl Server {
} }
} }
fn update_hostname(&self, handle: ClientHandle, hostname: Option<String>) { fn update_hostname(
&self,
handle: ClientHandle,
hostname: Option<String>,
dns: &Sender<ClientHandle>,
) {
let mut client_manager = self.client_manager.borrow_mut(); let mut client_manager = self.client_manager.borrow_mut();
let Some((c, s)) = client_manager.get_mut(handle) else { let Some((c, s)) = client_manager.get_mut(handle) else {
return; return;
@@ -469,10 +443,13 @@ impl Server {
// hostname changed // hostname changed
if c.hostname != hostname { if c.hostname != hostname {
c.hostname = hostname; c.hostname = hostname;
s.ips = HashSet::from_iter(c.fix_ips.iter().cloned());
s.active_addr = None; s.active_addr = None;
self.request_dns(handle); s.dns_ips.clear();
drop(client_manager);
self.update_ips(handle);
dns.send(handle).expect("channel closed");
} }
self.client_updated(handle);
} }
fn update_port(&self, handle: ClientHandle, port: u16) { fn update_port(&self, handle: ClientHandle, port: u16) {
@@ -487,7 +464,7 @@ impl Server {
} }
} }
async fn update_pos( fn update_pos(
&self, &self,
handle: ClientHandle, handle: ClientHandle,
capture: &Sender<CaptureRequest>, capture: &Sender<CaptureRequest>,
@@ -501,18 +478,19 @@ impl Server {
}; };
let changed = c.pos != pos; let changed = c.pos != pos;
if changed {
log::info!("update pos {handle} {} -> {}", c.pos, pos);
}
c.pos = pos; c.pos = pos;
(changed, s.active) (changed, s.active)
}; };
// update state in event input emulator & input capture // update state in event input emulator & input capture
if changed { if changed {
self.deactivate_client(capture, emulate, handle);
if active { if active {
let _ = capture.send(CaptureRequest::Destroy(handle)); self.activate_client(capture, emulate, handle);
let _ = emulate.send(EmulationRequest::Destroy(handle));
} }
let _ = capture.send(CaptureRequest::Create(handle, to_capture_pos(pos)));
let _ = emulate.send(EmulationRequest::Create(handle));
} }
} }
@@ -545,7 +523,7 @@ impl Server {
self.client_updated(handle); self.client_updated(handle);
} }
pub(crate) fn get_hostname(&self, handle: u64) -> Option<String> { pub(crate) fn get_hostname(&self, handle: ClientHandle) -> Option<String> {
self.client_manager self.client_manager
.borrow_mut() .borrow_mut()
.get_mut(handle) .get_mut(handle)
@@ -561,12 +539,12 @@ impl Server {
self.state.replace(state); self.state.replace(state);
} }
fn set_active(&self, handle: Option<u64>) { fn set_active(&self, handle: Option<ClientHandle>) {
log::debug!("active client => {handle:?}"); log::debug!("active client => {handle:?}");
self.active_client.replace(handle); self.active_client.replace(handle);
} }
fn active_addr(&self, handle: u64) -> Option<SocketAddr> { fn active_addr(&self, handle: ClientHandle) -> Option<SocketAddr> {
self.client_manager self.client_manager
.borrow() .borrow()
.get(handle) .get(handle)