This commit is contained in:
Mona Mayrhofer 2026-03-03 18:08:02 +01:00
parent 6d466c65a4
commit 579869f2f0
WARNING! Although there is a key with this ID in the database it does not verify this commit! This commit is SUSPICIOUS.
GPG key ID: 374AB152BDEBA1AE
21 changed files with 168 additions and 2771 deletions

View file

@ -3,6 +3,11 @@ name = "cursor-move-webapp"
version = "0.1.0"
authors = ["Mona Mayrhofer <mona.mayrhofer@proton.me>"]
edition = "2024"
repository = "https://github.com/mona-mayrhofer/cursor-mover-app"
categories = ["tools"]
keywords = ["tools", "cursor", "remote"]
license = "EUPL-1.2"
description = "A web application for controlling your cursor and keyboard via a smartphone browser."
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@ -10,25 +15,25 @@ edition = "2024"
dioxus = { version = "0.7.3", features = ["router", "fullstack", "logger"] }
dioxus-html = { version = "0.7.3", features = ["serialize"] }
serde = { version = "1.0.228", features = ["derive"] }
wasmtimer = "0.4.3"
wayland-client = { version = "0.31.12", optional = true }
wayland-protocols-wlr = { version = "0.3.10", features = ["client"], optional = true }
tokio = {version = "1.49.0", optional = true}
rustix = { version = "1.1.4", optional = true, features = ["time"] }
rustix = { version = "1.1.4", optional = true, features = ["time"], default-features = false }
wayland-protocols-misc = { version = "0.3.10", features = ["client"], optional = true }
wayland-protocols = { version = "0.32.10", features = ["client", "staging"], optional = true }
xkb = {version = "0.3.0", optional = true}
memfile = {version = "0.3.2", optional = true}
wasmtimer = "0.4.3"
[features]
default = ["web"]
# The feature that are only required for the web = ["dioxus/web"] build target should be optional and only enabled in the web = ["dioxus/web"] feature
web = ["dioxus/web"]
# The feature that are only required for the desktop = ["dioxus/desktop"] build target should be optional and only enabled in the desktop = ["dioxus/desktop"] feature
desktop = ["dioxus/desktop"]
#desktop = ["dioxus/desktop"]
# The feature that are only required for the mobile = ["dioxus/mobile"] build target should be optional and only enabled in the mobile = ["dioxus/mobile"] feature
mobile = ["dioxus/mobile"]
#mobile = ["dioxus/mobile"]
# The feature that are only required for the server = ["dioxus/server"] build target should be optional and only enabled in the server = ["dioxus/server"] feature
server = ["dioxus/server",
"dep:wayland-client",
@ -37,7 +42,6 @@ server = ["dioxus/server",
"dep:wayland-protocols",
"dep:tokio",
"dep:rustix",
"dep:xkb",
"dep:memfile"
]

View file

@ -1,9 +1,6 @@
use dioxus::{
fullstack::{CborEncoding, WebSocketOptions, Websocket, extract::State, use_websocket},
html::{
geometry::{ElementSpace, euclid::Point2D},
input_data::MouseButton,
},
html::input_data::MouseButton,
logger::tracing,
prelude::*,
};

View file

@ -12,7 +12,7 @@ pub fn KeyboardArea(onevent: EventHandler<ClientEvent>) -> Element {
let v = evt.value();
input_state.set(v.clone());
onevent.call(ClientEvent::TextInputEvent { text: v })
onevent.call(ClientEvent::TextInputEvent { text: v });
});
let key_press_handler = use_callback(move |evt: Event<KeyboardData>| {
if input_state.read().is_empty() {

View file

@ -1,8 +1,4 @@
use std::{
collections::{HashMap, VecDeque},
ops::Sub,
time::Duration,
};
use std::{collections::HashMap, ops::Sub, time::Duration};
use dioxus::{
html::{

View file

@ -38,6 +38,7 @@ const BUTTON_RIGHT: u32 = 0x111;
const BUTTON_MIDDLE: u32 = 0x112;
// https://wayland.app/protocols/wayland#wl_keyboard:enum:keymap_format
#[expect(unused)]
const NO_KEYMAP: u32 = 0;
const XKB_V1: u32 = 1;
@ -233,8 +234,11 @@ enum InputProxyServiceState {
keymap: Keymap,
},
Running {
#[expect(unused)]
seat: WlSeat,
#[expect(unused)]
virtual_pointer_manager: ZwlrVirtualPointerManagerV1,
#[expect(unused)]
virtual_keyboard_manager: ZwpVirtualKeyboardManagerV1,
virtual_pointer: ZwlrVirtualPointerV1,
@ -282,7 +286,7 @@ impl InputProxyServiceState {
}
}
fn input_method_mut(&mut self) -> Option<&mut InputMethod> {
const fn input_method_mut(&mut self) -> Option<&mut InputMethod> {
match self {
Self::Running { input_method, .. } => Some(input_method),
Self::Incomplete { .. } => None,
@ -480,7 +484,6 @@ impl Dispatch<ZwpInputMethodV2, ()> for InputProxyServiceState {
_conn: &Connection,
_qhandle: &QueueHandle<Self>,
) {
panic!();
tracing::warn!("Unknown event received from ZwpInputMethodV2");
}
}

View file

@ -1,9 +1,12 @@
#![expect(unused)]
use dioxus::logger::tracing;
fn is_key_string(s: &str) -> bool {
s.chars().all(|c| !c.is_control()) && s.chars().skip(1).all(|c| !c.is_ascii())
}
#[expect(clippy::too_many_lines)]
pub fn web_key_to_linux_keycode(s: &str) -> Option<u32> {
tracing::info!("Converting {s}");
match s {

View file

@ -57,16 +57,14 @@ impl MouseFilterBuffer {
let mut i = self.buffer.iter();
let mut last = if let Some(it) = i.find(|it| it.time >= start_time) {
it
} else {
let Some(mut last) = i.find(|it| it.time >= start_time) else {
return Vector2D::zero();
};
let start_time = last.time;
let mut last_time = last.time;
for point in i {
total_distance += (point.position - last.position);
total_distance += point.position - last.position;
last = point;
last_time = point.time;
}

View file

@ -1,4 +1,4 @@
use crate::components::{Controls, MouseArea};
use crate::components::Controls;
use dioxus::prelude::*;
/// The Home page component that will be rendered when the current route is `[Route::Home]`

View file

@ -1,18 +0,0 @@
[package]
name = "cursor-mover-app"
version = "0.1.0"
edition = "2024"
license = "EUPL-1.2"
description = "A web-application that lets you remotely control your cursor."
repository = "https://forgejo.monalith.xyz/Nionidh/cursor-mover-app"
keywords = ["web", "application", "cursor", "control"]
categories = ["web-programming"]
readme = "README.md"
[dependencies]
tokio = { version = "1.49.0", features = ["full"] }
wayland-client = "0.31.12"
wayland-protocols-wlr = { version = "0.3.10", features = ["client"] }
[lints]
workspace = true

View file

@ -1 +0,0 @@
The main application

View file

@ -1,133 +0,0 @@
use std::{
alloc::System,
f64,
future::poll_fn,
time::{Duration, SystemTime, UNIX_EPOCH},
};
use tokio::{
select,
time::{self, Instant},
};
use wayland_client::{Connection, Dispatch, Proxy, QueueHandle, protocol::wl_registry};
use wayland_protocols_wlr::virtual_pointer::v1::client::{
zwlr_virtual_pointer_manager_v1::ZwlrVirtualPointerManagerV1,
zwlr_virtual_pointer_v1::ZwlrVirtualPointerV1,
};
#[derive(Default)]
struct AppData {
virtual_pointer: Option<ZwlrVirtualPointerV1>,
}
impl Dispatch<ZwlrVirtualPointerV1, ()> for AppData {
fn event(
state: &mut Self,
proxy: &ZwlrVirtualPointerV1,
event: <ZwlrVirtualPointerV1 as Proxy>::Event,
data: &(),
conn: &Connection,
qhandle: &QueueHandle<Self>,
) {
println!("VPointerData");
}
}
impl Dispatch<ZwlrVirtualPointerManagerV1, ()> for AppData {
fn event(
state: &mut Self,
proxy: &ZwlrVirtualPointerManagerV1,
event: <ZwlrVirtualPointerManagerV1 as Proxy>::Event,
data: &(),
conn: &Connection,
qhandle: &QueueHandle<Self>,
) {
println!("ZwlrEvent")
}
}
impl Dispatch<wl_registry::WlRegistry, ()> for AppData {
fn event(
app_data: &mut Self,
registry: &wl_registry::WlRegistry,
event: wl_registry::Event,
_: &(),
_: &Connection,
queue_handle: &QueueHandle<Self>,
) {
println!("WlRegistry Event");
if let wl_registry::Event::Global {
name,
interface,
version,
} = event
&& interface == "zwlr_virtual_pointer_manager_v1"
{
app_data.virtual_pointer.get_or_insert_with(|| {
let manager = registry.bind::<ZwlrVirtualPointerManagerV1, _, _>(
name,
version,
queue_handle,
(),
);
let pointer = manager.create_virtual_pointer(None, queue_handle, ());
println!("Virtual pointer manager created");
pointer
});
}
}
}
#[tokio::main]
async fn main() {
let connection = Connection::connect_to_env().unwrap();
let display = connection.display();
let mut event_queue = connection.new_event_queue();
let queue_handle = event_queue.handle();
let _registry = display.get_registry(&queue_handle, ());
println!("Advertized globals:");
let mut appdata = AppData::default();
event_queue.roundtrip(&mut appdata).unwrap();
let mut interval = time::interval(Duration::from_millis(15));
let start = Instant::now();
loop {
if Instant::now().duration_since(start) > Duration::from_secs(5) {
break;
}
select! {
poll = poll_fn(|cx| event_queue.poll_dispatch_pending(cx, &mut appdata)) => {
println!("Did the mash");
},
now = interval.tick() => {
if let Some(pointer) = appdata.virtual_pointer.as_mut() {
handle_pointer_motion(now, &pointer);
}
event_queue.flush().unwrap();
},
}
}
}
pub fn handle_pointer_motion(
time: Instant,
pointer: &ZwlrVirtualPointerV1,
) {
let time = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_millis();
let x = ((time as f64 / 1000.0 * f64::consts::PI).sin() * 10.0);
let y = ((time as f64 / 1000.0 * f64::consts::PI).cos() * 10.0);
pointer.motion(time as u32, x, y);
pointer.frame();
}