Skip to content

Commit

Permalink
feat: add cursor_position (#668)
Browse files Browse the repository at this point in the history
* feat: add `cursor_position`

* fix android/ios and add docs

* fix macos, ios, and android

* fix macOS

* fix android

* review changes
  • Loading branch information
amrbashir authored Jan 12, 2023
1 parent 6c2718c commit 5d8bf51
Show file tree
Hide file tree
Showing 16 changed files with 132 additions and 7 deletions.
5 changes: 5 additions & 0 deletions .changes/cursor-position.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"tao": "patch"
---

Add `Window::cursor_position` and `EventLoopWindowTarget::cursor_position` to get the current mouse position.
14 changes: 13 additions & 1 deletion src/event_loop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -241,6 +243,16 @@ impl<T> EventLoopWindowTarget<T> {
#[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<PhysicalPosition<f64>, ExternalError> {
self.p.cursor_position()
}
}

unsafe impl<T> HasRawDisplayHandle for EventLoopWindowTarget<T> {
Expand Down
10 changes: 10 additions & 0 deletions src/platform_impl/android/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -501,6 +501,11 @@ impl<T: 'static> EventLoopWindowTarget<T> {
pub fn raw_display_handle(&self) -> RawDisplayHandle {
RawDisplayHandle::Android(AndroidDisplayHandle::empty())
}

pub fn cursor_position(&self) -> Result<PhysicalPosition<f64>, error::ExternalError> {
debug!("`EventLoopWindowTarget::cursor_position` is ignored on Android");
Ok((0, 0).into())
}
}

#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
Expand Down Expand Up @@ -734,6 +739,11 @@ impl Window {
))
}

pub fn cursor_position(&self) -> Result<PhysicalPosition<f64>, 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();
Expand Down
8 changes: 7 additions & 1 deletion src/platform_impl/ios/event_loop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -80,6 +81,11 @@ impl<T: 'static> EventLoopWindowTarget<T> {
pub fn raw_display_handle(&self) -> RawDisplayHandle {
RawDisplayHandle::UiKit(UiKitDisplayHandle::empty())
}

pub fn cursor_position(&self) -> Result<PhysicalPosition<f64>, ExternalError> {
debug!("`EventLoopWindowTarget::cursor_position` is ignored on iOS");
Ok((0, 0).into())
}
}

pub struct EventLoop<T: 'static> {
Expand Down
5 changes: 5 additions & 0 deletions src/platform_impl/ios/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,11 @@ impl Inner {
debug!("`Window::set_cursor_visible` is ignored on iOS")
}

pub fn cursor_position(&self) -> Result<PhysicalPosition<f64>, 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()))
}
Expand Down
9 changes: 8 additions & 1 deletion src/platform_impl/linux/event_loop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
},
Expand All @@ -38,6 +39,7 @@ use crate::{
use super::{
keyboard,
monitor::{self, MonitorHandle},
util,
window::{WindowId, WindowRequest},
};

Expand Down Expand Up @@ -99,6 +101,11 @@ impl<T> EventLoopWindowTarget<T> {
pub fn is_wayland(&self) -> bool {
self.display.backend().is_wayland()
}

#[inline]
pub fn cursor_position(&self) -> Result<PhysicalPosition<f64>, ExternalError> {
util::cursor_position()
}
}

pub struct EventLoop<T: 'static> {
Expand Down
1 change: 1 addition & 0 deletions src/platform_impl/linux/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ mod menu;
mod monitor;
#[cfg(feature = "tray")]
mod system_tray;
mod util;
mod window;
pub mod x11;

Expand Down
13 changes: 13 additions & 0 deletions src/platform_impl/linux/util.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
use gdk::Display;

use crate::{dpi::PhysicalPosition, error::ExternalError};

#[inline]
pub fn cursor_position() -> Result<PhysicalPosition<f64>, 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)))
}
7 changes: 6 additions & 1 deletion src/platform_impl/linux/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down Expand Up @@ -749,6 +749,11 @@ impl Window {
}
}

#[inline]
pub fn cursor_position(&self) -> Result<PhysicalPosition<f64>, ExternalError> {
util::cursor_position()
}

pub fn current_monitor(&self) -> Option<RootMonitorHandle> {
let screen = self.window.display().default_screen();
// `.window()` returns `None` if the window is invisible;
Expand Down
9 changes: 8 additions & 1 deletion src/platform_impl/macos/event_loop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -33,7 +35,7 @@ use crate::{
app_state::AppState,
monitor::{self, MonitorHandle},
observer::*,
util::IdRef,
util::{self, IdRef},
},
};

Expand Down Expand Up @@ -99,6 +101,11 @@ impl<T: 'static> EventLoopWindowTarget<T> {
pub fn raw_display_handle(&self) -> RawDisplayHandle {
RawDisplayHandle::AppKit(AppKitDisplayHandle::empty())
}

#[inline]
pub fn cursor_position(&self) -> Result<PhysicalPosition<f64>, ExternalError> {
util::cursor_position()
}
}

pub struct EventLoop<T: 'static> {
Expand Down
13 changes: 12 additions & 1 deletion src/platform_impl/macos/util/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down Expand Up @@ -120,6 +124,13 @@ pub fn window_position(position: LogicalPosition<f64>) -> NSPoint {
)
}

pub fn cursor_position() -> Result<PhysicalPosition<f64>, 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))
}
Expand Down
5 changes: 5 additions & 0 deletions src/platform_impl/macos/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -803,6 +803,11 @@ impl UnownedWindow {
}
}

#[inline]
pub fn cursor_position(&self) -> Result<PhysicalPosition<f64>, ExternalError> {
util::cursor_position()
}

#[inline]
pub fn scale_factor(&self) -> f64 {
unsafe { NSWindow::backingScaleFactor(*self.ns_window) as _ }
Expand Down
6 changes: 6 additions & 0 deletions src/platform_impl/windows/event_loop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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},
Expand Down Expand Up @@ -319,6 +320,11 @@ impl<T> EventLoopWindowTarget<T> {
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<PhysicalPosition<f64>, ExternalError> {
util::cursor_position()
}
}

fn main_thread_id() -> u32 {
Expand Down
19 changes: 18 additions & 1 deletion src/platform_impl/windows/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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},
Expand All @@ -30,6 +34,8 @@ use windows::{
},
};

use super::OsError;

pub fn has_flag<T>(bitset: T, flag: T) -> bool
where
T: Copy + PartialEq + BitAnd<T, Output = T>,
Expand Down Expand Up @@ -229,6 +235,17 @@ pub fn is_maximized(window: HWND) -> bool {
placement.showCmd == SW_MAXIMIZE
}

pub fn cursor_position() -> Result<PhysicalPosition<f64>, 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 {
Expand Down
5 changes: 5 additions & 0 deletions src/platform_impl/windows/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -419,6 +419,11 @@ impl Window {
rx.recv().unwrap().ok();
}

#[inline]
pub fn cursor_position(&self) -> Result<PhysicalPosition<f64>, ExternalError> {
util::cursor_position()
}

#[inline]
pub fn scale_factor(&self) -> f64 {
self.window_state.lock().scale_factor
Expand Down
10 changes: 10 additions & 0 deletions src/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<PhysicalPosition<f64>, ExternalError> {
self.window.cursor_position()
}
}

/// Monitor info functions.
Expand Down

0 comments on commit 5d8bf51

Please sign in to comment.