diff --git a/desktop/src/app.rs b/desktop/src/app.rs index 9cab6256..1648616f 100644 --- a/desktop/src/app.rs +++ b/desktop/src/app.rs @@ -5,7 +5,7 @@ use std::sync::mpsc::{Receiver, Sender, SyncSender}; use std::thread; use std::time::{Duration, Instant}; use winit::application::ApplicationHandler; -use winit::dpi::PhysicalSize; +use winit::dpi::{PhysicalPosition, PhysicalSize}; use winit::event::{ButtonSource, ElementState, MouseButton, WindowEvent}; use winit::event_loop::{ActiveEventLoop, ControlFlow}; use winit::window::WindowId; @@ -27,6 +27,8 @@ pub(crate) struct App { window_size: PhysicalSize, window_maximized: bool, window_fullscreen: bool, + pointer_position: PhysicalPosition, + pointer_lock_position: Option>, ui_scale: f64, app_event_receiver: Receiver, app_event_scheduler: AppEventScheduler, @@ -84,6 +86,8 @@ impl App { window_size: PhysicalSize { width: 0, height: 0 }, window_maximized: false, window_fullscreen: false, + pointer_position: Default::default(), + pointer_lock_position: Default::default(), ui_scale: 1., app_event_receiver, app_event_scheduler, @@ -329,6 +333,12 @@ impl App { window.clipboard_write(content); } } + DesktopFrontendMessage::PointerLock => { + self.pointer_lock_position = Some(self.pointer_position); + if let Some(window) = &self.window { + window.start_pointer_lock(); + } + } DesktopFrontendMessage::WindowClose => { self.app_event_scheduler.schedule(AppEvent::CloseWindow); } @@ -480,6 +490,26 @@ impl ApplicationHandler for App { } fn window_event(&mut self, event_loop: &dyn ActiveEventLoop, _window_id: WindowId, event: WindowEvent) { + // Handle pointer lock release + if let Some(pointer_lock_position) = self.pointer_lock_position + && let WindowEvent::PointerButton { + state: ElementState::Released, + button: ButtonSource::Mouse(MouseButton::Left), + .. + } = event + { + self.pointer_lock_position = None; + if let Some(window) = &self.window { + window.end_pointer_lock(); + } + self.cef_context.handle_window_event(&WindowEvent::PointerMoved { + device_id: None, + position: pointer_lock_position, + primary: true, + source: winit::event::PointerSource::Mouse, + }); + } + self.cef_context.handle_window_event(&event); match event { @@ -556,6 +586,13 @@ impl ApplicationHandler for App { self.app_event_scheduler.schedule(AppEvent::DesktopWrapperMessage(message)); } } + + WindowEvent::PointerMoved { position, .. } | WindowEvent::PointerLeft { position: Some(position), .. } | WindowEvent::PointerEntered { position, .. } + if self.pointer_lock_position.is_none() => + { + self.pointer_position = position; + } + _ => {} } @@ -563,6 +600,15 @@ impl ApplicationHandler for App { self.cef_context.work(); } + fn device_event(&mut self, _event_loop: &dyn ActiveEventLoop, _device_id: Option, event: winit::event::DeviceEvent) { + if self.pointer_lock_position.is_some() + && let winit::event::DeviceEvent::PointerMotion { delta: (x, y) } = event + { + let message = DesktopWrapperMessage::PointerLockMove { x, y }; + self.app_event_scheduler.schedule(AppEvent::DesktopWrapperMessage(message)); + } + } + fn about_to_wait(&mut self, event_loop: &dyn ActiveEventLoop) { // Set a timeout in case we miss any cef schedule requests let timeout = Instant::now() + Duration::from_millis(10); diff --git a/desktop/src/window.rs b/desktop/src/window.rs index c140ed76..249ba2d4 100644 --- a/desktop/src/window.rs +++ b/desktop/src/window.rs @@ -159,6 +159,16 @@ impl Window { self.winit_window.set_cursor(cursor); } + pub(crate) fn start_pointer_lock(&self) { + let _ = self.winit_window.set_cursor_grab(winit::window::CursorGrabMode::Locked); + self.winit_window.set_cursor_visible(false); + } + + pub(crate) fn end_pointer_lock(&self) { + let _ = self.winit_window.set_cursor_grab(winit::window::CursorGrabMode::None); + self.winit_window.set_cursor_visible(true); + } + pub(crate) fn update_menu(&self, entries: Vec) { self.native_handle.update_menu(entries); } diff --git a/desktop/wrapper/src/handle_desktop_wrapper_message.rs b/desktop/wrapper/src/handle_desktop_wrapper_message.rs index b1857e9f..a830931f 100644 --- a/desktop/wrapper/src/handle_desktop_wrapper_message.rs +++ b/desktop/wrapper/src/handle_desktop_wrapper_message.rs @@ -172,5 +172,9 @@ pub(super) fn handle_desktop_wrapper_message(dispatcher: &mut DesktopWrapperMess dispatcher.queue_editor_message(message); } } + DesktopWrapperMessage::PointerLockMove { x, y } => { + let message = AppWindowMessage::PointerLockMove { x, y }; + dispatcher.queue_editor_message(message); + } } } diff --git a/desktop/wrapper/src/intercept_frontend_message.rs b/desktop/wrapper/src/intercept_frontend_message.rs index 764f11fd..baa8000b 100644 --- a/desktop/wrapper/src/intercept_frontend_message.rs +++ b/desktop/wrapper/src/intercept_frontend_message.rs @@ -136,6 +136,9 @@ pub(super) fn intercept_frontend_message(dispatcher: &mut DesktopWrapperMessageD FrontendMessage::TriggerClipboardWrite { content } => { dispatcher.respond(DesktopFrontendMessage::ClipboardWrite { content }); } + FrontendMessage::WindowPointerLock => { + dispatcher.respond(DesktopFrontendMessage::PointerLock); + } FrontendMessage::WindowClose => { dispatcher.respond(DesktopFrontendMessage::WindowClose); } diff --git a/desktop/wrapper/src/messages.rs b/desktop/wrapper/src/messages.rs index a5769ecf..f9d4bc5e 100644 --- a/desktop/wrapper/src/messages.rs +++ b/desktop/wrapper/src/messages.rs @@ -65,6 +65,7 @@ pub enum DesktopFrontendMessage { ClipboardWrite { content: String, }, + PointerLock, WindowClose, WindowMinimize, WindowMaximize, @@ -132,6 +133,10 @@ pub enum DesktopWrapperMessage { ClipboardReadResult { content: Option, }, + PointerLockMove { + x: f64, + y: f64, + }, } #[derive(Clone, serde::Serialize, serde::Deserialize, Debug)] diff --git a/editor/src/messages/app_window/app_window_message.rs b/editor/src/messages/app_window/app_window_message.rs index 9ac40984..7a988c6c 100644 --- a/editor/src/messages/app_window/app_window_message.rs +++ b/editor/src/messages/app_window/app_window_message.rs @@ -6,6 +6,8 @@ use super::app_window_message_handler::AppWindowPlatform; #[derive(PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize)] pub enum AppWindowMessage { UpdatePlatform { platform: AppWindowPlatform }, + PointerLock, + PointerLockMove { x: f64, y: f64 }, Close, Minimize, Maximize, diff --git a/editor/src/messages/app_window/app_window_message_handler.rs b/editor/src/messages/app_window/app_window_message_handler.rs index 3b8b9235..b862d3b1 100644 --- a/editor/src/messages/app_window/app_window_message_handler.rs +++ b/editor/src/messages/app_window/app_window_message_handler.rs @@ -15,6 +15,12 @@ impl MessageHandler for AppWindowMessageHandler { self.platform = platform; responses.add(FrontendMessage::UpdatePlatform { platform: self.platform }); } + AppWindowMessage::PointerLock => { + responses.add(FrontendMessage::WindowPointerLock); + } + AppWindowMessage::PointerLockMove { x, y } => { + responses.add(FrontendMessage::WindowPointerLockMove { x, y }); + } AppWindowMessage::Close => { responses.add(FrontendMessage::WindowClose); } diff --git a/editor/src/messages/frontend/frontend_message.rs b/editor/src/messages/frontend/frontend_message.rs index a1e11eba..0f47b6f5 100644 --- a/editor/src/messages/frontend/frontend_message.rs +++ b/editor/src/messages/frontend/frontend_message.rs @@ -364,6 +364,11 @@ pub enum FrontendMessage { }, // Window prefix: cause the application window to do something + WindowPointerLock, + WindowPointerLockMove { + x: f64, + y: f64, + }, WindowClose, WindowMinimize, WindowMaximize, diff --git a/frontend/src/components/widgets/inputs/NumberInput.svelte b/frontend/src/components/widgets/inputs/NumberInput.svelte index 91c31082..75502298 100644 --- a/frontend/src/components/widgets/inputs/NumberInput.svelte +++ b/frontend/src/components/widgets/inputs/NumberInput.svelte @@ -1,7 +1,8 @@