From 5d8bf51d7ea2ea89feb4e8b003e583fed69bf300 Mon Sep 17 00:00:00 2001 From: Amr Bashir Date: Thu, 12 Jan 2023 15:09:05 +0200 Subject: [PATCH] feat: add `cursor_position` (#668) * feat: add `cursor_position` * fix android/ios and add docs * fix macos, ios, and android * fix macOS * fix android * review changes --- .changes/cursor-position.md | 5 +++++ src/event_loop.rs | 14 +++++++++++++- src/platform_impl/android/mod.rs | 10 ++++++++++ src/platform_impl/ios/event_loop.rs | 8 +++++++- src/platform_impl/ios/window.rs | 5 +++++ src/platform_impl/linux/event_loop.rs | 9 ++++++++- src/platform_impl/linux/mod.rs | 1 + src/platform_impl/linux/util.rs | 13 +++++++++++++ src/platform_impl/linux/window.rs | 7 ++++++- src/platform_impl/macos/event_loop.rs | 9 ++++++++- src/platform_impl/macos/util/mod.rs | 13 ++++++++++++- src/platform_impl/macos/window.rs | 5 +++++ src/platform_impl/windows/event_loop.rs | 6 ++++++ src/platform_impl/windows/util.rs | 19 ++++++++++++++++++- src/platform_impl/windows/window.rs | 5 +++++ src/window.rs | 10 ++++++++++ 16 files changed, 132 insertions(+), 7 deletions(-) create mode 100644 .changes/cursor-position.md create mode 100644 src/platform_impl/linux/util.rs diff --git a/.changes/cursor-position.md b/.changes/cursor-position.md new file mode 100644 index 000000000..dc8ae9d23 --- /dev/null +++ b/.changes/cursor-position.md @@ -0,0 +1,5 @@ +--- +"tao": "patch" +--- + +Add `Window::cursor_position` and `EventLoopWindowTarget::cursor_position` to get the current mouse position. \ No newline at end of file diff --git a/src/event_loop.rs b/src/event_loop.rs index eb2098174..5ae7c8095 100644 --- a/src/event_loop.rs +++ b/src/event_loop.rs @@ -17,7 +17,9 @@ use instant::Instant; use raw_window_handle::{HasRawDisplayHandle, RawDisplayHandle}; use std::{error, fmt, ops::Deref}; -use crate::{event::Event, monitor::MonitorHandle, platform_impl}; +use crate::{ + dpi::PhysicalPosition, error::ExternalError, event::Event, monitor::MonitorHandle, platform_impl, +}; /// Provides a way to retrieve events from the system and from the windows that were registered to /// the events loop. @@ -241,6 +243,16 @@ impl EventLoopWindowTarget { #[cfg(target_os = "windows")] self.p.set_device_event_filter(_filter); } + + /// Returns the current cursor position + /// + /// ## Platform-specific + /// + /// - **iOS / Android**: Unsupported. + #[inline] + pub fn cursor_position(&self) -> Result, ExternalError> { + self.p.cursor_position() + } } unsafe impl HasRawDisplayHandle for EventLoopWindowTarget { diff --git a/src/platform_impl/android/mod.rs b/src/platform_impl/android/mod.rs index d54532f36..745bb34af 100644 --- a/src/platform_impl/android/mod.rs +++ b/src/platform_impl/android/mod.rs @@ -501,6 +501,11 @@ impl EventLoopWindowTarget { pub fn raw_display_handle(&self) -> RawDisplayHandle { RawDisplayHandle::Android(AndroidDisplayHandle::empty()) } + + pub fn cursor_position(&self) -> Result, error::ExternalError> { + debug!("`EventLoopWindowTarget::cursor_position` is ignored on Android"); + Ok((0, 0).into()) + } } #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] @@ -734,6 +739,11 @@ impl Window { )) } + pub fn cursor_position(&self) -> Result, error::ExternalError> { + debug!("`Window::cursor_position` is ignored on Android"); + Ok((0, 0).into()) + } + pub fn raw_window_handle(&self) -> RawWindowHandle { // TODO: Use main activity instead? let mut handle = AndroidNdkWindowHandle::empty(); diff --git a/src/platform_impl/ios/event_loop.rs b/src/platform_impl/ios/event_loop.rs index 4c2c5fe29..57c836159 100644 --- a/src/platform_impl/ios/event_loop.rs +++ b/src/platform_impl/ios/event_loop.rs @@ -15,7 +15,8 @@ use crossbeam_channel::{self as channel, Receiver, Sender}; use raw_window_handle::{RawDisplayHandle, UiKitDisplayHandle}; use crate::{ - dpi::LogicalSize, + dpi::{LogicalSize, PhysicalPosition}, + error::ExternalError, event::Event, event_loop::{ControlFlow, EventLoopClosed, EventLoopWindowTarget as RootEventLoopWindowTarget}, monitor::MonitorHandle as RootMonitorHandle, @@ -80,6 +81,11 @@ impl EventLoopWindowTarget { pub fn raw_display_handle(&self) -> RawDisplayHandle { RawDisplayHandle::UiKit(UiKitDisplayHandle::empty()) } + + pub fn cursor_position(&self) -> Result, ExternalError> { + debug!("`EventLoopWindowTarget::cursor_position` is ignored on iOS"); + Ok((0, 0).into()) + } } pub struct EventLoop { diff --git a/src/platform_impl/ios/window.rs b/src/platform_impl/ios/window.rs index 76935e0c6..736ae592f 100644 --- a/src/platform_impl/ios/window.rs +++ b/src/platform_impl/ios/window.rs @@ -216,6 +216,11 @@ impl Inner { debug!("`Window::set_cursor_visible` is ignored on iOS") } + pub fn cursor_position(&self) -> Result, ExternalError> { + debug!("`Window::cursor_position` is ignored on iOS"); + Ok((0, 0).into()) + } + pub fn drag_window(&self) -> Result<(), ExternalError> { Err(ExternalError::NotSupported(NotSupportedError::new())) } diff --git a/src/platform_impl/linux/event_loop.rs b/src/platform_impl/linux/event_loop.rs index e27a99ecb..339af7e95 100644 --- a/src/platform_impl/linux/event_loop.rs +++ b/src/platform_impl/linux/event_loop.rs @@ -23,7 +23,8 @@ use raw_window_handle::{RawDisplayHandle, XlibDisplayHandle}; use crate::{ accelerator::AcceleratorId, - dpi::{LogicalPosition, LogicalSize}, + dpi::{LogicalPosition, LogicalSize, PhysicalPosition}, + error::ExternalError, event::{ ElementState, Event, MouseButton, MouseScrollDelta, StartCause, TouchPhase, WindowEvent, }, @@ -38,6 +39,7 @@ use crate::{ use super::{ keyboard, monitor::{self, MonitorHandle}, + util, window::{WindowId, WindowRequest}, }; @@ -99,6 +101,11 @@ impl EventLoopWindowTarget { pub fn is_wayland(&self) -> bool { self.display.backend().is_wayland() } + + #[inline] + pub fn cursor_position(&self) -> Result, ExternalError> { + util::cursor_position() + } } pub struct EventLoop { diff --git a/src/platform_impl/linux/mod.rs b/src/platform_impl/linux/mod.rs index 0ca35cfe5..a4ea863ef 100644 --- a/src/platform_impl/linux/mod.rs +++ b/src/platform_impl/linux/mod.rs @@ -21,6 +21,7 @@ mod menu; mod monitor; #[cfg(feature = "tray")] mod system_tray; +mod util; mod window; pub mod x11; diff --git a/src/platform_impl/linux/util.rs b/src/platform_impl/linux/util.rs new file mode 100644 index 000000000..bd0b647f3 --- /dev/null +++ b/src/platform_impl/linux/util.rs @@ -0,0 +1,13 @@ +use gdk::Display; + +use crate::{dpi::PhysicalPosition, error::ExternalError}; + +#[inline] +pub fn cursor_position() -> Result, ExternalError> { + Display::default() + .and_then(|d| d.default_seat()) + .and_then(|s| s.pointer()) + .map(|p| p.position_double()) + .map(|(_, x, y)| (x, y).into()) + .ok_or(ExternalError::Os(os_error!(super::OsError))) +} diff --git a/src/platform_impl/linux/window.rs b/src/platform_impl/linux/window.rs index 07a77b865..ed84ca168 100644 --- a/src/platform_impl/linux/window.rs +++ b/src/platform_impl/linux/window.rs @@ -32,7 +32,7 @@ use super::{ event_loop::EventLoopWindowTarget, menu, monitor::{self, MonitorHandle}, - Parent, PlatformSpecificWindowBuilderAttributes, + util, Parent, PlatformSpecificWindowBuilderAttributes, }; #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] @@ -749,6 +749,11 @@ impl Window { } } + #[inline] + pub fn cursor_position(&self) -> Result, ExternalError> { + util::cursor_position() + } + pub fn current_monitor(&self) -> Option { let screen = self.window.display().default_screen(); // `.window()` returns `None` if the window is invisible; diff --git a/src/platform_impl/macos/event_loop.rs b/src/platform_impl/macos/event_loop.rs index b3156f524..8ec8a6b23 100644 --- a/src/platform_impl/macos/event_loop.rs +++ b/src/platform_impl/macos/event_loop.rs @@ -24,6 +24,8 @@ use raw_window_handle::{AppKitDisplayHandle, RawDisplayHandle}; use scopeguard::defer; use crate::{ + dpi::PhysicalPosition, + error::ExternalError, event::Event, event_loop::{ControlFlow, EventLoopClosed, EventLoopWindowTarget as RootWindowTarget}, monitor::MonitorHandle as RootMonitorHandle, @@ -33,7 +35,7 @@ use crate::{ app_state::AppState, monitor::{self, MonitorHandle}, observer::*, - util::IdRef, + util::{self, IdRef}, }, }; @@ -99,6 +101,11 @@ impl EventLoopWindowTarget { pub fn raw_display_handle(&self) -> RawDisplayHandle { RawDisplayHandle::AppKit(AppKitDisplayHandle::empty()) } + + #[inline] + pub fn cursor_position(&self) -> Result, ExternalError> { + util::cursor_position() + } } pub struct EventLoop { diff --git a/src/platform_impl/macos/util/mod.rs b/src/platform_impl/macos/util/mod.rs index 3afecba62..e989045ec 100644 --- a/src/platform_impl/macos/util/mod.rs +++ b/src/platform_impl/macos/util/mod.rs @@ -20,7 +20,11 @@ use cocoa::{ use core_graphics::display::CGDisplay; use objc::runtime::{Class, Object, Sel, BOOL, YES}; -use crate::{dpi::LogicalPosition, platform_impl::platform::ffi}; +use crate::{ + dpi::{LogicalPosition, PhysicalPosition}, + error::ExternalError, + platform_impl::platform::ffi, +}; // Replace with `!` once stable #[derive(Debug)] @@ -120,6 +124,13 @@ pub fn window_position(position: LogicalPosition) -> NSPoint { ) } +pub fn cursor_position() -> Result, ExternalError> { + unsafe { + let pt: NSPoint = msg_send![class!(NSEvent), mouseLocation]; + Ok((pt.x, pt.y).into()) + } +} + pub unsafe fn ns_string_id_ref(s: &str) -> IdRef { IdRef::new(NSString::alloc(nil).init_str(s)) } diff --git a/src/platform_impl/macos/window.rs b/src/platform_impl/macos/window.rs index 08cd94545..a458cf486 100644 --- a/src/platform_impl/macos/window.rs +++ b/src/platform_impl/macos/window.rs @@ -803,6 +803,11 @@ impl UnownedWindow { } } + #[inline] + pub fn cursor_position(&self) -> Result, ExternalError> { + util::cursor_position() + } + #[inline] pub fn scale_factor(&self) -> f64 { unsafe { NSWindow::backingScaleFactor(*self.ns_window) as _ } diff --git a/src/platform_impl/windows/event_loop.rs b/src/platform_impl/windows/event_loop.rs index a34465dcd..6121a4e1e 100644 --- a/src/platform_impl/windows/event_loop.rs +++ b/src/platform_impl/windows/event_loop.rs @@ -45,6 +45,7 @@ use windows::{ use crate::{ accelerator::AcceleratorId, dpi::{PhysicalPosition, PhysicalSize}, + error::ExternalError, event::{DeviceEvent, Event, Force, RawKeyEvent, Touch, TouchPhase, WindowEvent}, event_loop::{ControlFlow, DeviceEventFilter, EventLoopClosed, EventLoopWindowTarget as RootELW}, keyboard::{KeyCode, ModifiersState}, @@ -319,6 +320,11 @@ impl EventLoopWindowTarget { pub fn set_device_event_filter(&self, filter: DeviceEventFilter) { raw_input::register_all_mice_and_keyboards_for_raw_input(self.thread_msg_target, filter); } + + #[inline] + pub fn cursor_position(&self) -> Result, ExternalError> { + util::cursor_position() + } } fn main_thread_id() -> u32 { diff --git a/src/platform_impl/windows/util.rs b/src/platform_impl/windows/util.rs index 3130eccf6..8fc4af9d1 100644 --- a/src/platform_impl/windows/util.rs +++ b/src/platform_impl/windows/util.rs @@ -12,7 +12,11 @@ use std::{ sync::atomic::{AtomicBool, Ordering}, }; -use crate::{dpi::PhysicalSize, window::CursorIcon}; +use crate::{ + dpi::{PhysicalPosition, PhysicalSize}, + error::ExternalError, + window::CursorIcon, +}; use windows::{ core::{HRESULT, PCSTR, PCWSTR}, @@ -30,6 +34,8 @@ use windows::{ }, }; +use super::OsError; + pub fn has_flag(bitset: T, flag: T) -> bool where T: Copy + PartialEq + BitAnd, @@ -229,6 +235,17 @@ pub fn is_maximized(window: HWND) -> bool { placement.showCmd == SW_MAXIMIZE } +pub fn cursor_position() -> Result, ExternalError> { + let mut pt = POINT { x: 0, y: 0 }; + if !unsafe { GetCursorPos(&mut pt) }.as_bool() { + return Err(ExternalError::Os(os_error!(OsError::IoError( + io::Error::last_os_error() + )))); + } + + Ok((pt.x, pt.y).into()) +} + impl CursorIcon { pub(crate) fn to_windows_cursor(self) -> PCWSTR { match self { diff --git a/src/platform_impl/windows/window.rs b/src/platform_impl/windows/window.rs index eb1aefaaa..a9ba482ab 100644 --- a/src/platform_impl/windows/window.rs +++ b/src/platform_impl/windows/window.rs @@ -419,6 +419,11 @@ impl Window { rx.recv().unwrap().ok(); } + #[inline] + pub fn cursor_position(&self) -> Result, ExternalError> { + util::cursor_position() + } + #[inline] pub fn scale_factor(&self) -> f64 { self.window_state.lock().scale_factor diff --git a/src/window.rs b/src/window.rs index b72b272c8..269676412 100644 --- a/src/window.rs +++ b/src/window.rs @@ -1197,6 +1197,16 @@ impl Window { pub fn set_ignore_cursor_events(&self, ignore: bool) -> Result<(), ExternalError> { self.window.set_ignore_cursor_events(ignore) } + + /// Returns the current cursor position + /// + /// ## Platform-specific + /// + /// - **iOS / Android**: Unsupported, returns `0,0`. + #[inline] + pub fn cursor_position(&self) -> Result, ExternalError> { + self.window.cursor_position() + } } /// Monitor info functions.