diff --git a/src/event_loop.rs b/src/event_loop.rs index 1cb552100..91983eb55 100644 --- a/src/event_loop.rs +++ b/src/event_loop.rs @@ -213,6 +213,19 @@ impl EventLoopWindowTarget { self.p.primary_monitor() } + /// Returns the monitor that contains the given point. + /// + /// ## Platform-specific: + /// + /// - **Android / iOS:** Unsupported. + #[inline] + pub fn monitor_from_point(&self, x: f64, y: f64) -> Option { + self + .p + .monitor_from_point(x, y) + .map(|inner| MonitorHandle { inner }) + } + /// Change [`DeviceEvent`] filter mode. /// /// Since the [`DeviceEvent`] capture can lead to high CPU usage for unfocused windows, winit diff --git a/src/platform_impl/android/mod.rs b/src/platform_impl/android/mod.rs index 9cb236879..d7da777fc 100644 --- a/src/platform_impl/android/mod.rs +++ b/src/platform_impl/android/mod.rs @@ -486,6 +486,12 @@ impl EventLoopWindowTarget { }) } + #[inline] + pub fn monitor_from_point(&self, x: f64, y: f64) -> Option { + warn!("`Window::monitor_from_point` is ignored on Android"); + return None; + } + pub fn available_monitors(&self) -> VecDeque { let mut v = VecDeque::with_capacity(1); v.push_back(MonitorHandle); @@ -546,6 +552,12 @@ impl Window { v } + #[inline] + pub fn monitor_from_point(&self, x: f64, y: f64) -> Option { + warn!("`Window::monitor_from_point` is ignored on Android"); + None + } + pub fn current_monitor(&self) -> Option { Some(monitor::MonitorHandle { inner: MonitorHandle, diff --git a/src/platform_impl/ios/event_loop.rs b/src/platform_impl/ios/event_loop.rs index dbd580654..d8af48f56 100644 --- a/src/platform_impl/ios/event_loop.rs +++ b/src/platform_impl/ios/event_loop.rs @@ -64,6 +64,12 @@ impl EventLoopWindowTarget { unsafe { monitor::uiscreens() } } + #[inline] + pub fn monitor_from_point(&self, x: f64, y: f64) -> Option { + warn!("`Window::monitor_from_point` is ignored on iOS"); + return None; + } + pub fn primary_monitor(&self) -> Option { // guaranteed to be on main thread let monitor = unsafe { monitor::main_uiscreen() }; diff --git a/src/platform_impl/ios/window.rs b/src/platform_impl/ios/window.rs index c210541df..48423d9a1 100644 --- a/src/platform_impl/ios/window.rs +++ b/src/platform_impl/ios/window.rs @@ -383,6 +383,12 @@ impl Inner { unsafe { monitor::uiscreens() } } + #[inline] + pub fn monitor_from_point(&self, x: f64, y: f64) -> Option { + warn!("`Window::monitor_from_point` is ignored on iOS"); + None + } + pub fn primary_monitor(&self) -> Option { let monitor = unsafe { monitor::main_uiscreen() }; Some(RootMonitorHandle { inner: monitor }) diff --git a/src/platform_impl/linux/event_loop.rs b/src/platform_impl/linux/event_loop.rs index d363074b2..97624cc1b 100644 --- a/src/platform_impl/linux/event_loop.rs +++ b/src/platform_impl/linux/event_loop.rs @@ -37,7 +37,7 @@ use crate::{ use super::{ keyboard, - monitor::MonitorHandle, + monitor::{self, MonitorHandle}, window::{WindowId, WindowRequest}, }; @@ -57,6 +57,10 @@ pub struct EventLoopWindowTarget { } impl EventLoopWindowTarget { + #[inline] + pub fn monitor_from_point(&self, x: f64, y: f64) -> Option { + monitor::from_point(&self.display, x, y) + } #[inline] pub fn available_monitors(&self) -> VecDeque { let mut handles = VecDeque::new(); diff --git a/src/platform_impl/linux/monitor.rs b/src/platform_impl/linux/monitor.rs index 14e0a4074..7ab727d57 100644 --- a/src/platform_impl/linux/monitor.rs +++ b/src/platform_impl/linux/monitor.rs @@ -2,6 +2,8 @@ // Copyright 2021-2022 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 +use gdk::Display; + use crate::{ dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize}, monitor::{MonitorHandle as RootMonitorHandle, VideoMode as RootVideoMode}, @@ -85,3 +87,14 @@ impl VideoMode { panic!("VideoMode is unsupported on Linux.") } } + +pub fn from_point(display: &Display, x: f64, y: f64) -> Option { + if let Some(monitor) = display.monitor_at_point(x as i32, y as i32) { + (0..display.n_monitors()) + .map(|i| (i, display.monitor(i).unwrap())) + .find(|cur| cur.1.geometry() == monitor.geometry()) + .map(|x| MonitorHandle::new(display, x.0)) + } else { + None + } +} diff --git a/src/platform_impl/linux/window.rs b/src/platform_impl/linux/window.rs index 9a45f8678..7996979b8 100644 --- a/src/platform_impl/linux/window.rs +++ b/src/platform_impl/linux/window.rs @@ -29,8 +29,10 @@ use crate::{ }; use super::{ - event_loop::EventLoopWindowTarget, menu, monitor::MonitorHandle, Parent, - PlatformSpecificWindowBuilderAttributes, + event_loop::EventLoopWindowTarget, + menu, + monitor::{self, MonitorHandle}, + Parent, PlatformSpecificWindowBuilderAttributes, }; #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] @@ -749,6 +751,12 @@ impl Window { Some(RootMonitorHandle { inner: handle }) } + #[inline] + pub fn monitor_from_point(&self, x: f64, y: f64) -> Option { + let display = &self.window.display(); + monitor::from_point(display, x, y).map(|inner| RootMonitorHandle { inner }) + } + pub fn raw_window_handle(&self) -> RawWindowHandle { // TODO: add wayland support let mut window_handle = XlibWindowHandle::empty(); diff --git a/src/platform_impl/macos/event_loop.rs b/src/platform_impl/macos/event_loop.rs index 16257b9f6..8c0fa5fa4 100644 --- a/src/platform_impl/macos/event_loop.rs +++ b/src/platform_impl/macos/event_loop.rs @@ -84,6 +84,11 @@ impl EventLoopWindowTarget { monitor::available_monitors() } + #[inline] + pub fn monitor_from_point(&self, x: f64, y: f64) -> Option { + monitor::from_point(x, y) + } + #[inline] pub fn primary_monitor(&self) -> Option { let monitor = monitor::primary_monitor(); diff --git a/src/platform_impl/macos/ffi.rs b/src/platform_impl/macos/ffi.rs index c2428d607..4c3c510c6 100644 --- a/src/platform_impl/macos/ffi.rs +++ b/src/platform_impl/macos/ffi.rs @@ -14,6 +14,7 @@ use std::ffi::c_void; use cocoa::{ + appkit::CGPoint, base::id, foundation::{NSInteger, NSUInteger}, }; @@ -23,7 +24,8 @@ use core_foundation::{ }; use core_graphics::{ base::CGError, - display::{CGDirectDisplayID, CGDisplayConfigRef}, + display::{boolean_t, CGDirectDisplayID, CGDisplayConfigRef}, + geometry::CGRect, }; pub const NSNotFound: NSInteger = NSInteger::max_value(); @@ -221,6 +223,7 @@ extern "C" { blueBlend: f32, synchronous: Boolean, ) -> CGError; + pub fn CGRectContainsPoint(rect: CGRect, point: CGPoint) -> boolean_t; pub fn CGReleaseDisplayFadeReservation(token: CGDisplayFadeReservationToken) -> CGError; pub fn CGShieldingWindowLevel() -> CGWindowLevel; pub fn CGDisplaySetDisplayMode( diff --git a/src/platform_impl/macos/monitor.rs b/src/platform_impl/macos/monitor.rs index 010a9419c..ae35542c4 100644 --- a/src/platform_impl/macos/monitor.rs +++ b/src/platform_impl/macos/monitor.rs @@ -4,13 +4,16 @@ use std::{collections::VecDeque, fmt}; -use super::{ffi, util}; +use super::{ + ffi::{self, CGRectContainsPoint}, + util, +}; use crate::{ dpi::{PhysicalPosition, PhysicalSize}, monitor::{MonitorHandle as RootMonitorHandle, VideoMode as RootVideoMode}, }; use cocoa::{ - appkit::NSScreen, + appkit::{CGPoint, NSScreen}, base::{id, nil}, foundation::NSUInteger, }; @@ -158,6 +161,19 @@ pub fn primary_monitor() -> MonitorHandle { MonitorHandle(CGDisplay::main().id) } +// `from_point` get a monitor handle which contains the given point. +pub fn from_point(x: f64, y: f64) -> Option { + unsafe { + for monitor in available_monitors() { + let bound = CGDisplayBounds(monitor.0); + if CGRectContainsPoint(bound, CGPoint::new(x, y)) > 0 { + return Some(monitor); + } + } + } + return None; +} + impl fmt::Debug for MonitorHandle { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { // TODO: Do this using the proper fmt API diff --git a/src/platform_impl/macos/window.rs b/src/platform_impl/macos/window.rs index f7538d3bc..d064bf7f3 100644 --- a/src/platform_impl/macos/window.rs +++ b/src/platform_impl/macos/window.rs @@ -1320,6 +1320,10 @@ impl UnownedWindow { pub fn current_monitor(&self) -> Option { Some(self.current_monitor_inner()) } + #[inline] + pub fn monitor_from_point(&self, x: f64, y: f64) -> Option { + monitor::from_point(x, y).map(|inner| RootMonitorHandle { inner }) + } #[inline] pub fn available_monitors(&self) -> VecDeque { diff --git a/src/platform_impl/windows/event_loop.rs b/src/platform_impl/windows/event_loop.rs index c0f3e904c..2ea9c5935 100644 --- a/src/platform_impl/windows/event_loop.rs +++ b/src/platform_impl/windows/event_loop.rs @@ -308,6 +308,10 @@ impl EventLoopWindowTarget { Some(RootMonitorHandle { inner: monitor }) } + pub fn monitor_from_point(&self, x: f64, y: f64) -> Option { + monitor::from_point(x, y) + } + pub fn raw_display_handle(&self) -> RawDisplayHandle { RawDisplayHandle::Windows(WindowsDisplayHandle::empty()) } diff --git a/src/platform_impl/windows/monitor.rs b/src/platform_impl/windows/monitor.rs index 93a7d5f02..0d1196f4b 100644 --- a/src/platform_impl/windows/monitor.rs +++ b/src/platform_impl/windows/monitor.rs @@ -123,6 +123,23 @@ pub fn current_monitor(hwnd: HWND) -> MonitorHandle { MonitorHandle::new(hmonitor) } +pub fn from_point(x: f64, y: f64) -> Option { + let hmonitor = unsafe { + MonitorFromPoint( + POINT { + x: x as i32, + y: y as i32, + }, + MONITOR_DEFAULTTONULL, + ) + }; + if hmonitor.is_invalid() { + Some(MonitorHandle::new(hmonitor)) + } else { + None + } +} + impl Window { pub fn available_monitors(&self) -> VecDeque { available_monitors() @@ -132,6 +149,10 @@ impl Window { let monitor = primary_monitor(); Some(RootMonitorHandle { inner: monitor }) } + + pub fn monitor_from_point(&self, x: f64, y: f64) -> Option { + from_point(x, y).map(|inner| RootMonitorHandle { inner }) + } } pub(crate) fn get_monitor_info(hmonitor: HMONITOR) -> Result { diff --git a/src/window.rs b/src/window.rs index c309b8715..2638ec0a9 100644 --- a/src/window.rs +++ b/src/window.rs @@ -1184,6 +1184,16 @@ impl Window { self.window.current_monitor() } + #[inline] + /// Returns the monitor that contains the given point. + /// + /// ## Platform-specific: + /// + /// - **Android / iOS:** Unsupported. + pub fn monitor_from_point(&self, x: f64, y: f64) -> Option { + self.window.monitor_from_point(x, y) + } + /// Returns the list of all the monitors available on the system. /// /// This is the same as `EventLoopWindowTarget::available_monitors`, and is provided for convenience.