Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add cursor_position getter API #2648

Open
wants to merge 22 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 8 additions & 6 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ And please only add new entries to the top of this list, right below the `# Unre

# Unreleased

- Add `EventLoopWindowTarget::cursor_position`.

# 0.28.1

- On Wayland, fix crash when dropping a window in multi-window setup.
Expand Down Expand Up @@ -312,7 +314,7 @@ And please only add new entries to the top of this list, right below the `# Unre
- On macOS, fix inverted horizontal scroll.
- **Breaking:** `current_monitor` now returns `Option<MonitorHandle>`.
- **Breaking:** `primary_monitor` now returns `Option<MonitorHandle>`.
- On macOS, updated core-* dependencies and cocoa.
- On macOS, updated core-\* dependencies and cocoa.
- Bump `parking_lot` to 0.11
- On Android, bump `ndk`, `ndk-sys` and `ndk-glue` to 0.2. Checkout the new ndk-glue main proc attribute.
- On iOS, fixed starting the app in landscape where the view still had portrait dimensions.
Expand Down Expand Up @@ -354,7 +356,7 @@ And please only add new entries to the top of this list, right below the `# Unre
- On Windows, fix `WindowBuilder::with_maximized` being ignored.
- On Android, minimal platform support.
- On iOS, touch positions are now properly converted to physical pixels.
- On macOS, updated core-* dependencies and cocoa
- On macOS, updated core-\* dependencies and cocoa

# 0.22.1 (2020-04-16)

Expand Down Expand Up @@ -541,7 +543,7 @@ And please only add new entries to the top of this list, right below the `# Unre

- On X11, non-resizable windows now have maximize explicitly disabled.
- On Windows, support paths longer than MAX_PATH (260 characters) in `WindowEvent::DroppedFile`
and `WindowEvent::HoveredFile`.
and `WindowEvent::HoveredFile`.
- On Mac, implement `DeviceEvent::Button`.
- Change `Event::Suspended(true / false)` to `Event::Suspended` and `Event::Resumed`.
- On X11, fix sanity check which checks that a monitor's reported width and height (in millimeters) are non-zero when calculating the DPI factor.
Expand Down Expand Up @@ -883,20 +885,20 @@ _Yanked_
- Added event `WindowEvent::HiDPIFactorChanged`.
- Added method `MonitorId::get_hidpi_factor`.
- Deprecated `get_inner_size_pixels` and `get_inner_size_points` methods of `Window` in favor of
`get_inner_size`.
`get_inner_size`.
- **Breaking:** `EventsLoop` is `!Send` and `!Sync` because of platform-dependant constraints,
but `Window`, `WindowId`, `DeviceId` and `MonitorId` guaranteed to be `Send`.
- `MonitorId::get_position` now returns `(i32, i32)` instead of `(u32, u32)`.
- Rewrite of the wayland backend to use wayland-client-0.11
- Support for dead keys on wayland for keyboard utf8 input
- Monitor enumeration on Windows is now implemented using `EnumDisplayMonitors` instead of
`EnumDisplayDevices`. This changes the value returned by `MonitorId::get_name()`.
`EnumDisplayDevices`. This changes the value returned by `MonitorId::get_name()`.
- On Windows added `MonitorIdExt::hmonitor` method
- Impl `Clone` for `EventsLoopProxy`
- `EventsLoop::get_primary_monitor()` on X11 will fallback to any available monitor if no primary is found
- Support for touch event on wayland
- `WindowEvent`s `MouseMoved`, `MouseEntered`, and `MouseLeft` have been renamed to
`CursorMoved`, `CursorEntered`, and `CursorLeft`.
`CursorMoved`, `CursorEntered`, and `CursorLeft`.
- New `DeviceEvent`s added, `MouseMotion` and `MouseWheel`.
- Send `CursorMoved` event after `CursorEntered` and `Focused` events.
- Add support for `ModifiersState`, `MouseMove`, `MouseInput`, `MouseMotion` for emscripten backend.
Expand Down
12 changes: 12 additions & 0 deletions src/event_loop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ use instant::{Duration, Instant};
use once_cell::sync::OnceCell;
use raw_window_handle::{HasRawDisplayHandle, RawDisplayHandle};

use crate::dpi::PhysicalPosition;
use crate::error::ExternalError;
use crate::{event::Event, monitor::MonitorHandle, platform_impl};

/// Provides a way to retrieve events from the system and from the windows that were registered to
Expand Down Expand Up @@ -359,6 +361,16 @@ impl<T> EventLoopWindowTarget<T> {
#[cfg(any(x11_platform, wayland_platform, windows))]
self.p.set_device_event_filter(_filter);
}

/// Returns the current cursor position in screen coordinates.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Referencing #2648 (review).

I think we thought this was on Window, and hence expected things to be in window coordinates, just like the coordinates for set_cursor_position (such that window.set_cursor_position(window.cursor_position()?)? is (roughly) a no-op). But we decided against Window::cursor_position in https://github.com/rust-windowing/winit/pull/2648/files#r1090072945, this API is ActiveEventLoop::cursor_position.

In that case, I'd expect the coordinates to be the same as those from Window::outer_position, is that correct? If so, could you refer to that for discussion about the coordinate space, just like Window::set_outer_position does?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(If you think that this interpretation is correct, I'll test the macOS implementation myself to ensure that it matches Window::outer_position).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In that case, I'd expect the coordinates to be the same as those from Window::outer_position, is that correct?

Yeah

If so, could you refer to that for discussion about the coordinate space, just like Window::set_outer_position does?

like this amrbashir@1f9a3df ?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup.

And I've tested it now, and pushed a fix to return positions with the correct scaling and such (it's still a little broken, but that's a more general issue, see #3615).

///
amrbashir marked this conversation as resolved.
Show resolved Hide resolved
/// ## Platform-specific
///
/// - **iOS / Android / Wayland / Orbital / Web**: Unsupported.
#[inline]
pub fn cursor_position(&self) -> Result<PhysicalPosition<f64>, ExternalError> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It confused me that the return type of this is not PhysicalPosition<i32> like the return type of Window::outer_position, but I guess it matches the CursorMoved event - apparently cursors can be positioned on a sub-pixel scale, didn't know that!

self.p.cursor_position()
}
}

unsafe impl<T> HasRawDisplayHandle for EventLoopWindowTarget<T> {
Expand Down
6 changes: 6 additions & 0 deletions src/platform_impl/android/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -838,6 +838,12 @@ 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> {
Err(error::ExternalError::NotSupported(
error::NotSupportedError::new(),
))
}
}

#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
Expand Down
7 changes: 6 additions & 1 deletion src/platform_impl/ios/event_loop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ use super::uikit::{UIApplication, UIApplicationMain, UIDevice, UIScreen};
use super::view::WinitUIWindow;
use super::{app_state, monitor, view, MonitorHandle};
use crate::{
dpi::LogicalSize,
dpi::{LogicalSize, PhysicalPosition},
error::{ExternalError, NotSupportedError},
event::Event,
event_loop::{
ControlFlow, EventLoopClosed, EventLoopWindowTarget as RootEventLoopWindowTarget,
Expand Down Expand Up @@ -65,6 +66,10 @@ 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> {
Err(ExternalError::NotSupported(NotSupportedError::new()))
}
}

pub struct EventLoop<T: 'static> {
Expand Down
4 changes: 4 additions & 0 deletions src/platform_impl/linux/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -866,6 +866,10 @@ impl<T> EventLoopWindowTarget<T> {
pub fn raw_display_handle(&self) -> raw_window_handle::RawDisplayHandle {
x11_or_wayland!(match self; Self(evlp) => evlp.raw_display_handle())
}

pub fn cursor_position(&self) -> Result<PhysicalPosition<f64>, ExternalError> {
x11_or_wayland!(match self; Self(evlp) => evlp.cursor_position())
}
}

fn sticky_exit_callback<T, F>(
Expand Down
7 changes: 7 additions & 0 deletions src/platform_impl/linux/wayland/event_loop/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ use sctk::environment::Environment;
use sctk::seat::pointer::{ThemeManager, ThemeSpec};
use sctk::WaylandSource;

use crate::dpi::PhysicalPosition;
use crate::error::ExternalError;
use crate::error::NotSupportedError;
use crate::event::{Event, StartCause, WindowEvent};
use crate::event_loop::{ControlFlow, EventLoopWindowTarget as RootEventLoopWindowTarget};
use crate::platform_impl::platform::sticky_exit_callback;
Expand Down Expand Up @@ -80,6 +83,10 @@ impl<T> EventLoopWindowTarget<T> {
display_handle.display = self.display.get_display_ptr() as *mut _;
RawDisplayHandle::Wayland(display_handle)
}

pub fn cursor_position(&self) -> Result<PhysicalPosition<f64>, ExternalError> {
Err(ExternalError::NotSupported(NotSupportedError::new()))
}
}

pub struct EventLoop<T: 'static> {
Expand Down
7 changes: 6 additions & 1 deletion src/platform_impl/linux/x11/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@ use self::{
util::modifiers::ModifierKeymap,
};
use crate::{
error::OsError as RootOsError,
dpi::PhysicalPosition,
error::{ExternalError, OsError as RootOsError},
event::{Event, StartCause},
event_loop::{
ControlFlow, DeviceEventFilter, EventLoopClosed, EventLoopWindowTarget as RootELW,
Expand Down Expand Up @@ -573,6 +574,10 @@ impl<T> EventLoopWindowTarget<T> {
unsafe { (self.xconn.xlib.XDefaultScreen)(self.xconn.display as *mut _) };
RawDisplayHandle::Xlib(display_handle)
}

pub fn cursor_position(&self) -> Result<PhysicalPosition<f64>, ExternalError> {
self.xconn.cursor_position(self.root)
}
}

impl<T: 'static> EventLoopProxy<T> {
Expand Down
30 changes: 29 additions & 1 deletion src/platform_impl/linux/x11/util/cursor.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::window::CursorIcon;
use crate::{dpi::PhysicalPosition, error::ExternalError, window::CursorIcon};

use super::*;

Expand Down Expand Up @@ -127,4 +127,32 @@ impl XConnection {
self.flush_requests().expect("Failed to set the cursor");
}
}

pub fn cursor_position(
&self,
window: ffi::Window,
) -> Result<PhysicalPosition<f64>, ExternalError> {
let mut root_return = 0;
let mut child_return = 0;
let mut root_x_return = 0;
let mut root_y_return = 0;
let mut win_x_return = 0;
let mut win_y_return = 0;
let mut mask_return = 0;

unsafe {
(self.xlib.XQueryPointer)(
self.display,
window,
&mut root_return,
&mut child_return,
&mut root_x_return,
&mut root_y_return,
&mut win_x_return,
&mut win_y_return,
&mut mask_return,
);
}
Ok((root_x_return, root_y_return).into())
}
}
3 changes: 3 additions & 0 deletions src/platform_impl/macos/appkit/event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,9 @@ extern_methods!(
#[sel(locationInWindow)]
pub fn locationInWindow(&self) -> NSPoint;

#[sel(mouseLocation)]
pub fn mouseLocation() -> NSPoint;

// TODO: MainThreadMarker
#[sel(pressedMouseButtons)]
pub fn pressedMouseButtons() -> NSUInteger;
Expand Down
12 changes: 11 additions & 1 deletion src/platform_impl/macos/event_loop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,13 @@ use objc2::rc::{autoreleasepool, Id, Shared};
use objc2::{msg_send_id, ClassType};
use raw_window_handle::{AppKitDisplayHandle, RawDisplayHandle};

use super::appkit::{NSApp, NSApplicationActivationPolicy, NSEvent};
use super::{
appkit::{NSApp, NSApplicationActivationPolicy, NSEvent},
util,
};
use crate::{
dpi::PhysicalPosition,
error::ExternalError,
event::Event,
event_loop::{ControlFlow, EventLoopClosed, EventLoopWindowTarget as RootWindowTarget},
platform::macos::ActivationPolicy,
Expand Down Expand Up @@ -91,6 +96,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> {
Ok(util::cursor_position())
}
}

impl<T> EventLoopWindowTarget<T> {
Expand Down
7 changes: 7 additions & 0 deletions src/platform_impl/macos/util/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ use objc2::foundation::{CGFloat, NSNotFound, NSPoint, NSRange, NSRect, NSUIntege

use crate::dpi::LogicalPosition;

use super::appkit::NSEvent;

// Replace with `!` once stable
#[derive(Debug)]
pub enum Never {}
Expand Down Expand Up @@ -63,3 +65,8 @@ pub fn window_position(position: LogicalPosition<f64>) -> NSPoint {
CGDisplay::main().pixels_high() as CGFloat - position.y as CGFloat,
)
}

pub fn cursor_position() -> crate::dpi::PhysicalPosition<f64> {
let pt: NSPoint = NSEvent::mouseLocation();
amrbashir marked this conversation as resolved.
Show resolved Hide resolved
(pt.x, pt.y).into()
}
8 changes: 8 additions & 0 deletions src/platform_impl/orbital/event_loop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ use orbclient::{
use raw_window_handle::{OrbitalDisplayHandle, RawDisplayHandle};

use crate::{
dpi::PhysicalPosition,
error,
event::{self, StartCause, VirtualKeyCode},
event_loop::{self, ControlFlow},
window::WindowId as RootWindowId,
Expand Down Expand Up @@ -706,4 +708,10 @@ impl<T: 'static> EventLoopWindowTarget<T> {
pub fn raw_display_handle(&self) -> RawDisplayHandle {
RawDisplayHandle::Orbital(OrbitalDisplayHandle::empty())
}

pub fn cursor_position(&self) -> Result<PhysicalPosition<f64>, error::ExternalError> {
Err(error::ExternalError::NotSupported(
error::NotSupportedError::new(),
))
}
}
7 changes: 6 additions & 1 deletion src/platform_impl/web/event_loop/window_target.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ use super::{
super::monitor::MonitorHandle, backend, device::DeviceId, proxy::EventLoopProxy, runner,
window::WindowId,
};
use crate::dpi::{PhysicalSize, Size};
use crate::dpi::{PhysicalPosition, PhysicalSize, Size};
use crate::error::{ExternalError, NotSupportedError};
use crate::event::{
DeviceEvent, DeviceId as RootDeviceId, ElementState, Event, KeyboardInput, Touch, TouchPhase,
WindowEvent,
Expand Down Expand Up @@ -352,4 +353,8 @@ impl<T> EventLoopWindowTarget<T> {
pub fn raw_display_handle(&self) -> RawDisplayHandle {
RawDisplayHandle::Web(WebDisplayHandle::empty())
}

pub fn cursor_position(&self) -> Result<PhysicalPosition<f64>, ExternalError> {
Err(ExternalError::NotSupported(NotSupportedError::new()))
}
}
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 @@ -76,6 +76,7 @@ use windows_sys::Win32::{

use crate::{
dpi::{PhysicalPosition, PhysicalSize},
error::ExternalError,
event::{DeviceEvent, Event, Force, Ime, KeyboardInput, Touch, TouchPhase, WindowEvent},
event_loop::{
ControlFlow, DeviceEventFilter, EventLoopClosed, EventLoopWindowTarget as RootELW,
Expand Down Expand Up @@ -334,6 +335,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()
}
}

/// Returns the id of the main thread.
Expand Down
22 changes: 15 additions & 7 deletions src/platform_impl/windows/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use once_cell::sync::Lazy;
use windows_sys::{
core::{HRESULT, PCWSTR},
Win32::{
Foundation::{BOOL, HINSTANCE, HWND, RECT},
Foundation::{BOOL, HINSTANCE, HWND, POINT, RECT},
Graphics::Gdi::{ClientToScreen, HMONITOR},
System::{
LibraryLoader::{GetProcAddress, LoadLibraryA},
Expand All @@ -23,17 +23,18 @@ use windows_sys::{
HiDpi::{DPI_AWARENESS_CONTEXT, MONITOR_DPI_TYPE, PROCESS_DPI_AWARENESS},
Input::KeyboardAndMouse::GetActiveWindow,
WindowsAndMessaging::{
ClipCursor, GetClientRect, GetClipCursor, GetSystemMetrics, GetWindowPlacement,
GetWindowRect, IsIconic, ShowCursor, IDC_APPSTARTING, IDC_ARROW, IDC_CROSS,
IDC_HAND, IDC_HELP, IDC_IBEAM, IDC_NO, IDC_SIZEALL, IDC_SIZENESW, IDC_SIZENS,
IDC_SIZENWSE, IDC_SIZEWE, IDC_WAIT, SM_CXVIRTUALSCREEN, SM_CYVIRTUALSCREEN,
SM_XVIRTUALSCREEN, SM_YVIRTUALSCREEN, SW_MAXIMIZE, WINDOWPLACEMENT,
ClipCursor, GetClientRect, GetClipCursor, GetCursorPos, GetSystemMetrics,
GetWindowPlacement, GetWindowRect, IsIconic, ShowCursor, IDC_APPSTARTING,
IDC_ARROW, IDC_CROSS, IDC_HAND, IDC_HELP, IDC_IBEAM, IDC_NO, IDC_SIZEALL,
IDC_SIZENESW, IDC_SIZENS, IDC_SIZENWSE, IDC_SIZEWE, IDC_WAIT, SM_CXVIRTUALSCREEN,
SM_CYVIRTUALSCREEN, SM_XVIRTUALSCREEN, SM_YVIRTUALSCREEN, SW_MAXIMIZE,
WINDOWPLACEMENT,
},
},
},
};

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

pub fn encode_wide(string: impl AsRef<OsStr>) -> Vec<u16> {
string.as_ref().encode_wide().chain(once(0)).collect()
Expand Down Expand Up @@ -253,3 +254,10 @@ pub static SET_PROCESS_DPI_AWARENESS: Lazy<Option<SetProcessDpiAwareness>> =
Lazy::new(|| get_function!("shcore.dll", SetProcessDpiAwareness));
pub static SET_PROCESS_DPI_AWARE: Lazy<Option<SetProcessDPIAware>> =
Lazy::new(|| get_function!("user32.dll", SetProcessDPIAware));

pub fn cursor_position() -> Result<PhysicalPosition<f64>, ExternalError> {
let mut pt = POINT { x: 0, y: 0 };
win_to_err(unsafe { GetCursorPos(&mut pt) })
.map(|_| (pt.x, pt.y).into())
.map_err(|e| ExternalError::Os(os_error!(e)))
}