use dioxus::{ fullstack::{CborEncoding, WebSocketOptions, Websocket, extract::State, use_websocket}, html::geometry::{ElementSpace, euclid::Point2D}, 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::>>(|| 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); } } }); rsx! { div { class: Styles::mouse_area, onpointermove: move |evt| { 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; }); } }, onpointerdown: move |evt| { let point = evt.element_coordinates(); *last_cursor_position.write() = Some(point); } } } } #[derive(Serialize, Deserialize, Debug)] enum ClientEvent { MouseMove { dx: f64, dy: f64 }, } #[derive(Serialize, Deserialize, Debug)] enum ServerEvent { Ping, } #[expect(clippy::unused_async)] #[get("/api/mouse_move_ws", mouse_service: State)] async fn mouse_move( options: WebSocketOptions ) -> Result> { Ok(options.on_upgrade(move |mut socket| async move { _ = socket.send(ServerEvent::Ping).await; while let Ok(ClientEvent::MouseMove { dx, dy }) = socket.recv().await { mouse_service.move_mouse(dx, dy).await; } })) }