diff --git a/CHANGELOG.md b/CHANGELOG.md index 13c99439..cd95c68a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ # Unreleased - Add web support via the 'stdweb' or 'web-sys' features +- On Windows, implemented function to get HINSTANCE - On macOS, implement `run_return`. - On iOS, fix inverted parameter in `set_prefers_home_indicator_hidden`. - On X11, performance is improved when rapidly calling `Window::set_cursor_icon`. @@ -21,7 +22,17 @@ - On X11, return dummy monitor data to avoid panicking when no monitors exist. - On X11, prevent stealing input focus when creating a new window. Only steal input focus when entering fullscreen mode. -- On Wayland, fixed DeviceEvents for relative mouse movement is not always produced +- On Wayland, add support for set_cursor_visible and set_cursor_grab. +- On Wayland, fixed DeviceEvents for relative mouse movement is not always produced. +- Removed `derivative` crate dependency. +- On Wayland, add support for set_cursor_icon. +- Use `impl Iterator` instead of `AvailableMonitorsIter` consistently. +- On macOS, fix fullscreen state being updated after entering fullscreen instead of before, + resulting in `Window::fullscreen` returning the old state in `Resized` events instead of + reflecting the new fullscreen state +- On X11, fix use-after-free during window creation +- On Windows, disable monitor change keyboard shortcut while in exclusive fullscreen. +- On Windows, ensure that changing a borderless fullscreen window's monitor via keyboard shortcuts keeps the window fullscreen on the new monitor. # 0.20.0 Alpha 3 (2019-08-14) diff --git a/Cargo.toml b/Cargo.toml index 80ca553f..b2a21fa3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,8 +24,7 @@ lazy_static = "1" libc = "0.2" log = "0.4" serde = { version = "1", optional = true, features = ["serde_derive"] } -derivative = "1.0.2" -raw-window-handle = "0.2" +raw-window-handle = "0.3" [dev-dependencies] image = "0.21" diff --git a/examples/monitor_list.rs b/examples/monitor_list.rs index 55a2dde8..a6b24d29 100644 --- a/examples/monitor_list.rs +++ b/examples/monitor_list.rs @@ -4,6 +4,6 @@ fn main() { let event_loop = EventLoop::new(); let window = WindowBuilder::new().build(&event_loop).unwrap(); - dbg!(window.available_monitors()); + dbg!(window.available_monitors().collect::>()); dbg!(window.primary_monitor()); } diff --git a/src/event_loop.rs b/src/event_loop.rs index 72c5f094..5bb9692b 100644 --- a/src/event_loop.rs +++ b/src/event_loop.rs @@ -13,9 +13,7 @@ use instant::Instant; use std::ops::Deref; use std::{error, fmt}; -use crate::event::Event; -use crate::monitor::{AvailableMonitorsIter, MonitorHandle}; -use crate::platform_impl; +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 /// the events loop. @@ -150,10 +148,10 @@ impl EventLoop { /// Returns the list of all the monitors available on the system. #[inline] pub fn available_monitors(&self) -> impl Iterator { - let data = self.event_loop.available_monitors(); - AvailableMonitorsIter { - data: data.into_iter(), - } + self.event_loop + .available_monitors() + .into_iter() + .map(|inner| MonitorHandle { inner }) } /// Returns the primary monitor of the system. diff --git a/src/lib.rs b/src/lib.rs index 6eed4f04..8d5447f7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -121,8 +121,6 @@ extern crate log; #[macro_use] extern crate serde; #[macro_use] -extern crate derivative; -#[macro_use] #[cfg(any(target_os = "ios", target_os = "windows"))] extern crate bitflags; #[cfg(any(target_os = "macos", target_os = "ios"))] diff --git a/src/monitor.rs b/src/monitor.rs index 6db5f12b..9ef58be5 100644 --- a/src/monitor.rs +++ b/src/monitor.rs @@ -1,63 +1,36 @@ //! Types useful for interacting with a user's monitors. //! //! If you want to get basic information about a monitor, you can use the [`MonitorHandle`][monitor_id] -//! type. This is retreived from an [`AvailableMonitorsIter`][monitor_iter], which can be acquired -//! with: +//! type. This is retreived from one of the following methods, which return an iterator of +//! [`MonitorHandle`][monitor_id]: //! - [`EventLoop::available_monitors`][loop_get] //! - [`Window::available_monitors`][window_get]. //! //! [monitor_id]: ./struct.MonitorHandle.html -//! [monitor_iter]: ./struct.AvailableMonitorsIter.html //! [loop_get]: ../event_loop/struct.EventLoop.html#method.available_monitors //! [window_get]: ../window/struct.Window.html#method.available_monitors -use std::collections::vec_deque::IntoIter as VecDequeIter; - use crate::{ dpi::{PhysicalPosition, PhysicalSize}, platform_impl, }; -/// An iterator over all available monitors. -/// -/// Can be acquired with: -/// - [`EventLoop::available_monitors`][loop_get] -/// - [`Window::available_monitors`][window_get]. -/// -/// [loop_get]: ../event_loop/struct.EventLoop.html#method.available_monitors -/// [window_get]: ../window/struct.Window.html#method.available_monitors -// Implementation note: we retrieve the list once, then serve each element by one by one. -// This may change in the future. -#[derive(Debug)] -pub struct AvailableMonitorsIter { - pub(crate) data: VecDequeIter, -} - -impl Iterator for AvailableMonitorsIter { - type Item = MonitorHandle; - - #[inline] - fn next(&mut self) -> Option { - self.data.next().map(|id| MonitorHandle { inner: id }) - } - - #[inline] - fn size_hint(&self) -> (usize, Option) { - self.data.size_hint() - } -} - /// Describes a fullscreen video mode of a monitor. /// /// Can be acquired with: /// - [`MonitorHandle::video_modes`][monitor_get]. /// /// [monitor_get]: ../monitor/struct.MonitorHandle.html#method.video_modes -#[derive(Derivative)] -#[derivative(Clone, Debug = "transparent", PartialEq, Eq, Hash)] +#[derive(Clone, PartialEq, Eq, Hash)] pub struct VideoMode { pub(crate) video_mode: platform_impl::VideoMode, } +impl std::fmt::Debug for VideoMode { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.video_mode.fmt(f) + } +} + impl PartialOrd for VideoMode { fn partial_cmp(&self, other: &VideoMode) -> Option { Some(self.cmp(other)) diff --git a/src/platform/windows.rs b/src/platform/windows.rs index 43133e04..0b93b330 100644 --- a/src/platform/windows.rs +++ b/src/platform/windows.rs @@ -34,6 +34,8 @@ impl EventLoopExtWindows for EventLoop { /// Additional methods on `Window` that are specific to Windows. pub trait WindowExtWindows { + /// Returns the HINSTANCE of the window + fn hinstance(&self) -> *mut libc::c_void; /// Returns the native handle that is used by this window. /// /// The pointer will become invalid when the native window was destroyed. @@ -44,6 +46,11 @@ pub trait WindowExtWindows { } impl WindowExtWindows for Window { + #[inline] + fn hinstance(&self) -> *mut libc::c_void { + self.window.hinstance() as *mut _ + } + #[inline] fn hwnd(&self) -> *mut libc::c_void { self.window.hwnd() as *mut _ diff --git a/src/platform_impl/linux/wayland/event_loop.rs b/src/platform_impl/linux/wayland/event_loop.rs index 272fc832..cbe08799 100644 --- a/src/platform_impl/linux/wayland/event_loop.rs +++ b/src/platform_impl/linux/wayland/event_loop.rs @@ -7,11 +7,19 @@ use std::{ time::Instant, }; +use smithay_client_toolkit::reexports::protocols::unstable::pointer_constraints::v1::client::{ + zwp_locked_pointer_v1::ZwpLockedPointerV1, zwp_pointer_constraints_v1::ZwpPointerConstraintsV1, +}; use smithay_client_toolkit::reexports::protocols::unstable::relative_pointer::v1::client::{ zwp_relative_pointer_manager_v1::ZwpRelativePointerManagerV1, zwp_relative_pointer_v1::ZwpRelativePointerV1, }; +use smithay_client_toolkit::pointer::{AutoPointer, AutoThemer}; +use smithay_client_toolkit::reexports::client::protocol::{ + wl_compositor::WlCompositor, wl_shm::WlShm, wl_surface::WlSurface, +}; + use crate::{ dpi::{PhysicalPosition, PhysicalSize}, event::ModifiersState, @@ -21,6 +29,7 @@ use crate::{ sticky_exit_callback, MonitorHandle as PlatformMonitorHandle, VideoMode as PlatformVideoMode, }, + window::CursorIcon, }; use super::{window::WindowStore, DeviceId, WindowId}; @@ -69,16 +78,159 @@ impl WindowEventsSink { } } +pub struct CursorManager { + pointer_constraints_proxy: Arc>>, + auto_themer: Option, + pointers: Vec, + locked_pointers: Vec, + cursor_visible: bool, + current_cursor: CursorIcon, +} + +impl CursorManager { + fn new(constraints: Arc>>) -> CursorManager { + CursorManager { + pointer_constraints_proxy: constraints, + auto_themer: None, + pointers: Vec::new(), + locked_pointers: Vec::new(), + cursor_visible: true, + current_cursor: CursorIcon::default(), + } + } + + fn register_pointer(&mut self, pointer: wl_pointer::WlPointer) { + let auto_themer = self + .auto_themer + .as_ref() + .expect("AutoThemer not initialized. Server did not advertise shm or compositor?"); + self.pointers.push(auto_themer.theme_pointer(pointer)); + } + + fn set_auto_themer(&mut self, auto_themer: AutoThemer) { + self.auto_themer = Some(auto_themer); + } + + pub fn set_cursor_visible(&mut self, visible: bool) { + if !visible { + for pointer in self.pointers.iter() { + (**pointer).set_cursor(0, None, 0, 0); + } + } else { + self.set_cursor_icon_impl(self.current_cursor); + } + self.cursor_visible = visible; + } + + /// A helper function to restore cursor styles on PtrEvent::Enter. + pub fn reload_cursor_style(&mut self) { + if !self.cursor_visible { + self.set_cursor_visible(false); + } else { + self.set_cursor_icon_impl(self.current_cursor); + } + } + + pub fn set_cursor_icon(&mut self, cursor: CursorIcon) { + if self.cursor_visible && cursor != self.current_cursor { + self.current_cursor = cursor; + + self.set_cursor_icon_impl(cursor); + } + } + + fn set_cursor_icon_impl(&mut self, cursor: CursorIcon) { + let cursor = match cursor { + CursorIcon::Alias => "link", + CursorIcon::Arrow => "arrow", + CursorIcon::Cell => "plus", + CursorIcon::Copy => "copy", + CursorIcon::Crosshair => "crosshair", + CursorIcon::Default => "left_ptr", + CursorIcon::Hand => "hand", + CursorIcon::Help => "question_arrow", + CursorIcon::Move => "move", + CursorIcon::Grab => "grab", + CursorIcon::Grabbing => "grabbing", + CursorIcon::Progress => "progress", + CursorIcon::AllScroll => "all-scroll", + CursorIcon::ContextMenu => "context-menu", + + CursorIcon::NoDrop => "no-drop", + CursorIcon::NotAllowed => "crossed_circle", + + // Resize cursors + CursorIcon::EResize => "right_side", + CursorIcon::NResize => "top_side", + CursorIcon::NeResize => "top_right_corner", + CursorIcon::NwResize => "top_left_corner", + CursorIcon::SResize => "bottom_side", + CursorIcon::SeResize => "bottom_right_corner", + CursorIcon::SwResize => "bottom_left_corner", + CursorIcon::WResize => "left_side", + CursorIcon::EwResize => "h_double_arrow", + CursorIcon::NsResize => "v_double_arrow", + CursorIcon::NwseResize => "bd_double_arrow", + CursorIcon::NeswResize => "fd_double_arrow", + CursorIcon::ColResize => "h_double_arrow", + CursorIcon::RowResize => "v_double_arrow", + + CursorIcon::Text => "text", + CursorIcon::VerticalText => "vertical-text", + + CursorIcon::Wait => "watch", + + CursorIcon::ZoomIn => "zoom-in", + CursorIcon::ZoomOut => "zoom-out", + }; + + for pointer in self.pointers.iter() { + // Ignore erros, since we don't want to fail hard in case we can't find a proper cursor + // in a given theme. + let _ = pointer.set_cursor(cursor, None); + } + } + + pub fn grab_pointer(&mut self, surface: Option<&WlSurface>) { + for locked_pointer in self.locked_pointers.drain(..) { + locked_pointer.destroy(); + } + + if let Some(surface) = surface { + for pointer in self.pointers.iter() { + let locked_pointer = self + .pointer_constraints_proxy + .try_lock() + .unwrap() + .as_ref() + .and_then(|pointer_constraints| { + super::pointer::implement_locked_pointer( + surface, + &**pointer, + pointer_constraints, + ) + .ok() + }); + + if let Some(locked_pointer) = locked_pointer { + self.locked_pointers.push(locked_pointer); + } + } + } + } +} + pub struct EventLoop { // The loop inner_loop: ::calloop::EventLoop<()>, // The wayland display pub display: Arc, - // the output manager + // The output manager pub outputs: OutputMgr, - // our sink, shared with some handlers, buffering the events + // Our sink, shared with some handlers, buffering the events sink: Arc>>, pending_user_events: Rc>>, + // Utility for grabbing the cursor and changing visibility _user_source: ::calloop::Source<::calloop::channel::Channel>, user_sender: ::calloop::channel::Sender, _kbd_source: ::calloop::Source< @@ -91,17 +243,19 @@ pub struct EventLoop { // // We should only try and wake up the `EventLoop` if it still exists, so we hold Weak ptrs. pub struct EventLoopProxy { - user_sender: ::calloop::channel::Sender, + user_sender: calloop::channel::Sender, } pub struct EventLoopWindowTarget { - // the event queue + // The event queue pub evq: RefCell<::calloop::Source>, // The window store pub store: Arc>, - // the env + // The cursor manager + pub cursor_manager: Arc>, + // The env pub env: Environment, - // a cleanup switch to prune dead windows + // A cleanup switch to prune dead windows pub cleanup_needed: Arc>, // The wayland display pub display: Arc, @@ -146,14 +300,24 @@ impl EventLoop { }) .unwrap(); + let pointer_constraints_proxy = Arc::new(Mutex::new(None)); + let mut seat_manager = SeatManager { sink: sink.clone(), relative_pointer_manager_proxy: Rc::new(RefCell::new(None)), + pointer_constraints_proxy: pointer_constraints_proxy.clone(), store: store.clone(), seats: seats.clone(), kbd_sender, + cursor_manager: Arc::new(Mutex::new(CursorManager::new(pointer_constraints_proxy))), }; + let cursor_manager = seat_manager.cursor_manager.clone(); + let cursor_manager_clone = cursor_manager.clone(); + + let shm_cell = Rc::new(RefCell::new(None)); + let compositor_cell = Rc::new(RefCell::new(None)); + let env = Environment::from_display_with_cb( &display, &mut event_queue, @@ -175,6 +339,42 @@ impl EventLoop { .try_borrow_mut() .unwrap() = Some(relative_pointer_manager_proxy); } + if interface == "zwp_pointer_constraints_v1" { + let pointer_constraints_proxy = registry + .bind(version, id, move |pointer_constraints| { + pointer_constraints.implement_closure(|_, _| (), ()) + }) + .unwrap(); + + *seat_manager.pointer_constraints_proxy.lock().unwrap() = + Some(pointer_constraints_proxy); + } + if interface == "wl_shm" { + let shm: WlShm = registry + .bind(version, id, move |shm| shm.implement_closure(|_, _| (), ())) + .unwrap(); + + (*shm_cell.borrow_mut()) = Some(shm); + } + if interface == "wl_compositor" { + let compositor: WlCompositor = registry + .bind(version, id, move |compositor| { + compositor.implement_closure(|_, _| (), ()) + }) + .unwrap(); + (*compositor_cell.borrow_mut()) = Some(compositor); + } + + if compositor_cell.borrow().is_some() && shm_cell.borrow().is_some() { + let compositor = compositor_cell.borrow_mut().take().unwrap(); + let shm = shm_cell.borrow_mut().take().unwrap(); + let auto_themer = AutoThemer::init(None, compositor, &shm); + cursor_manager_clone + .lock() + .unwrap() + .set_auto_themer(auto_themer); + } + if interface == "wl_seat" { seat_manager.add_seat(id, version, registry) } @@ -207,6 +407,7 @@ impl EventLoop { }) .unwrap(); + let cursor_manager_clone = cursor_manager.clone(); Ok(EventLoop { inner_loop, sink, @@ -221,6 +422,7 @@ impl EventLoop { evq: RefCell::new(source), store, env, + cursor_manager: cursor_manager_clone, cleanup_needed: Arc::new(Mutex::new(false)), seats, display, @@ -242,7 +444,7 @@ impl EventLoop { F: 'static + FnMut(crate::event::Event, &RootELW, &mut ControlFlow), { self.run_return(callback); - ::std::process::exit(0); + std::process::exit(0); } pub fn run_return(&mut self, mut callback: F) @@ -497,6 +699,8 @@ struct SeatManager { seats: Arc>>, kbd_sender: ::calloop::channel::Sender<(crate::event::WindowEvent, super::WindowId)>, relative_pointer_manager_proxy: Rc>>, + pointer_constraints_proxy: Arc>>, + cursor_manager: Arc>, } impl SeatManager { @@ -513,6 +717,7 @@ impl SeatManager { touch: None, kbd_sender: self.kbd_sender.clone(), modifiers_tracker: Arc::new(Mutex::new(ModifiersState::default())), + cursor_manager: self.cursor_manager.clone(), }; let seat = registry .bind(min(version, 5), id, move |seat| { @@ -544,6 +749,7 @@ struct SeatData { keyboard: Option, touch: Option, modifiers_tracker: Arc>, + cursor_manager: Arc>, } impl SeatData { @@ -558,8 +764,14 @@ impl SeatData { self.sink.clone(), self.store.clone(), self.modifiers_tracker.clone(), + self.cursor_manager.clone(), )); + self.cursor_manager + .lock() + .unwrap() + .register_pointer(self.pointer.as_ref().unwrap().clone()); + self.relative_pointer = self .relative_pointer_manager_proxy .try_borrow() diff --git a/src/platform_impl/linux/wayland/pointer.rs b/src/platform_impl/linux/wayland/pointer.rs index 7ffcdd72..cad299e4 100644 --- a/src/platform_impl/linux/wayland/pointer.rs +++ b/src/platform_impl/linux/wayland/pointer.rs @@ -5,7 +5,11 @@ use crate::event::{ WindowEvent, }; -use super::{event_loop::WindowEventsSink, window::WindowStore, DeviceId}; +use super::{ + event_loop::{CursorManager, WindowEventsSink}, + window::WindowStore, + DeviceId, +}; use smithay_client_toolkit::reexports::client::protocol::{ wl_pointer::{self, Event as PtrEvent, WlPointer}, @@ -17,11 +21,19 @@ use smithay_client_toolkit::reexports::protocols::unstable::relative_pointer::v1 zwp_relative_pointer_v1::ZwpRelativePointerV1, }; +use smithay_client_toolkit::reexports::protocols::unstable::pointer_constraints::v1::client::{ + zwp_locked_pointer_v1::ZwpLockedPointerV1, zwp_pointer_constraints_v1::Lifetime, + zwp_pointer_constraints_v1::ZwpPointerConstraintsV1, +}; + +use smithay_client_toolkit::reexports::client::protocol::wl_surface::WlSurface; + pub fn implement_pointer( seat: &wl_seat::WlSeat, sink: Arc>>, store: Arc>, modifiers_tracker: Arc>, + cursor_manager: Arc>, ) -> WlPointer { seat.get_pointer(|pointer| { let mut mouse_focus = None; @@ -33,6 +45,7 @@ pub fn implement_pointer( move |evt, pointer| { let mut sink = sink.lock().unwrap(); let store = store.lock().unwrap(); + let mut cursor_manager = cursor_manager.lock().unwrap(); match evt { PtrEvent::Enter { surface, @@ -62,6 +75,8 @@ pub fn implement_pointer( wid, ); } + + cursor_manager.reload_cursor_style(); } PtrEvent::Leave { surface, .. } => { mouse_focus = None; @@ -241,3 +256,13 @@ pub fn implement_relative_pointer( ) }) } + +pub fn implement_locked_pointer( + surface: &WlSurface, + pointer: &WlPointer, + constraints: &ZwpPointerConstraintsV1, +) -> Result { + constraints.lock_pointer(surface, pointer, None, Lifetime::Persistent.to_raw(), |c| { + c.implement_closure(|_, _| (), ()) + }) +} diff --git a/src/platform_impl/linux/wayland/window.rs b/src/platform_impl/linux/wayland/window.rs index ddd4b4b9..f94b8177 100644 --- a/src/platform_impl/linux/wayland/window.rs +++ b/src/platform_impl/linux/wayland/window.rs @@ -1,6 +1,7 @@ use raw_window_handle::unix::WaylandHandle; use std::{ collections::VecDeque, + mem::replace, sync::{Arc, Mutex, Weak}, }; @@ -26,11 +27,12 @@ use smithay_client_toolkit::{ window::{ConceptFrame, Event as WEvent, State as WState, Theme, Window as SWindow}, }; -use super::{make_wid, EventLoopWindowTarget, MonitorHandle, WindowId}; +use super::{event_loop::CursorManager, make_wid, EventLoopWindowTarget, MonitorHandle, WindowId}; pub struct Window { surface: wl_surface::WlSurface, frame: Arc>>, + cursor_manager: Arc>, outputs: OutputMgr, // Access to info for all monitors size: Arc>, kill_switch: (Arc>, Arc>), @@ -52,6 +54,7 @@ impl Window { let fullscreen = Arc::new(Mutex::new(false)); let window_store = evlp.store.clone(); + let cursor_manager = evlp.cursor_manager.clone(); let surface = evlp.env.create_surface(move |dpi, surface| { window_store.lock().unwrap().dpi_change(&surface, dpi); surface.set_buffer_scale(dpi); @@ -165,6 +168,7 @@ impl Window { kill_switch: (kill_switch, evlp.cleanup_needed.clone()), need_frame_refresh, need_refresh, + cursor_manager, fullscreen, }) } @@ -292,18 +296,26 @@ impl Window { } #[inline] - pub fn set_cursor_icon(&self, _cursor: CursorIcon) { - // TODO + pub fn set_cursor_icon(&self, cursor: CursorIcon) { + let mut cursor_manager = self.cursor_manager.lock().unwrap(); + cursor_manager.set_cursor_icon(cursor); } #[inline] - pub fn set_cursor_visible(&self, _visible: bool) { - // TODO: This isn't possible on Wayland yet + pub fn set_cursor_visible(&self, visible: bool) { + let mut cursor_manager = self.cursor_manager.lock().unwrap(); + cursor_manager.set_cursor_visible(visible); } #[inline] - pub fn set_cursor_grab(&self, _grab: bool) -> Result<(), ExternalError> { - Err(ExternalError::NotSupported(NotSupportedError::new())) + pub fn set_cursor_grab(&self, grab: bool) -> Result<(), ExternalError> { + let mut cursor_manager = self.cursor_manager.lock().unwrap(); + if grab { + cursor_manager.grab_pointer(Some(&self.surface)); + } else { + cursor_manager.grab_pointer(None); + } + Ok(()) } #[inline] @@ -440,8 +452,8 @@ impl WindowStore { window.newsize.take(), &mut *(window.size.lock().unwrap()), window.new_dpi, - ::std::mem::replace(&mut *window.need_refresh.lock().unwrap(), false), - ::std::mem::replace(&mut *window.need_frame_refresh.lock().unwrap(), false), + replace(&mut *window.need_refresh.lock().unwrap(), false), + replace(&mut *window.need_frame_refresh.lock().unwrap(), false), window.closed, make_wid(&window.surface), opt_mutex_lock.as_mut().map(|m| &mut **m), diff --git a/src/platform_impl/linux/x11/util/randr.rs b/src/platform_impl/linux/x11/util/randr.rs index 1fbef41d..28fcb601 100644 --- a/src/platform_impl/linux/x11/util/randr.rs +++ b/src/platform_impl/linux/x11/util/randr.rs @@ -99,7 +99,8 @@ impl XConnection { // video mode is returned to the user monitor: None, } - }); + }) + .collect(); let name_slice = slice::from_raw_parts( (*output_info).name as *mut u8, @@ -119,7 +120,7 @@ impl XConnection { }; (self.xrandr.XRRFreeOutputInfo)(output_info); - Some((name, hidpi_factor, modes.collect())) + Some((name, hidpi_factor, modes)) } pub fn set_crtc_config(&self, crtc_id: RRCrtc, mode_id: RRMode) -> Result<(), ()> { unsafe { diff --git a/src/platform_impl/macos/monitor.rs b/src/platform_impl/macos/monitor.rs index c9408890..7688e31a 100644 --- a/src/platform_impl/macos/monitor.rs +++ b/src/platform_impl/macos/monitor.rs @@ -22,17 +22,46 @@ use core_video_sys::{ CVDisplayLinkGetNominalOutputVideoRefreshPeriod, CVDisplayLinkRelease, }; -#[derive(Derivative)] -#[derivative(Debug, Clone, PartialEq, Hash)] +#[derive(Clone)] pub struct VideoMode { pub(crate) size: (u32, u32), pub(crate) bit_depth: u16, pub(crate) refresh_rate: u16, pub(crate) monitor: MonitorHandle, - #[derivative(Debug = "ignore", PartialEq = "ignore", Hash = "ignore")] pub(crate) native_mode: NativeDisplayMode, } +impl PartialEq for VideoMode { + fn eq(&self, other: &Self) -> bool { + self.size == other.size + && self.bit_depth == other.bit_depth + && self.refresh_rate == other.refresh_rate + && self.monitor == other.monitor + } +} + +impl Eq for VideoMode {} + +impl std::hash::Hash for VideoMode { + fn hash(&self, state: &mut H) { + self.size.hash(state); + self.bit_depth.hash(state); + self.refresh_rate.hash(state); + self.monitor.hash(state); + } +} + +impl std::fmt::Debug for VideoMode { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("VideoMode") + .field("size", &self.size) + .field("bit_depth", &self.bit_depth) + .field("refresh_rate", &self.refresh_rate) + .field("monitor", &self.monitor) + .finish() + } +} + pub struct NativeDisplayMode(pub ffi::CGDisplayModeRef); unsafe impl Send for NativeDisplayMode {} diff --git a/src/platform_impl/macos/window_delegate.rs b/src/platform_impl/macos/window_delegate.rs index 041edf61..cb1bf3c2 100644 --- a/src/platform_impl/macos/window_delegate.rs +++ b/src/platform_impl/macos/window_delegate.rs @@ -402,7 +402,24 @@ extern "C" fn window_will_enter_fullscreen(this: &Object, _: Sel, _: id) { with_state(this, |state| { state.with_window(|window| { trace!("Locked shared state in `window_will_enter_fullscreen`"); - window.shared_state.lock().unwrap().maximized = window.is_zoomed(); + let mut shared_state = window.shared_state.lock().unwrap(); + shared_state.maximized = window.is_zoomed(); + match shared_state.fullscreen { + // Exclusive mode sets the state in `set_fullscreen` as the user + // can't enter exclusive mode by other means (like the + // fullscreen button on the window decorations) + Some(Fullscreen::Exclusive(_)) => (), + // `window_will_enter_fullscreen` was triggered and we're already + // in fullscreen, so we must've reached here by `set_fullscreen` + // as it updates the state + Some(Fullscreen::Borderless(_)) => (), + // Otherwise, we must've reached fullscreen by the user clicking + // on the green fullscreen button. Update state! + None => { + shared_state.fullscreen = Some(Fullscreen::Borderless(window.current_monitor())) + } + } + trace!("Unlocked shared state in `window_will_enter_fullscreen`"); }) }); @@ -433,25 +450,6 @@ extern "C" fn window_will_use_fullscreen_presentation_options( extern "C" fn window_did_enter_fullscreen(this: &Object, _: Sel, _: id) { trace!("Triggered `windowDidEnterFullscreen:`"); with_state(this, |state| { - state.with_window(|window| { - let monitor = window.current_monitor(); - trace!("Locked shared state in `window_did_enter_fullscreen`"); - let mut shared_state = window.shared_state.lock().unwrap(); - match shared_state.fullscreen { - // Exclusive mode sets the state in `set_fullscreen` as the user - // can't enter exclusive mode by other means (like the - // fullscreen button on the window decorations) - Some(Fullscreen::Exclusive(_)) => (), - // `window_did_enter_fullscreen` was triggered and we're already - // in fullscreen, so we must've reached here by `set_fullscreen` - // as it updates the state - Some(Fullscreen::Borderless(_)) => (), - // Otherwise, we must've reached fullscreen by the user clicking - // on the green fullscreen button. Update state! - None => shared_state.fullscreen = Some(Fullscreen::Borderless(monitor)), - } - trace!("Unlocked shared state in `window_did_enter_fullscreen`"); - }); state.initial_fullscreen = false; }); trace!("Completed `windowDidEnterFullscreen:`"); diff --git a/src/platform_impl/windows/event_loop.rs b/src/platform_impl/windows/event_loop.rs index 94f7255e..b9a0846a 100644 --- a/src/platform_impl/windows/event_loop.rs +++ b/src/platform_impl/windows/event_loop.rs @@ -54,13 +54,14 @@ use crate::{ }, drop_handler::FileDropHandler, event::{self, handle_extended_keys, process_key_params, vkey_to_winit_vkey}, + monitor, raw_input::{get_raw_input_data, get_raw_mouse_button_state}, util, window::adjust_size, window_state::{CursorFlags, WindowFlags, WindowState}, wrap_device_id, WindowId, DEVICE_ID, }, - window::WindowId as RootWindowId, + window::{Fullscreen, WindowId as RootWindowId}, }; type GetPointerFrameInfoHistory = unsafe extern "system" fn( @@ -982,6 +983,51 @@ unsafe extern "system" fn public_window_callback( commctrl::DefSubclassProc(window, msg, wparam, lparam) } + winuser::WM_WINDOWPOSCHANGING => { + let mut window_state = subclass_input.window_state.lock(); + if let Some(ref mut fullscreen) = window_state.fullscreen { + let window_pos = &mut *(lparam as *mut winuser::WINDOWPOS); + let new_rect = RECT { + left: window_pos.x, + top: window_pos.y, + right: window_pos.x + window_pos.cx, + bottom: window_pos.y + window_pos.cy, + }; + let new_monitor = + winuser::MonitorFromRect(&new_rect, winuser::MONITOR_DEFAULTTONULL); + match fullscreen { + Fullscreen::Borderless(ref mut fullscreen_monitor) => { + if new_monitor != fullscreen_monitor.inner.hmonitor() + && new_monitor != ptr::null_mut() + { + if let Ok(new_monitor_info) = monitor::get_monitor_info(new_monitor) { + let new_monitor_rect = new_monitor_info.rcMonitor; + window_pos.x = new_monitor_rect.left; + window_pos.y = new_monitor_rect.top; + window_pos.cx = new_monitor_rect.right - new_monitor_rect.left; + window_pos.cy = new_monitor_rect.bottom - new_monitor_rect.top; + } + *fullscreen_monitor = crate::monitor::MonitorHandle { + inner: monitor::MonitorHandle::new(new_monitor), + }; + } + } + Fullscreen::Exclusive(ref video_mode) => { + let old_monitor = video_mode.video_mode.monitor.hmonitor(); + if let Ok(old_monitor_info) = monitor::get_monitor_info(old_monitor) { + let old_monitor_rect = old_monitor_info.rcMonitor; + window_pos.x = old_monitor_rect.left; + window_pos.y = old_monitor_rect.top; + window_pos.cx = old_monitor_rect.right - old_monitor_rect.left; + window_pos.cy = old_monitor_rect.bottom - old_monitor_rect.top; + } + } + } + } + + 0 + } + // WM_MOVE supplies client area positions, so we send Moved here instead. winuser::WM_WINDOWPOSCHANGED => { use crate::event::WindowEvent::Moved; diff --git a/src/platform_impl/windows/monitor.rs b/src/platform_impl/windows/monitor.rs index cc2e1646..6705f334 100644 --- a/src/platform_impl/windows/monitor.rs +++ b/src/platform_impl/windows/monitor.rs @@ -21,17 +21,46 @@ use crate::{ }, }; -#[derive(Derivative)] -#[derivative(Debug, Clone, Eq, PartialEq, Hash)] +#[derive(Clone)] pub struct VideoMode { pub(crate) size: (u32, u32), pub(crate) bit_depth: u16, pub(crate) refresh_rate: u16, pub(crate) monitor: MonitorHandle, - #[derivative(Debug = "ignore", PartialEq = "ignore", Hash = "ignore")] pub(crate) native_video_mode: wingdi::DEVMODEW, } +impl PartialEq for VideoMode { + fn eq(&self, other: &Self) -> bool { + self.size == other.size + && self.bit_depth == other.bit_depth + && self.refresh_rate == other.refresh_rate + && self.monitor == other.monitor + } +} + +impl Eq for VideoMode {} + +impl std::hash::Hash for VideoMode { + fn hash(&self, state: &mut H) { + self.size.hash(state); + self.bit_depth.hash(state); + self.refresh_rate.hash(state); + self.monitor.hash(state); + } +} + +impl std::fmt::Debug for VideoMode { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("VideoMode") + .field("size", &self.size) + .field("bit_depth", &self.bit_depth) + .field("refresh_rate", &self.refresh_rate) + .field("monitor", &self.monitor) + .finish() + } +} + impl VideoMode { pub fn size(&self) -> PhysicalSize { self.size.into() diff --git a/src/platform_impl/windows/window.rs b/src/platform_impl/windows/window.rs index cb32c2ac..629eae17 100644 --- a/src/platform_impl/windows/window.rs +++ b/src/platform_impl/windows/window.rs @@ -14,7 +14,7 @@ use std::{ use winapi::{ ctypes::c_int, shared::{ - minwindef::{DWORD, LPARAM, UINT, WORD, WPARAM}, + minwindef::{DWORD, HINSTANCE, LPARAM, UINT, WORD, WPARAM}, windef::{HWND, POINT, RECT}, }, um::{ @@ -358,10 +358,16 @@ impl Window { self.window.0 } + #[inline] + pub fn hinstance(&self) -> HINSTANCE { + unsafe { winuser::GetWindowLongW(self.hwnd(), winuser::GWL_HINSTANCE) as *mut _ } + } + #[inline] pub fn raw_window_handle(&self) -> RawWindowHandle { let handle = WindowsHandle { hwnd: self.window.0 as *mut _, + hinstance: self.hinstance() as *mut _, ..WindowsHandle::empty() }; RawWindowHandle::Windows(handle) diff --git a/src/window.rs b/src/window.rs index 6be850cc..440572ee 100644 --- a/src/window.rs +++ b/src/window.rs @@ -5,7 +5,7 @@ use crate::{ dpi::{LogicalPosition, LogicalSize}, error::{ExternalError, NotSupportedError, OsError}, event_loop::EventLoopWindowTarget, - monitor::{AvailableMonitorsIter, MonitorHandle, VideoMode}, + monitor::{MonitorHandle, VideoMode}, platform_impl, }; @@ -674,6 +674,8 @@ impl Window { /// /// - **macOS:** This presently merely locks the cursor in a fixed location, which looks visually /// awkward. + /// - **Wayland:** This presently merely locks the cursor in a fixed location, which looks visually + /// awkward. /// - **Android:** Has no effect. /// - **iOS:** Always returns an Err. /// - **Web:** Has no effect. @@ -690,6 +692,7 @@ impl Window { /// /// - **Windows:** The cursor is only hidden within the confines of the window. /// - **X11:** The cursor is only hidden within the confines of the window. + /// - **Wayland:** The cursor is only hidden within the confines of the window. /// - **macOS:** The cursor is hidden as long as the window has input focus, even if the cursor is /// outside of the window. /// - **iOS:** Has no effect. @@ -720,11 +723,11 @@ impl Window { /// /// **iOS:** Can only be called on the main thread. #[inline] - pub fn available_monitors(&self) -> AvailableMonitorsIter { - let data = self.window.available_monitors(); - AvailableMonitorsIter { - data: data.into_iter(), - } + pub fn available_monitors(&self) -> impl Iterator { + self.window + .available_monitors() + .into_iter() + .map(|inner| MonitorHandle { inner }) } /// Returns the primary monitor of the system.