added mapped cursor position

This commit is contained in:
2025-11-25 15:00:18 +08:00
parent 4f90bd754a
commit b22fdac39c
7 changed files with 75 additions and 28 deletions

View File

@@ -37,5 +37,8 @@ pub async fn initialize_session() {
overlay_fullscreen(&window).unwrap(); overlay_fullscreen(&window).unwrap();
window.set_ignore_cursor_events(true).unwrap(); window.set_ignore_cursor_events(true).unwrap();
#[cfg(debug_assertions)]
webview_window.open_devtools();
println!("Scene window initialized."); println!("Scene window initialized.");
} }

View File

@@ -1,4 +1,4 @@
use crate::services::cursor::stream_cursor_position; use crate::services::cursor::channel_cursor_positions;
static APP_HANDLE: std::sync::OnceLock<tauri::AppHandle<tauri::Wry>> = std::sync::OnceLock::new(); static APP_HANDLE: std::sync::OnceLock<tauri::AppHandle<tauri::Wry>> = std::sync::OnceLock::new();
@@ -37,7 +37,7 @@ pub fn run() {
tauri::Builder::default() tauri::Builder::default()
.plugin(tauri_plugin_opener::init()) .plugin(tauri_plugin_opener::init())
.plugin(tauri_plugin_positioner::init()) .plugin(tauri_plugin_positioner::init())
.invoke_handler(tauri::generate_handler![stream_cursor_position]) .invoke_handler(tauri::generate_handler![channel_cursor_positions])
.setup(|app| { .setup(|app| {
APP_HANDLE APP_HANDLE
.set(app.handle().to_owned()) .set(app.handle().to_owned())

View File

@@ -3,6 +3,8 @@ use serde::Serialize;
use std::time::Duration; use std::time::Duration;
use tauri::ipc::Channel; use tauri::ipc::Channel;
use crate::get_app_handle;
#[derive(Clone, Serialize)] #[derive(Clone, Serialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct CursorPosition { pub struct CursorPosition {
@@ -10,22 +12,53 @@ pub struct CursorPosition {
pub y: i32, pub y: i32,
} }
#[derive(Clone, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct CursorPositions {
pub raw: CursorPosition,
pub mapped: CursorPosition,
}
fn map_to_grid(
pos: &CursorPosition,
grid_size: i32,
screen_w: i32,
screen_h: i32,
) -> CursorPosition {
CursorPosition {
x: pos.x * grid_size / screen_w,
y: pos.y * grid_size / screen_h,
}
}
#[tauri::command] #[tauri::command]
pub async fn stream_cursor_position(on_event: Channel<CursorPosition>) { pub async fn channel_cursor_positions(on_event: Channel<CursorPositions>) {
let app_handle = get_app_handle();
let primary_monitor = app_handle.primary_monitor().unwrap().unwrap();
let monitor_dimensions = primary_monitor.size();
let logical_monitor_dimensions: tauri::LogicalSize<i32> =
monitor_dimensions.to_logical(primary_monitor.scale_factor());
let device_state = let device_state =
DeviceEventsHandler::new(Duration::from_millis(200)).expect("Failed to start event loop"); DeviceEventsHandler::new(Duration::from_millis(200)).expect("Failed to start event loop");
let _guard = device_state.on_mouse_move(move |position| { let _guard = device_state.on_mouse_move(move |position| {
let pos = CursorPosition { let raw = CursorPosition {
x: position.0, x: position.0,
y: position.1, y: position.1,
}; };
// Ignore send error (e.g. frontend closed channel) let mapped = map_to_grid(
let _ = on_event.send(pos); &raw,
600,
logical_monitor_dimensions.width,
logical_monitor_dimensions.height,
);
let positions = CursorPositions { raw, mapped };
let _ = on_event.send(positions);
}); });
// Prevent function from exiting
loop { loop {
tokio::time::sleep(Duration::ZERO).await; // for whatever reason this sleep is not taking effect but it
// does reduce CPU usage on my Mac from 100% to 6% so...cool!
tokio::time::sleep(Duration::from_millis(1000)).await
} }
} }

View File

@@ -5,20 +5,19 @@ pub static SCENE_WINDOW_LABEL: &str = "scene";
pub fn overlay_fullscreen(window: &tauri::Window) -> Result<(), tauri::Error> { pub fn overlay_fullscreen(window: &tauri::Window) -> Result<(), tauri::Error> {
// Get the primary monitor // Get the primary monitor
let monitor = get_app_handle().primary_monitor()?.unwrap(); let monitor = get_app_handle().primary_monitor()?.unwrap();
let monitor_position = monitor.position();
let monitor_size = monitor.size();
// Get the work area (usable space, excluding menu bar/dock/notch) // Set window position to top-left
let work_area = monitor.work_area();
// Set window position to top-left of the work area
window.set_position(tauri::PhysicalPosition { window.set_position(tauri::PhysicalPosition {
x: work_area.position.x, x: monitor_position.x,
y: work_area.position.y, y: monitor_position.y,
})?; })?;
// Set window size to match work area size // Set window size to match screen size
window.set_size(tauri::PhysicalSize { window.set_size(tauri::PhysicalSize {
width: work_area.size.width, width: monitor_size.width,
height: work_area.size.height, height: monitor_size.height,
})?; })?;
Ok(()) Ok(())

View File

@@ -1,12 +1,19 @@
import { Channel, invoke } from "@tauri-apps/api/core"; import { Channel, invoke } from "@tauri-apps/api/core";
import { writable } from "svelte/store"; import { writable } from "svelte/store";
export let cursorPositionOnScreen = writable<{ x: number; y: number }>({ x: 0, y: 0 }); export type CursorPositions = {
raw: { x: number; y: number };
mapped: { x: number; y: number };
};
export let cursorPositionOnScreen = writable<CursorPositions>({
raw: { x: 0, y: 0 },
mapped: { x: 0, y: 0 },
});
export function initCursorPositionStream() { export function initChannelCursorPosition() {
const channel = new Channel<{ x: number; y: number }>(); const channel = new Channel<CursorPositions>();
channel.onmessage = (pos) => { channel.onmessage = (pos) => {
cursorPositionOnScreen.set(pos); cursorPositionOnScreen.set(pos);
}; };
invoke("stream_cursor_position", { onEvent: channel }); invoke("channel_cursor_positions", { onEvent: channel });
} }

View File

@@ -4,6 +4,6 @@
// See: https://v2.tauri.app/start/frontend/sveltekit/ for more info // See: https://v2.tauri.app/start/frontend/sveltekit/ for more info
export const ssr = false; export const ssr = false;
import "../app.css"; import "../app.css";
import { initCursorPositionStream } from "../channels/cursor"; import { initChannelCursorPosition } from "../channels/cursor";
initCursorPositionStream(); initChannelCursorPosition();

View File

@@ -2,7 +2,7 @@
import { cursorPositionOnScreen } from "../../channels/cursor"; import { cursorPositionOnScreen } from "../../channels/cursor";
</script> </script>
<div class="w-svw h-svh p-4"> <div class="w-svw h-svh p-4 relative">
<div <div
class="size-max mx-auto bg-base-100 border-base-200 border px-4 py-3 rounded-xl" class="size-max mx-auto bg-base-100 border-base-200 border px-4 py-3 rounded-xl"
> >
@@ -10,9 +10,14 @@
<p class="text-xl">Friendolls</p> <p class="text-xl">Friendolls</p>
<p class="text-sm opacity-50">Scene Screen</p> <p class="text-sm opacity-50">Scene Screen</p>
<div class="mt-4"> <div class="mt-4">
<span class="font-mono" <span class="font-mono">
>Cursor: ({$cursorPositionOnScreen.x}, {$cursorPositionOnScreen.y})</span Cursor: ({$cursorPositionOnScreen.raw.x}, {$cursorPositionOnScreen.raw
> .y})
</span>
<span class="font-mono">
Cursor: ({$cursorPositionOnScreen.mapped.x}, {$cursorPositionOnScreen
.mapped.y})
</span>
</div> </div>
</div> </div>
</div> </div>