mirror of
https://github.com/feschber/lan-mouse.git
synced 2026-03-11 15:20:54 +03:00
Compare commits
4 Commits
display-ut
...
enter-hook
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9424abbd56 | ||
|
|
e9738fc024 | ||
|
|
9969f997d3 | ||
|
|
b8cc9e2197 |
4
.github/workflows/cachix.yml
vendored
4
.github/workflows/cachix.yml
vendored
@@ -7,7 +7,7 @@ jobs:
|
|||||||
matrix:
|
matrix:
|
||||||
os:
|
os:
|
||||||
- ubuntu-latest
|
- ubuntu-latest
|
||||||
- macos-latest
|
- macos-13
|
||||||
- macos-14
|
- macos-14
|
||||||
name: "Build"
|
name: "Build"
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
@@ -31,7 +31,7 @@ jobs:
|
|||||||
run: nix build --print-build-logs --show-trace .#packages.x86_64-linux.lan-mouse
|
run: nix build --print-build-logs --show-trace .#packages.x86_64-linux.lan-mouse
|
||||||
|
|
||||||
- name: Build lan-mouse (x86_64-darwin)
|
- name: Build lan-mouse (x86_64-darwin)
|
||||||
if: matrix.os == 'macos-latest'
|
if: matrix.os == 'macos-13'
|
||||||
run: nix build --print-build-logs --show-trace .#packages.x86_64-darwin.lan-mouse
|
run: nix build --print-build-logs --show-trace .#packages.x86_64-darwin.lan-mouse
|
||||||
|
|
||||||
- name: Build lan-mouse (aarch64-darwin)
|
- name: Build lan-mouse (aarch64-darwin)
|
||||||
|
|||||||
2
.github/workflows/pre-release.yml
vendored
2
.github/workflows/pre-release.yml
vendored
@@ -78,7 +78,7 @@ jobs:
|
|||||||
path: lan-mouse-windows.zip
|
path: lan-mouse-windows.zip
|
||||||
|
|
||||||
macos-release-build:
|
macos-release-build:
|
||||||
runs-on: macos-latest
|
runs-on: macos-13
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- name: install dependencies
|
- name: install dependencies
|
||||||
|
|||||||
2
.github/workflows/rust.yml
vendored
2
.github/workflows/rust.yml
vendored
@@ -92,7 +92,7 @@ jobs:
|
|||||||
target/debug/*.dll
|
target/debug/*.dll
|
||||||
|
|
||||||
build-macos:
|
build-macos:
|
||||||
runs-on: macos-latest
|
runs-on: macos-13
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- name: install dependencies
|
- name: install dependencies
|
||||||
|
|||||||
2
.github/workflows/tagged-release.yml
vendored
2
.github/workflows/tagged-release.yml
vendored
@@ -74,7 +74,7 @@ jobs:
|
|||||||
path: lan-mouse-windows.zip
|
path: lan-mouse-windows.zip
|
||||||
|
|
||||||
macos-release-build:
|
macos-release-build:
|
||||||
runs-on: macos-latest
|
runs-on: macos-13
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- name: install dependencies
|
- name: install dependencies
|
||||||
|
|||||||
@@ -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", "io-std", "macros", "net", "rt", "sync", "signal"] }
|
tokio = {version = "1.32.0", features = ["io-util", "io-std", "macros", "net", "process", "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"
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ use std::task::ready;
|
|||||||
use std::{io, pin::Pin, thread};
|
use std::{io, pin::Pin, thread};
|
||||||
use tokio::sync::mpsc::{channel, Receiver, Sender};
|
use tokio::sync::mpsc::{channel, Receiver, Sender};
|
||||||
use windows::core::{w, PCWSTR};
|
use windows::core::{w, PCWSTR};
|
||||||
use windows::Win32::Foundation::{FALSE, HINSTANCE, HWND, LPARAM, LRESULT, WPARAM};
|
use windows::Win32::Foundation::{FALSE, HINSTANCE, HWND, LPARAM, LRESULT, RECT, WPARAM};
|
||||||
use windows::Win32::Graphics::Gdi::{
|
use windows::Win32::Graphics::Gdi::{
|
||||||
EnumDisplayDevicesW, EnumDisplaySettingsW, DEVMODEW, DISPLAY_DEVICEW,
|
EnumDisplayDevicesW, EnumDisplaySettingsW, DEVMODEW, DISPLAY_DEVICEW,
|
||||||
DISPLAY_DEVICE_ATTACHED_TO_DESKTOP, ENUM_CURRENT_SETTINGS,
|
DISPLAY_DEVICE_ATTACHED_TO_DESKTOP, ENUM_CURRENT_SETTINGS,
|
||||||
@@ -43,7 +43,6 @@ use crate::{
|
|||||||
event::Event,
|
event::Event,
|
||||||
scancode,
|
scancode,
|
||||||
};
|
};
|
||||||
use crate::display_util::{DirectedLine, Display, Point};
|
|
||||||
|
|
||||||
pub struct WindowsInputCapture {
|
pub struct WindowsInputCapture {
|
||||||
event_rx: Receiver<(ClientHandle, Event)>,
|
event_rx: Receiver<(ClientHandle, Event)>,
|
||||||
@@ -94,7 +93,7 @@ unsafe fn get_event_tid() -> Option<u32> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static mut ENTRY_POINT: Point<i32> = Point { x: 0, y: 0 };
|
static mut ENTRY_POINT: (i32, i32) = (0, 0);
|
||||||
|
|
||||||
fn to_mouse_event(wparam: WPARAM, lparam: LPARAM) -> Option<PointerEvent> {
|
fn to_mouse_event(wparam: WPARAM, lparam: LPARAM) -> Option<PointerEvent> {
|
||||||
let mouse_low_level: MSLLHOOKSTRUCT =
|
let mouse_low_level: MSLLHOOKSTRUCT =
|
||||||
@@ -131,12 +130,13 @@ fn to_mouse_event(wparam: WPARAM, lparam: LPARAM) -> Option<PointerEvent> {
|
|||||||
state: 0,
|
state: 0,
|
||||||
}),
|
}),
|
||||||
WPARAM(p) if p == WM_MOUSEMOVE as usize => unsafe {
|
WPARAM(p) if p == WM_MOUSEMOVE as usize => unsafe {
|
||||||
let current = Point { x: mouse_low_level.pt.x, y: mouse_low_level.pt.y };
|
let (x, y) = (mouse_low_level.pt.x, mouse_low_level.pt.y);
|
||||||
let diff = current - ENTRY_POINT;
|
let (ex, ey) = ENTRY_POINT;
|
||||||
|
let (dx, dy) = (x - ex, y - ey);
|
||||||
Some(PointerEvent::Motion {
|
Some(PointerEvent::Motion {
|
||||||
time: 0,
|
time: 0,
|
||||||
relative_x: diff.x as f64,
|
relative_x: dx as f64,
|
||||||
relative_y: diff.y as f64,
|
relative_y: dy as f64,
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
WPARAM(p) if p == WM_MOUSEWHEEL as usize => Some(PointerEvent::AxisDiscrete120 {
|
WPARAM(p) if p == WM_MOUSEWHEEL as usize => Some(PointerEvent::AxisDiscrete120 {
|
||||||
@@ -210,6 +210,31 @@ unsafe fn to_key_event(wparam: WPARAM, lparam: LPARAM) -> Option<KeyboardEvent>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// clamp point to display bounds
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `prev_point`: coordinates, the cursor was before entering, within bounds of a display
|
||||||
|
/// * `entry_point`: point to clamp
|
||||||
|
///
|
||||||
|
/// returns: (i32, i32), the corrected entry point
|
||||||
|
///
|
||||||
|
fn clamp_to_display_bounds(prev_point: (i32, i32), point: (i32, i32)) -> (i32, i32) {
|
||||||
|
/* find display where movement came from */
|
||||||
|
let display_regions = unsafe { get_display_regions() };
|
||||||
|
let display = display_regions
|
||||||
|
.iter()
|
||||||
|
.find(|&d| is_within_dp_region(prev_point, d))
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
/* clamp to bounds (inclusive) */
|
||||||
|
let (x, y) = point;
|
||||||
|
let (min_x, max_x) = (display.left, display.right - 1);
|
||||||
|
let (min_y, max_y) = (display.top, display.bottom - 1);
|
||||||
|
(x.clamp(min_x, max_x), y.clamp(min_y, max_y))
|
||||||
|
}
|
||||||
|
|
||||||
unsafe fn send_blocking(event: Event) {
|
unsafe fn send_blocking(event: Event) {
|
||||||
if let Some(active) = ACTIVE_CLIENT {
|
if let Some(active) = ACTIVE_CLIENT {
|
||||||
block_on(async move {
|
block_on(async move {
|
||||||
@@ -224,9 +249,8 @@ unsafe fn check_client_activation(wparam: WPARAM, lparam: LPARAM) -> bool {
|
|||||||
}
|
}
|
||||||
let mouse_low_level: MSLLHOOKSTRUCT =
|
let mouse_low_level: MSLLHOOKSTRUCT =
|
||||||
unsafe { *std::mem::transmute::<LPARAM, *const MSLLHOOKSTRUCT>(lparam) };
|
unsafe { *std::mem::transmute::<LPARAM, *const MSLLHOOKSTRUCT>(lparam) };
|
||||||
static mut PREV_POS: Option<Point<i32>> = None;
|
static mut PREV_POS: Option<(i32, i32)> = None;
|
||||||
let curr_pos = Point { x: mouse_low_level.pt.x, y: mouse_low_level.pt.y};
|
let curr_pos = (mouse_low_level.pt.x, mouse_low_level.pt.y);
|
||||||
log::warn!("POSITION: {curr_pos:?} - display: {:?}", curr_pos.display_in_bounds(get_display_regions()));
|
|
||||||
let prev_pos = PREV_POS.unwrap_or(curr_pos);
|
let prev_pos = PREV_POS.unwrap_or(curr_pos);
|
||||||
PREV_POS.replace(curr_pos);
|
PREV_POS.replace(curr_pos);
|
||||||
|
|
||||||
@@ -239,8 +263,7 @@ unsafe fn check_client_activation(wparam: WPARAM, lparam: LPARAM) -> bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* check if a client was activated */
|
/* check if a client was activated */
|
||||||
let line = DirectedLine { start: prev_pos, end: curr_pos };
|
let Some(pos) = entered_barrier(prev_pos, curr_pos, get_display_regions()) else {
|
||||||
let Some((display, pos)) = line.crossed_display_bounds(get_display_regions()) else {
|
|
||||||
return ret;
|
return ret;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -251,7 +274,7 @@ unsafe fn check_client_activation(wparam: WPARAM, lparam: LPARAM) -> bool {
|
|||||||
|
|
||||||
/* update active client and entry point */
|
/* update active client and entry point */
|
||||||
ACTIVE_CLIENT.replace(*client);
|
ACTIVE_CLIENT.replace(*client);
|
||||||
ENTRY_POINT = curr_pos.clamp_to_display(&display);
|
ENTRY_POINT = clamp_to_display_bounds(prev_pos, curr_pos);
|
||||||
|
|
||||||
/* notify main thread */
|
/* notify main thread */
|
||||||
log::debug!("ENTERED @ {prev_pos:?} -> {curr_pos:?}");
|
log::debug!("ENTERED @ {prev_pos:?} -> {curr_pos:?}");
|
||||||
@@ -324,7 +347,7 @@ unsafe extern "system" fn window_proc(
|
|||||||
LRESULT(1)
|
LRESULT(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn enumerate_displays() -> Vec<Display<i32>> {
|
fn enumerate_displays() -> Vec<RECT> {
|
||||||
unsafe {
|
unsafe {
|
||||||
let mut display_rects = vec![];
|
let mut display_rects = vec![];
|
||||||
let mut devices = vec![];
|
let mut devices = vec![];
|
||||||
@@ -356,11 +379,11 @@ fn enumerate_displays() -> Vec<Display<i32>> {
|
|||||||
let (x, y) = (pos.x, pos.y);
|
let (x, y) = (pos.x, pos.y);
|
||||||
let (width, height) = (dev_mode.dmPelsWidth, dev_mode.dmPelsHeight);
|
let (width, height) = (dev_mode.dmPelsWidth, dev_mode.dmPelsHeight);
|
||||||
|
|
||||||
display_rects.push(Display {
|
display_rects.push(RECT {
|
||||||
left: x,
|
left: x,
|
||||||
right: x + width as i32 - 1,
|
right: x + width as i32,
|
||||||
top: y,
|
top: y,
|
||||||
bottom: y + height as i32 - 1,
|
bottom: y + height as i32,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
display_rects
|
display_rects
|
||||||
@@ -369,8 +392,8 @@ fn enumerate_displays() -> Vec<Display<i32>> {
|
|||||||
|
|
||||||
static mut DISPLAY_RESOLUTION_CHANGED: bool = true;
|
static mut DISPLAY_RESOLUTION_CHANGED: bool = true;
|
||||||
|
|
||||||
unsafe fn get_display_regions() -> &'static Vec<Display<i32>> {
|
unsafe fn get_display_regions() -> &'static Vec<RECT> {
|
||||||
static mut DISPLAYS: Vec<Display<i32>> = vec![];
|
static mut DISPLAYS: Vec<RECT> = vec![];
|
||||||
if DISPLAY_RESOLUTION_CHANGED {
|
if DISPLAY_RESOLUTION_CHANGED {
|
||||||
DISPLAYS = enumerate_displays();
|
DISPLAYS = enumerate_displays();
|
||||||
DISPLAY_RESOLUTION_CHANGED = false;
|
DISPLAY_RESOLUTION_CHANGED = false;
|
||||||
@@ -379,6 +402,73 @@ unsafe fn get_display_regions() -> &'static Vec<Display<i32>> {
|
|||||||
&*addr_of!(DISPLAYS)
|
&*addr_of!(DISPLAYS)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_within_dp_region(point: (i32, i32), display: &RECT) -> bool {
|
||||||
|
[
|
||||||
|
Position::Left,
|
||||||
|
Position::Right,
|
||||||
|
Position::Top,
|
||||||
|
Position::Bottom,
|
||||||
|
]
|
||||||
|
.iter()
|
||||||
|
.all(|&pos| is_within_dp_boundary(point, display, pos))
|
||||||
|
}
|
||||||
|
fn is_within_dp_boundary(point: (i32, i32), display: &RECT, pos: Position) -> bool {
|
||||||
|
let (x, y) = point;
|
||||||
|
match pos {
|
||||||
|
Position::Left => display.left <= x,
|
||||||
|
Position::Right => display.right > x,
|
||||||
|
Position::Top => display.top <= y,
|
||||||
|
Position::Bottom => display.bottom > y,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// returns whether the given position is within the display bounds with respect to the given
|
||||||
|
/// barrier position
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `x`:
|
||||||
|
/// * `y`:
|
||||||
|
/// * `displays`:
|
||||||
|
/// * `pos`:
|
||||||
|
///
|
||||||
|
/// returns: bool
|
||||||
|
///
|
||||||
|
fn in_bounds(point: (i32, i32), displays: &[RECT], pos: Position) -> bool {
|
||||||
|
displays
|
||||||
|
.iter()
|
||||||
|
.any(|d| is_within_dp_boundary(point, d, pos))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn in_display_region(point: (i32, i32), displays: &[RECT]) -> bool {
|
||||||
|
displays.iter().any(|d| is_within_dp_region(point, d))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn moved_across_boundary(
|
||||||
|
prev_pos: (i32, i32),
|
||||||
|
curr_pos: (i32, i32),
|
||||||
|
displays: &[RECT],
|
||||||
|
pos: Position,
|
||||||
|
) -> bool {
|
||||||
|
/* was within bounds, but is not anymore */
|
||||||
|
in_display_region(prev_pos, displays) && !in_bounds(curr_pos, displays, pos)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn entered_barrier(
|
||||||
|
prev_pos: (i32, i32),
|
||||||
|
curr_pos: (i32, i32),
|
||||||
|
displays: &[RECT],
|
||||||
|
) -> Option<Position> {
|
||||||
|
[
|
||||||
|
Position::Left,
|
||||||
|
Position::Right,
|
||||||
|
Position::Top,
|
||||||
|
Position::Bottom,
|
||||||
|
]
|
||||||
|
.into_iter()
|
||||||
|
.find(|&pos| moved_across_boundary(prev_pos, curr_pos, displays, pos))
|
||||||
|
}
|
||||||
|
|
||||||
fn get_msg() -> Option<MSG> {
|
fn get_msg() -> Option<MSG> {
|
||||||
unsafe {
|
unsafe {
|
||||||
let mut msg = std::mem::zeroed();
|
let mut msg = std::mem::zeroed();
|
||||||
|
|||||||
@@ -102,6 +102,8 @@ pub struct ClientConfig {
|
|||||||
pub port: u16,
|
pub port: u16,
|
||||||
/// position of a client on screen
|
/// position of a client on screen
|
||||||
pub pos: Position,
|
pub pos: Position,
|
||||||
|
/// enter hook
|
||||||
|
pub cmd: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for ClientConfig {
|
impl Default for ClientConfig {
|
||||||
@@ -111,6 +113,7 @@ impl Default for ClientConfig {
|
|||||||
hostname: Default::default(),
|
hostname: Default::default(),
|
||||||
fix_ips: Default::default(),
|
fix_ips: Default::default(),
|
||||||
pos: Default::default(),
|
pos: Default::default(),
|
||||||
|
cmd: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ pub struct TomlClient {
|
|||||||
pub ips: Option<Vec<IpAddr>>,
|
pub ips: Option<Vec<IpAddr>>,
|
||||||
pub port: Option<u16>,
|
pub port: Option<u16>,
|
||||||
pub activate_on_startup: Option<bool>,
|
pub activate_on_startup: Option<bool>,
|
||||||
|
pub enter_hook: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ConfigToml {
|
impl ConfigToml {
|
||||||
@@ -92,6 +93,7 @@ pub struct ConfigClient {
|
|||||||
pub port: u16,
|
pub port: u16,
|
||||||
pub pos: Position,
|
pub pos: Position,
|
||||||
pub active: bool,
|
pub active: bool,
|
||||||
|
pub enter_hook: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
const DEFAULT_RELEASE_KEYS: [scancode::Linux; 4] =
|
const DEFAULT_RELEASE_KEYS: [scancode::Linux; 4] =
|
||||||
@@ -208,12 +210,14 @@ impl Config {
|
|||||||
None => c.host_name.clone(),
|
None => c.host_name.clone(),
|
||||||
};
|
};
|
||||||
let active = c.activate_on_startup.unwrap_or(false);
|
let active = c.activate_on_startup.unwrap_or(false);
|
||||||
|
let enter_hook = c.enter_hook.clone();
|
||||||
ConfigClient {
|
ConfigClient {
|
||||||
ips,
|
ips,
|
||||||
hostname,
|
hostname,
|
||||||
port,
|
port,
|
||||||
pos: *pos,
|
pos: *pos,
|
||||||
active,
|
active,
|
||||||
|
enter_hook,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
|
|||||||
@@ -1,114 +0,0 @@
|
|||||||
use std::ops::{Add, Mul, Sub};
|
|
||||||
use crate::client::Position;
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
|
||||||
pub struct Point<T> {
|
|
||||||
pub x: T,
|
|
||||||
pub y: T,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Sub<Output = T>> Sub for Point<T> {
|
|
||||||
type Output = Self;
|
|
||||||
fn sub(self, rhs: Self) -> Self::Output {
|
|
||||||
let x: T = self.x - rhs.x;
|
|
||||||
let y: T = self.y - rhs.y;
|
|
||||||
Self { x, y }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Copy + Eq + Ord + Sub<Output = T> + Mul<Output = T> + Add<Output = T>> Point<T> {
|
|
||||||
|
|
||||||
pub fn display_in_bounds<'a>(&self, displays: &'a[Display<T>]) -> Option<&'a Display<T>> {
|
|
||||||
displays.iter().find(|&d| self.in_display_bounds(d))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn in_display_bounds(&self, display: &Display<T>) -> bool {
|
|
||||||
self.clamp_to_display(display) == *self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn clamp_to_display(&self, display: &Display<T>) -> Self {
|
|
||||||
let x = self.x.clamp(display.left, display.right);
|
|
||||||
let y = self.y.clamp(display.top, display.bottom);
|
|
||||||
Self { x, y }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Calculates the direction of maximum change between this point and the point given by other
|
|
||||||
///
|
|
||||||
/// # Arguments
|
|
||||||
///
|
|
||||||
/// * `other`: the point to calculate the distance
|
|
||||||
///
|
|
||||||
/// returns: Position -> The direction in which the distance is largest
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use lan_mouse::client::Position;
|
|
||||||
/// use lan_mouse::display_util::Point;
|
|
||||||
/// let a = Point { x: 0, y: 0 };
|
|
||||||
/// let b = Point { x: 1, y: 2 };
|
|
||||||
/// assert_eq!(a.direction_of_maximum_change(b), Position::Bottom)
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use lan_mouse::client::Position;
|
|
||||||
/// use lan_mouse::display_util::Point;
|
|
||||||
/// let a = Point { x: 0, y: 0 };
|
|
||||||
/// let b = Point { x: 1, y: -2 };
|
|
||||||
/// assert_eq!(a.direction_of_maximum_change(b), Position::Top)
|
|
||||||
/// ```
|
|
||||||
/// ```
|
|
||||||
/// use lan_mouse::client::Position;
|
|
||||||
/// use lan_mouse::display_util::Point;
|
|
||||||
/// let a = Point { x: 0, y: 0 };
|
|
||||||
/// let b = Point { x: -2, y: -1 };
|
|
||||||
/// assert_eq!(a.direction_of_maximum_change(b), Position::Left)
|
|
||||||
/// ```
|
|
||||||
/// ```
|
|
||||||
/// use lan_mouse::client::Position;
|
|
||||||
/// use lan_mouse::display_util::Point;
|
|
||||||
/// let a = Point { x: 0, y: 0 };
|
|
||||||
/// let b = Point { x: 2, y: -1 };
|
|
||||||
/// assert_eq!(a.direction_of_maximum_change(b), Position::Right)
|
|
||||||
/// ```
|
|
||||||
pub fn direction_of_maximum_change(self, other: Self) -> Position {
|
|
||||||
let distances = [
|
|
||||||
(Position::Left, self.x - other.x),
|
|
||||||
(Position::Right, other.x - self.x),
|
|
||||||
(Position::Top, self.y - other.y),
|
|
||||||
(Position::Bottom, other.y - self.y),
|
|
||||||
];
|
|
||||||
distances.into_iter().max_by_key(|(_, d)| *d).map(|(p, _)| p).expect("no position")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
|
||||||
pub struct Display<T> {
|
|
||||||
pub left: T,
|
|
||||||
pub right: T,
|
|
||||||
pub top: T,
|
|
||||||
pub bottom: T,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
|
||||||
pub struct DirectedLine<T> {
|
|
||||||
pub start: Point<T>,
|
|
||||||
pub end: Point<T>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Copy + Eq + Ord + Sub<Output = T> + Mul<Output = T> + Add<Output = T>> DirectedLine<T> {
|
|
||||||
pub fn crossed_display_bounds<'a>(&self, displays: &'a [Display<T>]) -> Option<(&'a Display<T>, Position)> {
|
|
||||||
// was in bounds
|
|
||||||
let Some(display) = self.start.display_in_bounds(displays) else {
|
|
||||||
return None;
|
|
||||||
};
|
|
||||||
// still in bounds
|
|
||||||
if self.end.display_in_bounds(displays).is_some() {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
// was in bounds of `display`, now out of bounds
|
|
||||||
let clamped = self.end.clamp_to_display(&display);
|
|
||||||
let dir = clamped.direction_of_maximum_change(self.end);
|
|
||||||
Some((display, dir))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -107,16 +107,18 @@ pub enum FrontendRequest {
|
|||||||
UpdatePosition(ClientHandle, Position),
|
UpdatePosition(ClientHandle, Position),
|
||||||
/// update fix-ips
|
/// update fix-ips
|
||||||
UpdateFixIps(ClientHandle, Vec<IpAddr>),
|
UpdateFixIps(ClientHandle, Vec<IpAddr>),
|
||||||
|
/// request the state of the given client
|
||||||
|
GetState(ClientHandle),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub enum FrontendEvent {
|
pub enum FrontendEvent {
|
||||||
/// a client was created
|
/// a client was created
|
||||||
Created(ClientHandle, ClientConfig, ClientState),
|
Created(ClientHandle, ClientConfig, ClientState),
|
||||||
/// a client was updated
|
/// no such client
|
||||||
Updated(ClientHandle, ClientConfig),
|
NoSuchClient(ClientHandle),
|
||||||
/// state changed
|
/// state changed
|
||||||
StateChange(ClientHandle, ClientState),
|
State(ClientHandle, ClientConfig, 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)
|
||||||
@@ -235,9 +237,7 @@ impl FrontendListener {
|
|||||||
let json = serde_json::to_string(¬ify).unwrap();
|
let json = serde_json::to_string(¬ify).unwrap();
|
||||||
let payload = json.as_bytes();
|
let payload = json.as_bytes();
|
||||||
let len = payload.len().to_be_bytes();
|
let len = payload.len().to_be_bytes();
|
||||||
log::debug!("json: {json}, len: {}", payload.len());
|
log::debug!("broadcasting event to streams: {json}");
|
||||||
|
|
||||||
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() {
|
||||||
|
|||||||
@@ -100,6 +100,18 @@ impl<'a> Cli<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn update_client(&mut self, handle: ClientHandle) -> Result<()> {
|
||||||
|
self.send_request(FrontendRequest::GetState(handle)).await?;
|
||||||
|
loop {
|
||||||
|
let event = self.await_event().await?;
|
||||||
|
self.handle_event(event.clone());
|
||||||
|
if let FrontendEvent::State(_, _, _) | FrontendEvent::NoSuchClient(_) = event {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
async fn execute(&mut self, cmd: Command) -> Result<()> {
|
async fn execute(&mut self, cmd: Command) -> Result<()> {
|
||||||
match cmd {
|
match cmd {
|
||||||
Command::None => {}
|
Command::None => {}
|
||||||
@@ -125,14 +137,8 @@ impl<'a> Cli<'a> {
|
|||||||
FrontendRequest::UpdatePosition(handle, pos),
|
FrontendRequest::UpdatePosition(handle, pos),
|
||||||
] {
|
] {
|
||||||
self.send_request(request).await?;
|
self.send_request(request).await?;
|
||||||
loop {
|
|
||||||
let event = self.await_event().await?;
|
|
||||||
self.handle_event(event.clone());
|
|
||||||
if let FrontendEvent::Updated(_, _) = event {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
self.update_client(handle).await?;
|
||||||
}
|
}
|
||||||
Command::Disconnect(id) => {
|
Command::Disconnect(id) => {
|
||||||
self.send_request(FrontendRequest::Delete(id)).await?;
|
self.send_request(FrontendRequest::Delete(id)).await?;
|
||||||
@@ -148,26 +154,12 @@ impl<'a> Cli<'a> {
|
|||||||
Command::Activate(id) => {
|
Command::Activate(id) => {
|
||||||
self.send_request(FrontendRequest::Activate(id, true))
|
self.send_request(FrontendRequest::Activate(id, true))
|
||||||
.await?;
|
.await?;
|
||||||
loop {
|
self.update_client(id).await?;
|
||||||
let event = self.await_event().await?;
|
|
||||||
self.handle_event(event.clone());
|
|
||||||
if let FrontendEvent::StateChange(_, _) = event {
|
|
||||||
self.handle_event(event);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Command::Deactivate(id) => {
|
Command::Deactivate(id) => {
|
||||||
self.send_request(FrontendRequest::Activate(id, false))
|
self.send_request(FrontendRequest::Activate(id, false))
|
||||||
.await?;
|
.await?;
|
||||||
loop {
|
self.update_client(id).await?;
|
||||||
let event = self.await_event().await?;
|
|
||||||
self.handle_event(event.clone());
|
|
||||||
if let FrontendEvent::StateChange(_, _) = event {
|
|
||||||
self.handle_event(event);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Command::List => {
|
Command::List => {
|
||||||
self.send_request(FrontendRequest::Enumerate()).await?;
|
self.send_request(FrontendRequest::Enumerate()).await?;
|
||||||
@@ -182,25 +174,12 @@ impl<'a> Cli<'a> {
|
|||||||
Command::SetHost(handle, host) => {
|
Command::SetHost(handle, host) => {
|
||||||
let request = FrontendRequest::UpdateHostname(handle, Some(host.clone()));
|
let request = FrontendRequest::UpdateHostname(handle, Some(host.clone()));
|
||||||
self.send_request(request).await?;
|
self.send_request(request).await?;
|
||||||
loop {
|
self.update_client(handle).await?;
|
||||||
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) => {
|
Command::SetPort(handle, port) => {
|
||||||
let request = FrontendRequest::UpdatePort(handle, port.unwrap_or(DEFAULT_PORT));
|
let request = FrontendRequest::UpdatePort(handle, port.unwrap_or(DEFAULT_PORT));
|
||||||
self.send_request(request).await?;
|
self.send_request(request).await?;
|
||||||
loop {
|
self.update_client(handle).await?;
|
||||||
let event = self.await_event().await?;
|
|
||||||
self.handle_event(event.clone());
|
|
||||||
if let FrontendEvent::Updated(_, _) = event {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Command::Help => {
|
Command::Help => {
|
||||||
for cmd_type in [
|
for cmd_type in [
|
||||||
@@ -244,8 +223,11 @@ impl<'a> Cli<'a> {
|
|||||||
eprintln!();
|
eprintln!();
|
||||||
self.clients.push((h, c, s));
|
self.clients.push((h, c, s));
|
||||||
}
|
}
|
||||||
FrontendEvent::Updated(h, c) => {
|
FrontendEvent::NoSuchClient(h) => {
|
||||||
if let Some((_, config, _)) = self.find_mut(h) {
|
eprintln!("no such client: {h}");
|
||||||
|
}
|
||||||
|
FrontendEvent::State(h, c, s) => {
|
||||||
|
if let Some((_, config, state)) = self.find_mut(h) {
|
||||||
let old_host = config.hostname.clone().unwrap_or("\"\"".into());
|
let old_host = config.hostname.clone().unwrap_or("\"\"".into());
|
||||||
let new_host = c.hostname.clone().unwrap_or("\"\"".into());
|
let new_host = c.hostname.clone().unwrap_or("\"\"".into());
|
||||||
if old_host != new_host {
|
if old_host != new_host {
|
||||||
@@ -261,10 +243,6 @@ impl<'a> Cli<'a> {
|
|||||||
eprintln!("client {h} ips updated: {:?}", c.fix_ips)
|
eprintln!("client {h} ips updated: {:?}", c.fix_ips)
|
||||||
}
|
}
|
||||||
*config = c;
|
*config = c;
|
||||||
}
|
|
||||||
}
|
|
||||||
FrontendEvent::StateChange(h, s) => {
|
|
||||||
if let Some((_, _, state)) = self.find_mut(h) {
|
|
||||||
if state.active ^ s.active {
|
if state.active ^ s.active {
|
||||||
eprintln!(
|
eprintln!(
|
||||||
"client {h} {}",
|
"client {h} {}",
|
||||||
|
|||||||
@@ -126,12 +126,11 @@ fn build_ui(app: &Application) {
|
|||||||
FrontendEvent::Deleted(client) => {
|
FrontendEvent::Deleted(client) => {
|
||||||
window.delete_client(client);
|
window.delete_client(client);
|
||||||
}
|
}
|
||||||
FrontendEvent::Updated(handle, client) => {
|
FrontendEvent::State(handle, config, state) => {
|
||||||
window.update_client_config(handle, client);
|
window.update_client_config(handle, config);
|
||||||
}
|
|
||||||
FrontendEvent::StateChange(handle, state) => {
|
|
||||||
window.update_client_state(handle, state);
|
window.update_client_state(handle, state);
|
||||||
}
|
}
|
||||||
|
FrontendEvent::NoSuchClient(_) => { }
|
||||||
FrontendEvent::Error(e) => {
|
FrontendEvent::Error(e) => {
|
||||||
window.show_toast(e.as_str());
|
window.show_toast(e.as_str());
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -51,6 +51,10 @@ impl Window {
|
|||||||
.expect("Could not get clients")
|
.expect("Could not get clients")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn client_by_idx(&self, idx: u32) -> Option<ClientObject> {
|
||||||
|
self.clients().item(idx).map(|o| o.downcast().unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
fn setup_clients(&self) {
|
fn setup_clients(&self) {
|
||||||
let model = gio::ListStore::new::<ClientObject>();
|
let model = gio::ListStore::new::<ClientObject>();
|
||||||
self.imp().clients.replace(Some(model));
|
self.imp().clients.replace(Some(model));
|
||||||
@@ -62,27 +66,24 @@ impl Window {
|
|||||||
let client_object = obj.downcast_ref().expect("Expected object of type `ClientObject`.");
|
let client_object = obj.downcast_ref().expect("Expected object of type `ClientObject`.");
|
||||||
let row = window.create_client_row(client_object);
|
let row = window.create_client_row(client_object);
|
||||||
row.connect_closure("request-update", false, closure_local!(@strong window => move |row: ClientRow, active: bool| {
|
row.connect_closure("request-update", false, closure_local!(@strong window => move |row: ClientRow, active: bool| {
|
||||||
let index = row.index() as u32;
|
if let Some(client) = window.client_by_idx(row.index() as u32) {
|
||||||
let Some(client) = window.clients().item(index) else {
|
window.request_client_activate(&client, active);
|
||||||
return;
|
window.request_client_update(&client);
|
||||||
};
|
window.request_client_state(&client);
|
||||||
let client = client.downcast_ref::<ClientObject>().unwrap();
|
}
|
||||||
window.request_client_update(client);
|
|
||||||
window.request_client_activate(client, active)
|
|
||||||
}));
|
}));
|
||||||
row.connect_closure("request-delete", false, closure_local!(@strong window => move |row: ClientRow| {
|
row.connect_closure("request-delete", false, closure_local!(@strong window => move |row: ClientRow| {
|
||||||
let index = row.index() as u32;
|
if let Some(client) = window.client_by_idx(row.index() as u32) {
|
||||||
window.request_client_delete(index);
|
window.request_client_delete(&client);
|
||||||
|
}
|
||||||
}));
|
}));
|
||||||
row.connect_closure("request-dns", false, closure_local!(@strong window => move
|
row.connect_closure("request-dns", false, closure_local!(@strong window => move
|
||||||
|row: ClientRow| {
|
|row: ClientRow| {
|
||||||
let index = row.index() as u32;
|
if let Some(client) = window.client_by_idx(row.index() as u32) {
|
||||||
let Some(client) = window.clients().item(index) else {
|
window.request_client_update(&client);
|
||||||
return;
|
window.request_dns(&client);
|
||||||
};
|
window.request_client_state(&client);
|
||||||
let client = client.downcast_ref::<ClientObject>().unwrap();
|
}
|
||||||
window.request_client_update(client);
|
|
||||||
window.request_dns(index);
|
|
||||||
}));
|
}));
|
||||||
row.upcast()
|
row.upcast()
|
||||||
})
|
})
|
||||||
@@ -177,7 +178,7 @@ impl Window {
|
|||||||
|
|
||||||
if state.resolving != data.resolving {
|
if state.resolving != data.resolving {
|
||||||
client_object.set_resolving(state.resolving);
|
client_object.set_resolving(state.resolving);
|
||||||
log::debug!("resolving {}: {}", data.handle, state.active);
|
log::debug!("resolving {}: {}", data.handle, state.resolving);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.update_dns_state(handle, !state.ips.is_empty());
|
self.update_dns_state(handle, !state.ips.is_empty());
|
||||||
@@ -204,20 +205,6 @@ impl Window {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn request_dns(&self, idx: u32) {
|
|
||||||
let client_object = self.clients().item(idx).unwrap();
|
|
||||||
let client_object: &ClientObject = client_object.downcast_ref().unwrap();
|
|
||||||
let data = client_object.get_data();
|
|
||||||
let event = FrontendRequest::ResolveDns(data.handle);
|
|
||||||
self.request(event);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn request_client_create(&self) {
|
|
||||||
let event = FrontendRequest::Create;
|
|
||||||
self.imp().set_port(DEFAULT_PORT);
|
|
||||||
self.request(event);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn request_port_change(&self) {
|
pub fn request_port_change(&self) {
|
||||||
let port = self.imp().port_entry.get().text().to_string();
|
let port = self.imp().port_entry.get().text().to_string();
|
||||||
if let Ok(port) = port.as_str().parse::<u16>() {
|
if let Ok(port) = port.as_str().parse::<u16>() {
|
||||||
@@ -227,6 +214,23 @@ impl Window {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn request_client_state(&self, client: &ClientObject) {
|
||||||
|
let handle = client.handle();
|
||||||
|
let event = FrontendRequest::GetState(handle);
|
||||||
|
self.request(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn request_client_create(&self) {
|
||||||
|
let event = FrontendRequest::Create;
|
||||||
|
self.request(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn request_dns(&self, client: &ClientObject) {
|
||||||
|
let data = client.get_data();
|
||||||
|
let event = FrontendRequest::ResolveDns(data.handle);
|
||||||
|
self.request(event);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn request_client_update(&self, client: &ClientObject) {
|
pub fn request_client_update(&self, client: &ClientObject) {
|
||||||
let handle = client.handle();
|
let handle = client.handle();
|
||||||
let data = client.get_data();
|
let data = client.get_data();
|
||||||
@@ -239,33 +243,25 @@ impl Window {
|
|||||||
FrontendRequest::UpdatePosition(handle, position),
|
FrontendRequest::UpdatePosition(handle, position),
|
||||||
FrontendRequest::UpdatePort(handle, port),
|
FrontendRequest::UpdatePort(handle, port),
|
||||||
] {
|
] {
|
||||||
log::debug!("requesting: {event:?}");
|
|
||||||
self.request(event);
|
self.request(event);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn request_client_activate(&self, client: &ClientObject, active: bool) {
|
pub fn request_client_activate(&self, client: &ClientObject, active: bool) {
|
||||||
let handle = client.handle();
|
let handle = client.handle();
|
||||||
|
|
||||||
let event = FrontendRequest::Activate(handle, active);
|
let event = FrontendRequest::Activate(handle, active);
|
||||||
log::debug!("requesting: {event:?}");
|
|
||||||
self.request(event);
|
self.request(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn request_client_delete(&self, idx: u32) {
|
pub fn request_client_delete(&self, client: &ClientObject) {
|
||||||
if let Some(obj) = self.clients().item(idx) {
|
let handle = client.handle();
|
||||||
let client_object: &ClientObject = obj
|
let event = FrontendRequest::Delete(handle);
|
||||||
.downcast_ref()
|
self.request(event);
|
||||||
.expect("Expected object of type `ClientObject`.");
|
|
||||||
let handle = client_object.handle();
|
|
||||||
let event = FrontendRequest::Delete(handle);
|
|
||||||
self.request(event);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub 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();
|
||||||
let stream = stream.as_mut().unwrap();
|
let stream = stream.as_mut().unwrap();
|
||||||
let bytes = json.as_bytes();
|
let bytes = json.as_bytes();
|
||||||
|
|||||||
@@ -11,4 +11,3 @@ pub mod capture_test;
|
|||||||
pub mod emulation_test;
|
pub mod emulation_test;
|
||||||
pub mod frontend;
|
pub mod frontend;
|
||||||
pub mod scancode;
|
pub mod scancode;
|
||||||
pub mod display_util;
|
|
||||||
|
|||||||
@@ -55,6 +55,7 @@ impl Server {
|
|||||||
fix_ips: config_client.ips.into_iter().collect(),
|
fix_ips: config_client.ips.into_iter().collect(),
|
||||||
port: config_client.port,
|
port: config_client.port,
|
||||||
pos: config_client.pos,
|
pos: config_client.pos,
|
||||||
|
cmd: config_client.enter_hook,
|
||||||
};
|
};
|
||||||
let state = ClientState {
|
let state = ClientState {
|
||||||
active: config_client.active,
|
active: config_client.active,
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ use anyhow::{anyhow, Result};
|
|||||||
use futures::StreamExt;
|
use futures::StreamExt;
|
||||||
use std::{collections::HashSet, net::SocketAddr};
|
use std::{collections::HashSet, net::SocketAddr};
|
||||||
|
|
||||||
use tokio::{sync::mpsc::Sender, task::JoinHandle};
|
use tokio::{process::Command, sync::mpsc::Sender, task::JoinHandle};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
capture::{self, InputCapture},
|
capture::{self, InputCapture},
|
||||||
@@ -140,6 +140,9 @@ async fn handle_capture_event(
|
|||||||
if start_timer {
|
if start_timer {
|
||||||
let _ = timer_tx.try_send(());
|
let _ = timer_tx.try_send(());
|
||||||
}
|
}
|
||||||
|
if enter {
|
||||||
|
spawn_hook_command(server, handle);
|
||||||
|
}
|
||||||
if let Some(addr) = addr {
|
if let Some(addr) = addr {
|
||||||
if enter {
|
if enter {
|
||||||
let _ = sender_tx.send((Event::Enter(), addr)).await;
|
let _ = sender_tx.send((Event::Enter(), addr)).await;
|
||||||
@@ -148,3 +151,34 @@ async fn handle_capture_event(
|
|||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn spawn_hook_command(server: &Server, handle: ClientHandle) {
|
||||||
|
let Some(cmd) = server
|
||||||
|
.client_manager
|
||||||
|
.borrow()
|
||||||
|
.get(handle)
|
||||||
|
.and_then(|(c, _)| c.cmd.clone())
|
||||||
|
else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
tokio::task::spawn_local(async move {
|
||||||
|
log::info!("spawning command!");
|
||||||
|
let mut child = match Command::new("sh").arg("-c").arg(cmd.as_str()).spawn() {
|
||||||
|
Ok(c) => c,
|
||||||
|
Err(e) => {
|
||||||
|
log::warn!("could not execute cmd: {e}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
match child.wait().await {
|
||||||
|
Ok(s) => {
|
||||||
|
if s.success() {
|
||||||
|
log::info!("{cmd} exited successfully");
|
||||||
|
} else {
|
||||||
|
log::warn!("{cmd} exited with {s}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => log::warn!("{cmd}: {e}"),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|||||||
@@ -111,16 +111,17 @@ async fn handle_frontend_event(
|
|||||||
}
|
}
|
||||||
FrontendRequest::Activate(handle, active) => {
|
FrontendRequest::Activate(handle, active) => {
|
||||||
if active {
|
if active {
|
||||||
activate_client(server, frontend, capture, emulate, handle).await;
|
activate_client(server, capture, emulate, handle).await;
|
||||||
} else {
|
} else {
|
||||||
deactivate_client(server, frontend, capture, emulate, handle).await;
|
deactivate_client(server, 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, emulate, handle).await;
|
remove_client(server, capture, emulate, handle).await;
|
||||||
|
broadcast(frontend, FrontendEvent::Deleted(handle)).await;
|
||||||
}
|
}
|
||||||
FrontendRequest::Enumerate() => {
|
FrontendRequest::Enumerate() => {
|
||||||
let clients = server
|
let clients = server
|
||||||
@@ -131,25 +132,24 @@ async fn handle_frontend_event(
|
|||||||
.collect();
|
.collect();
|
||||||
broadcast(frontend, FrontendEvent::Enumerate(clients)).await;
|
broadcast(frontend, FrontendEvent::Enumerate(clients)).await;
|
||||||
}
|
}
|
||||||
|
FrontendRequest::GetState(handle) => {
|
||||||
|
broadcast_client(server, frontend, handle).await;
|
||||||
|
}
|
||||||
FrontendRequest::Terminate() => {
|
FrontendRequest::Terminate() => {
|
||||||
log::info!("terminating gracefully...");
|
log::info!("terminating gracefully...");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
FrontendRequest::UpdateFixIps(handle, fix_ips) => {
|
FrontendRequest::UpdateFixIps(handle, fix_ips) => {
|
||||||
update_fix_ips(server, resolve_tx, handle, fix_ips).await;
|
update_fix_ips(server, resolve_tx, handle, fix_ips).await;
|
||||||
broadcast_client_update(server, frontend, handle).await;
|
|
||||||
}
|
}
|
||||||
FrontendRequest::UpdateHostname(handle, hostname) => {
|
FrontendRequest::UpdateHostname(handle, hostname) => {
|
||||||
update_hostname(server, resolve_tx, handle, hostname).await;
|
update_hostname(server, resolve_tx, handle, hostname).await;
|
||||||
broadcast_client_update(server, frontend, handle).await;
|
|
||||||
}
|
}
|
||||||
FrontendRequest::UpdatePort(handle, port) => {
|
FrontendRequest::UpdatePort(handle, port) => {
|
||||||
update_port(server, handle, port).await;
|
update_port(server, handle, port).await;
|
||||||
broadcast_client_update(server, frontend, handle).await;
|
|
||||||
}
|
}
|
||||||
FrontendRequest::UpdatePosition(handle, pos) => {
|
FrontendRequest::UpdatePosition(handle, pos) => {
|
||||||
update_pos(server, handle, capture, emulate, pos).await;
|
update_pos(server, handle, capture, emulate, pos).await;
|
||||||
broadcast_client_update(server, frontend, handle).await;
|
|
||||||
}
|
}
|
||||||
FrontendRequest::ResolveDns(handle) => {
|
FrontendRequest::ResolveDns(handle) => {
|
||||||
let hostname = server
|
let hostname = server
|
||||||
@@ -181,15 +181,13 @@ pub async fn add_client(server: &Server, frontend: &mut FrontendListener) {
|
|||||||
|
|
||||||
pub async fn deactivate_client(
|
pub async fn deactivate_client(
|
||||||
server: &Server,
|
server: &Server,
|
||||||
frontend: &mut FrontendListener,
|
|
||||||
capture: &Sender<CaptureEvent>,
|
capture: &Sender<CaptureEvent>,
|
||||||
emulate: &Sender<EmulationEvent>,
|
emulate: &Sender<EmulationEvent>,
|
||||||
handle: ClientHandle,
|
handle: ClientHandle,
|
||||||
) {
|
) {
|
||||||
let state = match server.client_manager.borrow_mut().get_mut(handle) {
|
match server.client_manager.borrow_mut().get_mut(handle) {
|
||||||
Some((_, s)) => {
|
Some((_, s)) => {
|
||||||
s.active = false;
|
s.active = false;
|
||||||
s.clone()
|
|
||||||
}
|
}
|
||||||
None => return,
|
None => return,
|
||||||
};
|
};
|
||||||
@@ -197,13 +195,10 @@ pub async fn deactivate_client(
|
|||||||
let event = ClientEvent::Destroy(handle);
|
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::StateChange(handle, state);
|
|
||||||
broadcast(frontend, event).await;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn activate_client(
|
pub async fn activate_client(
|
||||||
server: &Server,
|
server: &Server,
|
||||||
frontend: &mut FrontendListener,
|
|
||||||
capture: &Sender<CaptureEvent>,
|
capture: &Sender<CaptureEvent>,
|
||||||
emulate: &Sender<EmulationEvent>,
|
emulate: &Sender<EmulationEvent>,
|
||||||
handle: ClientHandle,
|
handle: ClientHandle,
|
||||||
@@ -217,14 +212,13 @@ pub async fn activate_client(
|
|||||||
let other = server.client_manager.borrow_mut().find_client(pos);
|
let other = server.client_manager.borrow_mut().find_client(pos);
|
||||||
if let Some(other) = other {
|
if let Some(other) = other {
|
||||||
if other != handle {
|
if other != handle {
|
||||||
deactivate_client(server, frontend, capture, emulate, other).await;
|
deactivate_client(server, capture, emulate, other).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* activate the client */
|
/* activate the client */
|
||||||
let state = if let Some((_, s)) = server.client_manager.borrow_mut().get_mut(handle) {
|
if let Some((_, s)) = server.client_manager.borrow_mut().get_mut(handle) {
|
||||||
s.active = true;
|
s.active = true;
|
||||||
s.clone()
|
|
||||||
} else {
|
} else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
@@ -233,13 +227,10 @@ pub async fn activate_client(
|
|||||||
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::StateChange(handle, state);
|
|
||||||
broadcast(frontend, event).await;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn remove_client(
|
pub async fn remove_client(
|
||||||
server: &Server,
|
server: &Server,
|
||||||
frontend: &mut FrontendListener,
|
|
||||||
capture: &Sender<CaptureEvent>,
|
capture: &Sender<CaptureEvent>,
|
||||||
emulate: &Sender<EmulationEvent>,
|
emulate: &Sender<EmulationEvent>,
|
||||||
handle: ClientHandle,
|
handle: ClientHandle,
|
||||||
@@ -258,9 +249,6 @@ pub async fn remove_client(
|
|||||||
let _ = capture.send(CaptureEvent::ClientEvent(destroy)).await;
|
let _ = capture.send(CaptureEvent::ClientEvent(destroy)).await;
|
||||||
let _ = emulate.send(EmulationEvent::ClientEvent(destroy)).await;
|
let _ = emulate.send(EmulationEvent::ClientEvent(destroy)).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
let event = FrontendEvent::Deleted(handle);
|
|
||||||
broadcast(frontend, event).await;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn update_fix_ips(
|
async fn update_fix_ips(
|
||||||
@@ -356,11 +344,11 @@ async fn update_pos(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn broadcast_client_update(
|
async fn broadcast_client(server: &Server, frontend: &mut FrontendListener, handle: ClientHandle) {
|
||||||
server: &Server,
|
let client = server.client_manager.borrow().get(handle).cloned();
|
||||||
frontend: &mut FrontendListener,
|
if let Some((config, state)) = client {
|
||||||
handle: ClientHandle,
|
broadcast(frontend, FrontendEvent::State(handle, config, state)).await;
|
||||||
) {
|
} else {
|
||||||
let (client, _) = server.client_manager.borrow().get(handle).unwrap().clone();
|
broadcast(frontend, FrontendEvent::NoSuchClient(handle)).await;
|
||||||
broadcast(frontend, FrontendEvent::Updated(handle, client)).await;
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ pub fn new(
|
|||||||
Ok(ips) => ips,
|
Ok(ips) => ips,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
log::warn!("could not resolve host '{host}': {e}");
|
log::warn!("could not resolve host '{host}': {e}");
|
||||||
continue;
|
vec![]
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -59,14 +59,10 @@ async fn notify_state_change(
|
|||||||
server: &mut Server,
|
server: &mut Server,
|
||||||
handle: ClientHandle,
|
handle: ClientHandle,
|
||||||
) {
|
) {
|
||||||
let state = server
|
let state = server.client_manager.borrow_mut().get_mut(handle).cloned();
|
||||||
.client_manager
|
if let Some((config, state)) = state {
|
||||||
.borrow_mut()
|
|
||||||
.get_mut(handle)
|
|
||||||
.map(|(_, s)| s.clone());
|
|
||||||
if let Some(state) = state {
|
|
||||||
let _ = frontend
|
let _ = frontend
|
||||||
.send(FrontendEvent::StateChange(handle, state))
|
.send(FrontendEvent::State(handle, config, state))
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user