use axum::{
extract::{ws::WebSocket, WebSocketUpgrade},
response::IntoResponse,
routing::get,
Router,
};
use serde::{Deserialize, Serialize};
use tower_http::services::ServeDir;
use winput::{press, release, send, send_inputs, send_str, Action, Button, Input, Mouse, Vk};
#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
enum ClickKind {
Left,
LeftHold,
LeftRelease,
Middle,
Right,
}
#[derive(Debug, Serialize, Deserialize)]
struct Vector2 {
x: f32,
y: f32,
}
#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
enum ActionKind {
Cut,
Copy,
Paste,
Enter,
}
#[derive(Debug, Serialize, Deserialize)]
#[serde(untagged)]
enum Command {
Difference { difference: Vector2 },
Click { click: ClickKind },
Scroll { scroll: Vector2 },
Text { text: String },
Action { action: ActionKind },
}
impl Command {
fn execute(&self) {
match self {
Command::Difference { difference } => {
mouse_mouve(difference);
}
Command::Click { click } => {
mouse_click(click);
}
Command::Scroll { scroll } => {
mouse_scroll(scroll);
}
Command::Text { text } => {
keyboard_text(text);
}
Command::Action { action } => {
keyboard_action(action);
}
};
}
}
fn mouse_mouve(difference: &Vector2) {
let mouse_position = Mouse::position();
if mouse_position.is_err() {
println!("Err (mouse_position): {}", mouse_position.unwrap_err());
return;
}
let (x, y) = mouse_position.unwrap();
let position_setting = Mouse::set_position(x + difference.x as i32, y + difference.y as i32);
if position_setting.is_err() {
println!("Err (position_setting): {}", position_setting.unwrap_err());
}
}
fn mouse_click(kind: &ClickKind) {
match kind {
ClickKind::Left => send(Button::Left),
ClickKind::LeftHold => press(Button::Left),
ClickKind::LeftRelease => release(Button::Left),
ClickKind::Middle => send(Button::Middle),
ClickKind::Right => send(Button::Right),
};
}
fn mouse_scroll(amount: &Vector2) {
if amount.x != 0. {
Mouse::scrollh(amount.x);
}
if amount.y != 0. {
Mouse::scroll(amount.y);
}
}
fn keyboard_text(text: &str) {
send_str(text);
}
fn keyboard_action(action: &ActionKind) {
match action {
ActionKind::Cut => {
send_key_with_control_modifier(Vk::X);
}
ActionKind::Copy => {
send_key_with_control_modifier(Vk::C);
}
ActionKind::Paste => {
send_key_with_control_modifier(Vk::V);
}
ActionKind::Enter => {
send(Vk::Enter);
}
};
}
fn send_key_with_control_modifier(key: Vk) {
let inputs = [
Input::from_vk(Vk::Control, Action::Press),
Input::from_vk(key, Action::Press),
Input::from_vk(key, Action::Release),
Input::from_vk(Vk::Control, Action::Release),
];
send_inputs(inputs);
}
#[tokio::main]
async fn main() {
let app = Router::new()
.nest_service("/", ServeDir::new("public"))
.route("/realtime", get(realtime_handler));
let listener = tokio::net::TcpListener::bind("0.0.0.0:3004").await.unwrap();
axum::serve(listener, app).await.unwrap();
}
#[axum::debug_handler]
async fn realtime_handler(ws: WebSocketUpgrade) -> impl IntoResponse {
ws.on_upgrade(|ws: WebSocket| async { ws_client(ws).await })
}
async fn ws_client(mut ws: WebSocket) {
while let Some(msg) = ws.recv().await {
if msg.is_err() {
println!("Err (msg): {}", msg.unwrap_err());
continue;
}
let msg = msg.unwrap();
let msg = msg.into_text();
if msg.is_err() {
println!("Err (and_then into_text): {}", msg.unwrap_err());
continue;
}
let msg = msg.unwrap();
if msg.is_empty() {
println!("Err (msg.is_empty): message is empty");
continue;
}
let parsed_command = serde_json::from_str::<Command>(&msg);
if parsed_command.is_err() {
println!("Err (parse json): {}", parsed_command.unwrap_err());
println!("Err (parse json): msg={}", msg);
continue;
}
let parsed_command: Command = parsed_command.unwrap();
parsed_command.execute();
}
}