cursor-mover-app/crates/cursor-move-webapp/src/components/mouse_area.rs

137 lines
4.2 KiB
Rust

use dioxus::{
fullstack::{CborEncoding, WebSocketOptions, Websocket, extract::State, use_websocket},
html::{
geometry::{ElementSpace, euclid::Point2D},
input_data::MouseButton,
},
logger::tracing,
prelude::*,
};
use serde::{Deserialize, Serialize};
#[component]
pub fn MouseArea() -> Element {
#[css_module("/assets/styling/mouse_area.module.css")]
struct Styles;
let mut last_cursor_position = use_signal::<Option<Point2D<f64, ElementSpace>>>(|| None);
let mut socket = use_websocket(move || mouse_move(WebSocketOptions::new()));
use_future(move || async move {
loop {
// Wait for the socket to connect
_ = socket.connect().await;
// Loop poll with recv. Throws an error when the connection closes, making it possible
// to run code before the socket re-connects when the name input changes
while let Ok(message) = socket.recv().await {
tracing::info!("Received message: {:?}", message);
}
}
});
let pointer_move_handler = use_callback(move |evt: Event<PointerData>| {
if evt.held_buttons().contains(MouseButton::Primary) {
evt.prevent_default();
let point = evt.element_coordinates();
let last_position = last_cursor_position.write().replace(point);
if let Some(last_position) = last_position {
let delta = point - last_position;
spawn(async move {
_ = socket
.send(ClientEvent::MouseMove {
dx: delta.x,
dy: delta.y,
})
.await;
});
}
}
});
let pointer_down_handler = use_callback(move |evt: Event<PointerData>| {
let point = evt.element_coordinates();
*last_cursor_position.write() = Some(point);
});
let pointer_click_handler = use_callback(move |evt: Event<MouseData>| {
spawn(async move {
_ = socket.send(ClientEvent::Click).await;
});
});
let key_down_handler = use_callback(move |evt: Event<KeyboardData>| {
tracing::info!("Keydown");
spawn(async move {
_ = socket
.send(ClientEvent::KeyEvent {
key: evt.key().to_string(),
is_pressed: true,
})
.await;
});
});
let key_up_handler = use_callback(move |evt: Event<KeyboardData>| {
spawn(async move {
_ = socket
.send(ClientEvent::KeyEvent {
key: evt.key().to_string(),
is_pressed: false,
})
.await;
});
});
rsx! {
div {
input { onkeydown: key_down_handler, onkeyup: key_up_handler}
div {
class: Styles::mouse_area,
onpointermove: pointer_move_handler,
onpointerdown: pointer_down_handler,
onclick: pointer_click_handler
}
}
}
}
#[derive(Serialize, Deserialize, Debug)]
enum ClientEvent {
MouseMove { dx: f64, dy: f64 },
Click,
KeyEvent { key: String, is_pressed: bool },
}
#[derive(Serialize, Deserialize, Debug)]
enum ServerEvent {
Ping,
}
#[expect(clippy::unused_async)]
#[get("/api/mouse_move_ws", mouse_service: State<crate::server::input_proxy_service::InputProxyService>)]
async fn mouse_move(
options: WebSocketOptions
) -> Result<Websocket<ClientEvent, ServerEvent, CborEncoding>> {
Ok(options.on_upgrade(move |mut socket| async move {
_ = socket.send(ServerEvent::Ping).await;
while let Ok(event) = socket.recv().await {
match event {
ClientEvent::MouseMove { dx, dy } => {
mouse_service.move_mouse(dx, dy).await;
},
ClientEvent::Click => {
mouse_service.click().await;
},
ClientEvent::KeyEvent { key, is_pressed } => {
mouse_service.key_event(key, is_pressed).await;
},
}
}
}))
}