From 92530299eb26da5f0db46dba809f192034419327 Mon Sep 17 00:00:00 2001 From: Kirill Chibisov Date: Sun, 5 Jun 2022 22:19:27 +0300 Subject: [PATCH 01/60] Revert "On Wayland, fix resize not propagating properly" This reverts commit 78e5a395dac9604e851f781b6810247ead7928e3. It was discovered that in some cases mesa will lock the back buffer, e.g. when making context current, leading to resize missing. Given that applications can restructure their rendering to account for that, and that winit isn't limited to playing nice with mesa reverting the original commit. --- CHANGELOG.md | 1 - .../linux/wayland/event_loop/mod.rs | 30 +++++-------------- 2 files changed, 8 insertions(+), 23 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 099994d498..9cfed90221 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,7 +30,6 @@ And please only add new entries to the top of this list, right below the `# Unre - **Breaking:** Replaced `EventLoopExtUnix` with `EventLoopBuilderExtUnix` (which also has renamed methods). - **Breaking:** The platform specific extensions for Windows `winit::platform::windows` have changed. All `HANDLE`-like types e.g. `HWND` and `HMENU` were converted from winapi types or `*mut c_void` to `isize`. This was done to be consistent with the type definitions in windows-sys and to not expose internal dependencies. - The internal bindings to the [Windows API](https://docs.microsoft.com/en-us/windows/) were changed from the unofficial [winapi](https://github.com/retep998/winapi-rs) bindings to the official Microsoft [windows-sys](https://github.com/microsoft/windows-rs) bindings. -- On Wayland, fix resize and scale factor changes not being propagated properly. - On Wayland, fix polling during consecutive `EventLoop::run_return` invocations. - On Windows, fix race issue creating fullscreen windows with `WindowBuilder::with_fullscreen` - On Android, `virtual_keycode` for `KeyboardInput` events is now filled in where a suitable match is found. diff --git a/src/platform_impl/linux/wayland/event_loop/mod.rs b/src/platform_impl/linux/wayland/event_loop/mod.rs index 84dcb9fabb..0f879ea2d9 100644 --- a/src/platform_impl/linux/wayland/event_loop/mod.rs +++ b/src/platform_impl/linux/wayland/event_loop/mod.rs @@ -24,7 +24,7 @@ use crate::platform_impl::EventLoopWindowTarget as PlatformEventLoopWindowTarget use super::env::{WindowingFeatures, WinitEnv}; use super::output::OutputManager; use super::seat::SeatManager; -use super::window::shim::{self, WindowRequest, WindowUpdate}; +use super::window::shim::{self, WindowUpdate}; use super::{DeviceId, WindowId}; mod proxy; @@ -344,8 +344,7 @@ impl EventLoop { } // Process 'new' pending updates. - self.with_window_target(|window_target| { - let state = window_target.state.get_mut(); + self.with_state(|state| { window_updates.clear(); window_updates.extend( state @@ -357,8 +356,7 @@ impl EventLoop { for (window_id, window_update) in window_updates.iter_mut() { if let Some(scale_factor) = window_update.scale_factor.map(|f| f as f64) { - let mut physical_size = self.with_window_target(|window_target| { - let state = window_target.state.get_mut(); + let mut physical_size = self.with_state(|state| { let window_handle = state.window_map.get(window_id).unwrap(); let mut size = window_handle.size.lock().unwrap(); @@ -391,8 +389,7 @@ impl EventLoop { } if let Some(size) = window_update.size.take() { - let physical_size = self.with_window_target(|window_target| { - let state = window_target.state.get_mut(); + let physical_size = self.with_state(|state| { let window_handle = state.window_map.get_mut(window_id).unwrap(); let mut window_size = window_handle.size.lock().unwrap(); @@ -418,15 +415,6 @@ impl EventLoop { // Mark that refresh isn't required, since we've done it right now. window_update.refresh_frame = false; - // Queue request for redraw into the next iteration of the loop, since - // resize and scale factor changes are double buffered. - window_handle - .pending_window_requests - .lock() - .unwrap() - .push(WindowRequest::Redraw); - window_target.event_loop_awakener.ping(); - physical_size }); @@ -463,8 +451,7 @@ impl EventLoop { // The purpose of the back buffer and that swap is to not hold borrow_mut when // we're doing callback to the user, since we can double borrow if the user decides // to create a window in one of those callbacks. - self.with_window_target(|window_target| { - let state = window_target.state.get_mut(); + self.with_state(|state| { std::mem::swap( &mut event_sink_back_buffer, &mut state.event_sink.window_events, @@ -489,8 +476,7 @@ impl EventLoop { for (window_id, window_update) in window_updates.iter() { // Handle refresh of the frame. if window_update.refresh_frame { - self.with_window_target(|window_target| { - let state = window_target.state.get_mut(); + self.with_state(|state| { let window_handle = state.window_map.get_mut(window_id).unwrap(); window_handle.window.refresh(); if !window_update.redraw_requested { @@ -535,9 +521,9 @@ impl EventLoop { &self.window_target } - fn with_window_target) -> U>(&mut self, f: F) -> U { + fn with_state U>(&mut self, f: F) -> U { let state = match &mut self.window_target.p { - PlatformEventLoopWindowTarget::Wayland(window_target) => window_target, + PlatformEventLoopWindowTarget::Wayland(window_target) => window_target.state.get_mut(), #[cfg(feature = "x11")] _ => unreachable!(), }; From c7f718138818e82971426f8dec576a97db3821f0 Mon Sep 17 00:00:00 2001 From: Lucas Kent Date: Wed, 8 Jun 2022 06:55:57 +1000 Subject: [PATCH 02/60] Set `WindowBuilder` to must_use --- src/window.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/window.rs b/src/window.rs index 96189872f8..27184d4c76 100644 --- a/src/window.rs +++ b/src/window.rs @@ -85,6 +85,7 @@ impl WindowId { /// Object that allows building windows. #[derive(Clone, Default)] +#[must_use] pub struct WindowBuilder { /// The attributes to use to create the window. pub window: WindowAttributes, From f10a984ba3035e3b539d4b5b93937279bb5cc862 Mon Sep 17 00:00:00 2001 From: Christian Duerr Date: Tue, 7 Jun 2022 23:17:45 +0200 Subject: [PATCH 03/60] Add X11 opt-in function for device events Previously on X11, by default all global events were broadcasted to every winit application. This unnecessarily drains battery due to excessive CPU usage when moving the mouse. To resolve this, device events are now ignored by default and users must manually opt into it using `EventLoopWindowTarget::set_filter_device_events`. Fixes (#1634) on Linux. --- CHANGELOG.md | 1 + src/event_loop.rs | 39 ++++++++++ src/platform_impl/linux/mod.rs | 16 +++- .../linux/x11/event_processor.rs | 7 +- src/platform_impl/linux/x11/mod.rs | 77 ++++++++++++------- 5 files changed, 108 insertions(+), 32 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9cfed90221..84e6b0ba7f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -50,6 +50,7 @@ And please only add new entries to the top of this list, right below the `# Unre - Added `Window::set_ime_allowed` supported on desktop platforms. - **Breaking:** IME input on desktop platforms won't be received unless it's explicitly allowed via `Window::set_ime_allowed` and new `WindowEvent::Ime` events are handled. - On macOS, `WindowEvent::Resized` is now emitted in `frameDidChange` instead of `windowDidResize`. +- **Breaking:** On X11, device events are now ignored for unfocused windows by default, use `EventLoopWindowTarget::set_device_event_filter` to set the filter level. # 0.26.1 (2022-01-05) diff --git a/src/event_loop.rs b/src/event_loop.rs index d6a15cfa9a..58181b3f80 100644 --- a/src/event_loop.rs +++ b/src/event_loop.rs @@ -286,6 +286,28 @@ impl EventLoopWindowTarget { pub fn primary_monitor(&self) -> Option { self.p.primary_monitor() } + + /// Change [`DeviceEvent`] filter mode. + /// + /// Since the [`DeviceEvent`] capture can lead to high CPU usage for unfocused windows, winit + /// will ignore them by default for unfocused windows on Linux/BSD. This method allows changing + /// this filter at runtime to explicitly capture them again. + /// + /// ## Platform-specific + /// + /// - **Wayland / Windows / macOS / iOS / Android / Web**: Unsupported. + /// + /// [`DeviceEvent`]: crate::event::DeviceEvent + pub fn set_device_event_filter(&mut self, _filter: DeviceEventFilter) { + #[cfg(any( + target_os = "linux", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd" + ))] + self.p.set_device_event_filter(_filter); + } } /// Used to send custom events to `EventLoop`. @@ -330,3 +352,20 @@ impl fmt::Display for EventLoopClosed { } impl error::Error for EventLoopClosed {} + +/// Fiter controlling the propagation of device events. +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] +pub enum DeviceEventFilter { + /// Always filter out device events. + Always, + /// Filter out device events while the window is not focused. + Unfocused, + /// Report all device events regardless of window focus. + Never, +} + +impl Default for DeviceEventFilter { + fn default() -> Self { + Self::Unfocused + } +} diff --git a/src/platform_impl/linux/mod.rs b/src/platform_impl/linux/mod.rs index 6552a8227d..39511dd79e 100644 --- a/src/platform_impl/linux/mod.rs +++ b/src/platform_impl/linux/mod.rs @@ -30,7 +30,9 @@ use crate::{ dpi::{PhysicalPosition, PhysicalSize, Position, Size}, error::{ExternalError, NotSupportedError, OsError as RootOsError}, event::Event, - event_loop::{ControlFlow, EventLoopClosed, EventLoopWindowTarget as RootELW}, + event_loop::{ + ControlFlow, DeviceEventFilter, EventLoopClosed, EventLoopWindowTarget as RootELW, + }, icon::Icon, monitor::{MonitorHandle as RootMonitorHandle, VideoMode as RootVideoMode}, window::{CursorIcon, Fullscreen, UserAttentionType, WindowAttributes}, @@ -732,7 +734,7 @@ impl EventLoop { } pub fn window_target(&self) -> &crate::event_loop::EventLoopWindowTarget { - x11_or_wayland!(match self; EventLoop(evl) => evl.window_target()) + x11_or_wayland!(match self; EventLoop(evlp) => evlp.window_target()) } } @@ -793,6 +795,16 @@ impl EventLoopWindowTarget { } } } + + #[inline] + pub fn set_device_event_filter(&mut self, _filter: DeviceEventFilter) { + match *self { + #[cfg(feature = "wayland")] + EventLoopWindowTarget::Wayland(_) => (), + #[cfg(feature = "x11")] + EventLoopWindowTarget::X(ref mut evlp) => evlp.set_device_event_filter(_filter), + } + } } fn sticky_exit_callback( diff --git a/src/platform_impl/linux/x11/event_processor.rs b/src/platform_impl/linux/x11/event_processor.rs index 0db198bbab..8ec326526c 100644 --- a/src/platform_impl/linux/x11/event_processor.rs +++ b/src/platform_impl/linux/x11/event_processor.rs @@ -49,7 +49,7 @@ impl EventProcessor { let mut devices = self.devices.borrow_mut(); if let Some(info) = DeviceInfo::get(&wt.xconn, device) { for info in info.iter() { - devices.insert(DeviceId(info.deviceid), Device::new(self, info)); + devices.insert(DeviceId(info.deviceid), Device::new(info)); } } } @@ -907,6 +907,8 @@ impl EventProcessor { if self.active_window != Some(xev.event) { self.active_window = Some(xev.event); + wt.update_device_event_filter(true); + let window_id = mkwid(xev.event); let position = PhysicalPosition::new(xev.event_x, xev.event_y); @@ -956,6 +958,7 @@ impl EventProcessor { if !self.window_exists(xev.event) { return; } + wt.ime .borrow_mut() .unfocus(xev.event) @@ -964,6 +967,8 @@ impl EventProcessor { if self.active_window.take() == Some(xev.event) { let window_id = mkwid(xev.event); + wt.update_device_event_filter(false); + // Issue key release events for all pressed keys Self::handle_pressed_keys( wt, diff --git a/src/platform_impl/linux/x11/mod.rs b/src/platform_impl/linux/x11/mod.rs index 491e37d6d9..a38b9030ec 100644 --- a/src/platform_impl/linux/x11/mod.rs +++ b/src/platform_impl/linux/x11/mod.rs @@ -50,7 +50,9 @@ use self::{ use crate::{ error::OsError as RootOsError, event::{Event, StartCause}, - event_loop::{ControlFlow, EventLoopClosed, EventLoopWindowTarget as RootELW}, + event_loop::{ + ControlFlow, DeviceEventFilter, EventLoopClosed, EventLoopWindowTarget as RootELW, + }, platform_impl::{platform::sticky_exit_callback, PlatformSpecificWindowBuilderAttributes}, window::WindowAttributes, }; @@ -105,6 +107,7 @@ pub struct EventLoopWindowTarget { ime: RefCell, windows: RefCell>>, redraw_sender: WakeSender, + device_event_filter: DeviceEventFilter, _marker: ::std::marker::PhantomData, } @@ -229,21 +232,27 @@ impl EventLoop { let (user_sender, user_channel) = std::sync::mpsc::channel(); let (redraw_sender, redraw_channel) = std::sync::mpsc::channel(); + let window_target = EventLoopWindowTarget { + ime, + root, + windows: Default::default(), + _marker: ::std::marker::PhantomData, + ime_sender, + xconn, + wm_delete_window, + net_wm_ping, + redraw_sender: WakeSender { + sender: redraw_sender, // not used again so no clone + waker: waker.clone(), + }, + device_event_filter: Default::default(), + }; + + // Set initial device event filter. + window_target.update_device_event_filter(true); + let target = Rc::new(RootELW { - p: super::EventLoopWindowTarget::X(EventLoopWindowTarget { - ime, - root, - windows: Default::default(), - _marker: ::std::marker::PhantomData, - ime_sender, - xconn, - wm_delete_window, - net_wm_ping, - redraw_sender: WakeSender { - sender: redraw_sender, // not used again so no clone - waker: waker.clone(), - }, - }), + p: super::EventLoopWindowTarget::X(window_target), _marker: ::std::marker::PhantomData, }); @@ -524,6 +533,29 @@ impl EventLoopWindowTarget { pub fn x_connection(&self) -> &Arc { &self.xconn } + + pub fn set_device_event_filter(&mut self, filter: DeviceEventFilter) { + self.device_event_filter = filter; + } + + /// Update the device event filter based on window focus. + pub fn update_device_event_filter(&self, focus: bool) { + let filter_events = self.device_event_filter == DeviceEventFilter::Never + || (self.device_event_filter == DeviceEventFilter::Unfocused && !focus); + + let mut mask = 0; + if !filter_events { + mask = ffi::XI_RawMotionMask + | ffi::XI_RawButtonPressMask + | ffi::XI_RawButtonReleaseMask + | ffi::XI_RawKeyPressMask + | ffi::XI_RawKeyReleaseMask; + } + + self.xconn + .select_xinput_events(self.root, ffi::XIAllDevices, mask) + .queue(); + } } impl EventLoopProxy { @@ -698,24 +730,11 @@ enum ScrollOrientation { } impl Device { - fn new(el: &EventProcessor, info: &ffi::XIDeviceInfo) -> Self { + fn new(info: &ffi::XIDeviceInfo) -> Self { let name = unsafe { CStr::from_ptr(info.name).to_string_lossy() }; let mut scroll_axes = Vec::new(); - let wt = get_xtarget(&el.target); - if Device::physical_device(info) { - // Register for global raw events - let mask = ffi::XI_RawMotionMask - | ffi::XI_RawButtonPressMask - | ffi::XI_RawButtonReleaseMask - | ffi::XI_RawKeyPressMask - | ffi::XI_RawKeyReleaseMask; - // The request buffer is flushed when we poll for events - wt.xconn - .select_xinput_events(wt.root, info.deviceid, mask) - .queue(); - // Identify scroll axes for class_ptr in Device::classes(info) { let class = unsafe { &**class_ptr }; From 224872ce0345bf171cdb4a118b17e4364509d8f5 Mon Sep 17 00:00:00 2001 From: Kirill Chibisov Date: Wed, 8 Jun 2022 01:04:33 +0300 Subject: [PATCH 04/60] Prevent null dereference on X11 with bad locale --- src/platform_impl/linux/x11/ime/context.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/platform_impl/linux/x11/ime/context.rs b/src/platform_impl/linux/x11/ime/context.rs index 2fdfc6f320..f34d4bad32 100644 --- a/src/platform_impl/linux/x11/ime/context.rs +++ b/src/platform_impl/linux/x11/ime/context.rs @@ -103,7 +103,14 @@ extern "C" fn preedit_draw_callback( if xim_text.encoding_is_wchar > 0 { return; } - let new_text = unsafe { CStr::from_ptr(xim_text.string.multi_byte) }; + + let new_text = unsafe { xim_text.string.multi_byte }; + + if new_text.is_null() { + return; + } + + let new_text = unsafe { CStr::from_ptr(new_text) }; String::from(new_text.to_str().expect("Invalid UTF-8 String from IME")) .chars() From 4c39b3188cee29548761b57f38e4aa046cbe31ea Mon Sep 17 00:00:00 2001 From: Kevin King Date: Wed, 8 Jun 2022 08:22:54 -0700 Subject: [PATCH 05/60] Remove old dialog fix that is superseded by #2027 (#2292) This fixes the run_return loop never returning on macos when using multiple windows --- CHANGELOG.md | 1 + src/platform_impl/macos/app_state.rs | 37 ++-------------------- src/platform_impl/macos/window.rs | 5 +-- src/platform_impl/macos/window_delegate.rs | 10 +----- 4 files changed, 6 insertions(+), 47 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 84e6b0ba7f..8c3e879499 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ And please only add new entries to the top of this list, right below the `# Unre # Unreleased +- On macOS, fixed an issue where having multiple windows would prevent run_return from ever returning. - On Wayland, fix bug where the cursor wouldn't hide in GNOME. - On macOS, Windows, and Wayland, add `set_cursor_hittest` to let the window ignore mouse events. - On Windows, added `WindowExtWindows::set_skip_taskbar` and `WindowBuilderExtWindows::with_skip_taskbar`. diff --git a/src/platform_impl/macos/app_state.rs b/src/platform_impl/macos/app_state.rs index 7b392a4124..48c91cde1c 100644 --- a/src/platform_impl/macos/app_state.rs +++ b/src/platform_impl/macos/app_state.rs @@ -127,7 +127,6 @@ impl EventHandler for EventLoopHandler { struct Handler { ready: AtomicBool, in_callback: AtomicBool, - dialog_is_closing: AtomicBool, control_flow: Mutex, control_flow_prev: Mutex, start_time: Mutex>, @@ -262,8 +261,6 @@ impl Handler { } } -pub static INTERRUPT_EVENT_LOOP_EXIT: AtomicBool = AtomicBool::new(false); - pub enum AppState {} impl AppState { @@ -403,40 +400,12 @@ impl AppState { if HANDLER.should_exit() { unsafe { let app: id = NSApp(); - let windows: id = msg_send![app, windows]; - let window_count: usize = msg_send![windows, count]; - - let dialog_open = if window_count > 1 { - let dialog: id = msg_send![windows, lastObject]; - let is_main_window: BOOL = msg_send![dialog, isMainWindow]; - let is_visible: BOOL = msg_send![dialog, isVisible]; - is_visible != NO && is_main_window == NO - } else { - false - }; - let dialog_is_closing = HANDLER.dialog_is_closing.load(Ordering::SeqCst); autoreleasepool(|| { - if !INTERRUPT_EVENT_LOOP_EXIT.load(Ordering::SeqCst) - && !dialog_open - && !dialog_is_closing - { - let () = msg_send![app, stop: nil]; - // To stop event loop immediately, we need to post some event here. - post_dummy_event(app); - } + let () = msg_send![app, stop: nil]; + // To stop event loop immediately, we need to post some event here. + post_dummy_event(app); }); - - if window_count > 0 { - let window: id = msg_send![windows, firstObject]; - let window_has_focus: BOOL = msg_send![window, isKeyWindow]; - if !dialog_open && window_has_focus != NO && dialog_is_closing { - HANDLER.dialog_is_closing.store(false, Ordering::SeqCst); - } - if dialog_open { - HANDLER.dialog_is_closing.store(true, Ordering::SeqCst); - } - } }; } HANDLER.update_start_time(); diff --git a/src/platform_impl/macos/window.rs b/src/platform_impl/macos/window.rs index d4856cb91b..6526629ec5 100644 --- a/src/platform_impl/macos/window.rs +++ b/src/platform_impl/macos/window.rs @@ -20,7 +20,6 @@ use crate::{ platform::macos::WindowExtMacOS, platform_impl::platform::{ app_state::AppState, - app_state::INTERRUPT_EVENT_LOOP_EXIT, ffi, monitor::{self, MonitorHandle, VideoMode}, util::{self, IdRef}, @@ -907,8 +906,6 @@ impl UnownedWindow { let mut shared_state_lock = self.lock_shared_state("set_fullscreen"); shared_state_lock.fullscreen = fullscreen.clone(); - INTERRUPT_EVENT_LOOP_EXIT.store(true, Ordering::SeqCst); - match (&old_fullscreen, &fullscreen) { (&None, &Some(_)) => unsafe { util::toggle_full_screen_async( @@ -978,7 +975,7 @@ impl UnownedWindow { setLevel: ffi::NSWindowLevel::NSNormalWindowLevel ]; }, - _ => INTERRUPT_EVENT_LOOP_EXIT.store(false, Ordering::SeqCst), + _ => {} }; } diff --git a/src/platform_impl/macos/window_delegate.rs b/src/platform_impl/macos/window_delegate.rs index 01a8c5fa8c..bb3abd8450 100644 --- a/src/platform_impl/macos/window_delegate.rs +++ b/src/platform_impl/macos/window_delegate.rs @@ -1,7 +1,7 @@ use std::{ f64, os::raw::c_void, - sync::{atomic::Ordering, Arc, Weak}, + sync::{Arc, Weak}, }; use cocoa::{ @@ -20,7 +20,6 @@ use crate::{ event::{Event, ModifiersState, WindowEvent}, platform_impl::platform::{ app_state::AppState, - app_state::INTERRUPT_EVENT_LOOP_EXIT, event::{EventProxy, EventWrapper}, util::{self, IdRef}, view::ViewState, @@ -414,8 +413,6 @@ extern "C" fn dragging_exited(this: &Object, _: Sel, _: id) { extern "C" fn window_will_enter_fullscreen(this: &Object, _: Sel, _: id) { trace_scope!("windowWillEnterFullscreen:"); - INTERRUPT_EVENT_LOOP_EXIT.store(true, Ordering::SeqCst); - with_state(this, |state| { state.with_window(|window| { let mut shared_state = window.lock_shared_state("window_will_enter_fullscreen"); @@ -445,8 +442,6 @@ extern "C" fn window_will_enter_fullscreen(this: &Object, _: Sel, _: id) { extern "C" fn window_will_exit_fullscreen(this: &Object, _: Sel, _: id) { trace_scope!("windowWillExitFullScreen:"); - INTERRUPT_EVENT_LOOP_EXIT.store(true, Ordering::SeqCst); - with_state(this, |state| { state.with_window(|window| { let mut shared_state = window.lock_shared_state("window_will_exit_fullscreen"); @@ -490,8 +485,6 @@ extern "C" fn window_will_use_fullscreen_presentation_options( /// Invoked when entered fullscreen extern "C" fn window_did_enter_fullscreen(this: &Object, _: Sel, _: id) { trace_scope!("windowDidEnterFullscreen:"); - INTERRUPT_EVENT_LOOP_EXIT.store(false, Ordering::SeqCst); - with_state(this, |state| { state.initial_fullscreen = false; state.with_window(|window| { @@ -509,7 +502,6 @@ extern "C" fn window_did_enter_fullscreen(this: &Object, _: Sel, _: id) { /// Invoked when exited fullscreen extern "C" fn window_did_exit_fullscreen(this: &Object, _: Sel, _: id) { trace_scope!("windowDidExitFullscreen:"); - INTERRUPT_EVENT_LOOP_EXIT.store(false, Ordering::SeqCst); with_state(this, |state| { state.with_window(|window| { From 2c01e9e747fb524d2ef15f3269538bbdeb228761 Mon Sep 17 00:00:00 2001 From: James Liu Date: Wed, 8 Jun 2022 11:50:26 -0700 Subject: [PATCH 06/60] Migrate from lazy_static to once_cell --- Cargo.toml | 2 +- src/lib.rs | 3 - src/platform_impl/android/mod.rs | 47 +-- src/platform_impl/ios/app_state.rs | 45 ++- src/platform_impl/linux/mod.rs | 8 +- .../linux/x11/ime/input_method.rs | 5 +- src/platform_impl/linux/x11/monitor.rs | 5 +- src/platform_impl/linux/x11/util/atom.rs | 5 +- src/platform_impl/linux/x11/util/wm.rs | 8 +- src/platform_impl/macos/app.rs | 21 +- src/platform_impl/macos/app_delegate.rs | 38 +- src/platform_impl/macos/app_state.rs | 5 +- src/platform_impl/macos/view.rs | 333 +++++++++--------- src/platform_impl/macos/window.rs | 45 ++- src/platform_impl/macos/window_delegate.rs | 181 +++++----- src/platform_impl/windows/dark_mode.rs | 103 +++--- src/platform_impl/windows/event_loop.rs | 129 +++---- src/platform_impl/windows/util.rs | 31 +- 18 files changed, 486 insertions(+), 528 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 3f5acfb21d..764da52e81 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,7 +26,7 @@ wayland-csd-adwaita-notitle = ["sctk-adwaita"] [dependencies] instant = { version = "0.1", features = ["wasm-bindgen"] } -lazy_static = "1" +once_cell = "1.12" log = "0.4" serde = { version = "1", optional = true, features = ["serde_derive"] } raw-window-handle = "0.4.2" diff --git a/src/lib.rs b/src/lib.rs index a74586f850..4ad8f806fc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -133,9 +133,6 @@ #![deny(rust_2018_idioms)] #![deny(rustdoc::broken_intra_doc_links)] -#[allow(unused_imports)] -#[macro_use] -extern crate lazy_static; #[allow(unused_imports)] #[macro_use] extern crate log; diff --git a/src/platform_impl/android/mod.rs b/src/platform_impl/android/mod.rs index 27d9984b1d..b67f47eb12 100644 --- a/src/platform_impl/android/mod.rs +++ b/src/platform_impl/android/mod.rs @@ -1,38 +1,41 @@ #![cfg(target_os = "android")] -use crate::{ - dpi::{PhysicalPosition, PhysicalSize, Position, Size}, - error, - event::{self, VirtualKeyCode}, - event_loop::{self, ControlFlow}, - monitor, window, +use std::{ + collections::VecDeque, + sync::{Arc, Mutex, RwLock}, + time::{Duration, Instant}, }; + use ndk::{ configuration::Configuration, event::{InputEvent, KeyAction, Keycode, MotionAction}, looper::{ForeignLooper, Poll, ThreadLooper}, }; use ndk_glue::{Event, Rect}; +use once_cell::sync::Lazy; use raw_window_handle::{AndroidNdkHandle, RawWindowHandle}; -use std::{ - collections::VecDeque, - sync::{Arc, Mutex, RwLock}, - time::{Duration, Instant}, + +use crate::{ + dpi::{PhysicalPosition, PhysicalSize, Position, Size}, + error, + event::{self, VirtualKeyCode}, + event_loop::{self, ControlFlow}, + monitor, window, }; -lazy_static! { - static ref CONFIG: RwLock = RwLock::new(Configuration::from_asset_manager( +static CONFIG: Lazy> = Lazy::new(|| { + RwLock::new(Configuration::from_asset_manager( #[allow(deprecated)] // TODO: rust-windowing/winit#2196 - &ndk_glue::native_activity().asset_manager() - )); - // If this is `Some()` a `Poll::Wake` is considered an `EventSource::Internal` with the event - // contained in the `Option`. The event is moved outside of the `Option` replacing it with a - // `None`. - // - // This allows us to inject event into the event loop without going through `ndk-glue` and - // calling unsafe function that should only be called by Android. - static ref INTERNAL_EVENT: RwLock> = RwLock::new(None); -} + &ndk_glue::native_activity().asset_manager(), + )) +}); +// If this is `Some()` a `Poll::Wake` is considered an `EventSource::Internal` with the event +// contained in the `Option`. The event is moved outside of the `Option` replacing it with a +// `None`. +// +// This allows us to inject event into the event loop without going through `ndk-glue` and +// calling unsafe function that should only be called by Android. +static INTERNAL_EVENT: Lazy>> = Lazy::new(|| RwLock::new(None)); enum InternalEvent { RedrawRequested, diff --git a/src/platform_impl/ios/app_state.rs b/src/platform_impl/ios/app_state.rs index 6d1d206942..c21ceff53b 100644 --- a/src/platform_impl/ios/app_state.rs +++ b/src/platform_impl/ios/app_state.rs @@ -10,6 +10,7 @@ use std::{ }; use objc::runtime::{BOOL, YES}; +use once_cell::sync::Lazy; use crate::{ dpi::LogicalSize, @@ -1016,29 +1017,27 @@ impl NSOperatingSystemVersion { } pub fn os_capabilities() -> OSCapabilities { - lazy_static! { - static ref OS_CAPABILITIES: OSCapabilities = { - let version: NSOperatingSystemVersion = unsafe { - let process_info: id = msg_send![class!(NSProcessInfo), processInfo]; - let atleast_ios_8: BOOL = msg_send![ - process_info, - respondsToSelector: sel!(operatingSystemVersion) - ]; - // winit requires atleast iOS 8 because no one has put the time into supporting earlier os versions. - // Older iOS versions are increasingly difficult to test. For example, Xcode 11 does not support - // debugging on devices with an iOS version of less than 8. Another example, in order to use an iOS - // simulator older than iOS 8, you must download an older version of Xcode (<9), and at least Xcode 7 - // has been tested to not even run on macOS 10.15 - Xcode 8 might? - // - // The minimum required iOS version is likely to grow in the future. - assert!( - atleast_ios_8 == YES, - "`winit` requires iOS version 8 or greater" - ); - msg_send![process_info, operatingSystemVersion] - }; - version.into() + static OS_CAPABILITIES: Lazy = Lazy::new(|| { + let version: NSOperatingSystemVersion = unsafe { + let process_info: id = msg_send![class!(NSProcessInfo), processInfo]; + let atleast_ios_8: BOOL = msg_send![ + process_info, + respondsToSelector: sel!(operatingSystemVersion) + ]; + // winit requires atleast iOS 8 because no one has put the time into supporting earlier os versions. + // Older iOS versions are increasingly difficult to test. For example, Xcode 11 does not support + // debugging on devices with an iOS version of less than 8. Another example, in order to use an iOS + // simulator older than iOS 8, you must download an older version of Xcode (<9), and at least Xcode 7 + // has been tested to not even run on macOS 10.15 - Xcode 8 might? + // + // The minimum required iOS version is likely to grow in the future. + assert!( + atleast_ios_8 == YES, + "`winit` requires iOS version 8 or greater" + ); + msg_send![process_info, operatingSystemVersion] }; - } + version.into() + }); OS_CAPABILITIES.clone() } diff --git a/src/platform_impl/linux/mod.rs b/src/platform_impl/linux/mod.rs index 39511dd79e..4e5ac66293 100644 --- a/src/platform_impl/linux/mod.rs +++ b/src/platform_impl/linux/mod.rs @@ -18,6 +18,8 @@ use std::{collections::VecDeque, env, fmt}; #[cfg(feature = "x11")] use std::{ffi::CStr, mem::MaybeUninit, os::raw::*, sync::Arc}; +#[cfg(feature = "x11")] +use once_cell::sync::Lazy; #[cfg(feature = "x11")] use parking_lot::Mutex; use raw_window_handle::RawWindowHandle; @@ -135,10 +137,8 @@ impl Default for PlatformSpecificWindowBuilderAttributes { } #[cfg(feature = "x11")] -lazy_static! { - pub static ref X11_BACKEND: Mutex, XNotSupported>> = - Mutex::new(XConnection::new(Some(x_error_callback)).map(Arc::new)); -} +pub static X11_BACKEND: Lazy, XNotSupported>>> = + Lazy::new(|| Mutex::new(XConnection::new(Some(x_error_callback)).map(Arc::new))); #[derive(Debug, Clone)] pub enum OsError { diff --git a/src/platform_impl/linux/x11/ime/input_method.rs b/src/platform_impl/linux/x11/ime/input_method.rs index e552f55635..5a1695cb0e 100644 --- a/src/platform_impl/linux/x11/ime/input_method.rs +++ b/src/platform_impl/linux/x11/ime/input_method.rs @@ -7,13 +7,12 @@ use std::{ sync::Arc, }; +use once_cell::sync::Lazy; use parking_lot::Mutex; use super::{ffi, util, XConnection, XError}; -lazy_static! { - static ref GLOBAL_LOCK: Mutex<()> = Default::default(); -} +static GLOBAL_LOCK: Lazy> = Lazy::new(Default::default); unsafe fn open_im(xconn: &Arc, locale_modifiers: &CStr) -> Option { let _lock = GLOBAL_LOCK.lock(); diff --git a/src/platform_impl/linux/x11/monitor.rs b/src/platform_impl/linux/x11/monitor.rs index 2bd0ab17fb..8249d83caf 100644 --- a/src/platform_impl/linux/x11/monitor.rs +++ b/src/platform_impl/linux/x11/monitor.rs @@ -1,5 +1,6 @@ use std::os::raw::*; +use once_cell::sync::Lazy; use parking_lot::Mutex; use super::{ @@ -18,9 +19,7 @@ use crate::{ // Used for testing. This should always be committed as false. const DISABLE_MONITOR_LIST_CACHING: bool = false; -lazy_static! { - static ref MONITORS: Mutex>> = Mutex::default(); -} +static MONITORS: Lazy>>> = Lazy::new(Mutex::default); pub fn invalidate_cached_monitor_list() -> Option> { // We update this lazily. diff --git a/src/platform_impl/linux/x11/util/atom.rs b/src/platform_impl/linux/x11/util/atom.rs index 4138722483..5bfa386a49 100644 --- a/src/platform_impl/linux/x11/util/atom.rs +++ b/src/platform_impl/linux/x11/util/atom.rs @@ -5,15 +5,14 @@ use std::{ os::raw::*, }; +use once_cell::sync::Lazy; use parking_lot::Mutex; use super::*; type AtomCache = HashMap; -lazy_static! { - static ref ATOM_CACHE: Mutex = Mutex::new(HashMap::with_capacity(2048)); -} +static ATOM_CACHE: Lazy> = Lazy::new(|| Mutex::new(HashMap::with_capacity(2048))); impl XConnection { pub fn get_atom + Debug>(&self, name: T) -> ffi::Atom { diff --git a/src/platform_impl/linux/x11/util/wm.rs b/src/platform_impl/linux/x11/util/wm.rs index 6fef5a3c4d..21fa790d73 100644 --- a/src/platform_impl/linux/x11/util/wm.rs +++ b/src/platform_impl/linux/x11/util/wm.rs @@ -1,12 +1,12 @@ +use once_cell::sync::Lazy; use parking_lot::Mutex; use super::*; // This info is global to the window manager. -lazy_static! { - static ref SUPPORTED_HINTS: Mutex> = Mutex::new(Vec::with_capacity(0)); - static ref WM_NAME: Mutex> = Mutex::new(None); -} +static SUPPORTED_HINTS: Lazy>> = + Lazy::new(|| Mutex::new(Vec::with_capacity(0))); +static WM_NAME: Lazy>> = Lazy::new(|| Mutex::new(None)); pub fn hint_is_supported(hint: ffi::Atom) -> bool { (*SUPPORTED_HINTS.lock()).contains(&hint) diff --git a/src/platform_impl/macos/app.rs b/src/platform_impl/macos/app.rs index b5a5582cc3..a4a6ef0b4d 100644 --- a/src/platform_impl/macos/app.rs +++ b/src/platform_impl/macos/app.rs @@ -8,6 +8,7 @@ use objc::{ declare::ClassDecl, runtime::{Class, Object, Sel}, }; +use once_cell::sync::Lazy; use super::{app_state::AppState, event::EventWrapper, util, DEVICE_ID}; use crate::event::{DeviceEvent, ElementState, Event}; @@ -16,19 +17,17 @@ pub struct AppClass(pub *const Class); unsafe impl Send for AppClass {} unsafe impl Sync for AppClass {} -lazy_static! { - pub static ref APP_CLASS: AppClass = unsafe { - let superclass = class!(NSApplication); - let mut decl = ClassDecl::new("WinitApp", superclass).unwrap(); +pub static APP_CLASS: Lazy = Lazy::new(|| unsafe { + let superclass = class!(NSApplication); + let mut decl = ClassDecl::new("WinitApp", superclass).unwrap(); - decl.add_method( - sel!(sendEvent:), - send_event as extern "C" fn(&Object, Sel, id), - ); + decl.add_method( + sel!(sendEvent:), + send_event as extern "C" fn(&Object, Sel, id), + ); - AppClass(decl.register()) - }; -} + AppClass(decl.register()) +}); // Normally, holding Cmd + any key never sends us a `keyUp` event for that key. // Overriding `sendEvent:` like this fixes that. (https://stackoverflow.com/a/15294196) diff --git a/src/platform_impl/macos/app_delegate.rs b/src/platform_impl/macos/app_delegate.rs index 940a5538e6..5ff8be2fb0 100644 --- a/src/platform_impl/macos/app_delegate.rs +++ b/src/platform_impl/macos/app_delegate.rs @@ -1,14 +1,16 @@ -use crate::{platform::macos::ActivationPolicy, platform_impl::platform::app_state::AppState}; +use std::{ + cell::{RefCell, RefMut}, + os::raw::c_void, +}; use cocoa::base::id; use objc::{ declare::ClassDecl, runtime::{Class, Object, Sel}, }; -use std::{ - cell::{RefCell, RefMut}, - os::raw::c_void, -}; +use once_cell::sync::Lazy; + +use crate::{platform::macos::ActivationPolicy, platform_impl::platform::app_state::AppState}; static AUX_DELEGATE_STATE_NAME: &str = "auxState"; @@ -21,23 +23,21 @@ pub struct AppDelegateClass(pub *const Class); unsafe impl Send for AppDelegateClass {} unsafe impl Sync for AppDelegateClass {} -lazy_static! { - pub static ref APP_DELEGATE_CLASS: AppDelegateClass = unsafe { - let superclass = class!(NSResponder); - let mut decl = ClassDecl::new("WinitAppDelegate", superclass).unwrap(); +pub static APP_DELEGATE_CLASS: Lazy = Lazy::new(|| unsafe { + let superclass = class!(NSResponder); + let mut decl = ClassDecl::new("WinitAppDelegate", superclass).unwrap(); - decl.add_class_method(sel!(new), new as extern "C" fn(&Class, Sel) -> id); - decl.add_method(sel!(dealloc), dealloc as extern "C" fn(&Object, Sel)); + decl.add_class_method(sel!(new), new as extern "C" fn(&Class, Sel) -> id); + decl.add_method(sel!(dealloc), dealloc as extern "C" fn(&Object, Sel)); - decl.add_method( - sel!(applicationDidFinishLaunching:), - did_finish_launching as extern "C" fn(&Object, Sel, id), - ); - decl.add_ivar::<*mut c_void>(AUX_DELEGATE_STATE_NAME); + decl.add_method( + sel!(applicationDidFinishLaunching:), + did_finish_launching as extern "C" fn(&Object, Sel, id), + ); + decl.add_ivar::<*mut c_void>(AUX_DELEGATE_STATE_NAME); - AppDelegateClass(decl.register()) - }; -} + AppDelegateClass(decl.register()) +}); /// Safety: Assumes that Object is an instance of APP_DELEGATE_CLASS pub unsafe fn get_aux_state_mut(this: &Object) -> RefMut<'_, AuxDelegateState> { diff --git a/src/platform_impl/macos/app_state.rs b/src/platform_impl/macos/app_state.rs index 48c91cde1c..12b78e332e 100644 --- a/src/platform_impl/macos/app_state.rs +++ b/src/platform_impl/macos/app_state.rs @@ -21,6 +21,7 @@ use objc::{ rc::autoreleasepool, runtime::{Object, BOOL, NO, YES}, }; +use once_cell::sync::Lazy; use crate::{ dpi::LogicalSize, @@ -41,9 +42,7 @@ use crate::{ window::WindowId, }; -lazy_static! { - static ref HANDLER: Handler = Default::default(); -} +static HANDLER: Lazy = Lazy::new(Default::default); impl<'a, Never> Event<'a, Never> { fn userify(self) -> Event<'a, T> { diff --git a/src/platform_impl/macos/view.rs b/src/platform_impl/macos/view.rs index 302282119b..94e3c73863 100644 --- a/src/platform_impl/macos/view.rs +++ b/src/platform_impl/macos/view.rs @@ -18,6 +18,7 @@ use objc::{ declare::ClassDecl, runtime::{Class, Object, Protocol, Sel, BOOL, NO, YES}, }; +use once_cell::sync::Lazy; use crate::{ dpi::{LogicalPosition, LogicalSize}, @@ -155,173 +156,171 @@ struct ViewClass(*const Class); unsafe impl Send for ViewClass {} unsafe impl Sync for ViewClass {} -lazy_static! { - static ref VIEW_CLASS: ViewClass = unsafe { - let superclass = class!(NSView); - let mut decl = ClassDecl::new("WinitView", superclass).unwrap(); - decl.add_method(sel!(dealloc), dealloc as extern "C" fn(&Object, Sel)); - decl.add_method( - sel!(initWithWinit:), - init_with_winit as extern "C" fn(&Object, Sel, *mut c_void) -> id, - ); - decl.add_method( - sel!(viewDidMoveToWindow), - view_did_move_to_window as extern "C" fn(&Object, Sel), - ); - decl.add_method( - sel!(drawRect:), - draw_rect as extern "C" fn(&Object, Sel, NSRect), - ); - decl.add_method( - sel!(acceptsFirstResponder), - accepts_first_responder as extern "C" fn(&Object, Sel) -> BOOL, - ); - decl.add_method( - sel!(touchBar), - touch_bar as extern "C" fn(&Object, Sel) -> BOOL, - ); - decl.add_method( - sel!(resetCursorRects), - reset_cursor_rects as extern "C" fn(&Object, Sel), - ); - - // ------------------------------------------------------------------ - // NSTextInputClient - decl.add_method( - sel!(hasMarkedText), - has_marked_text as extern "C" fn(&Object, Sel) -> BOOL, - ); - decl.add_method( - sel!(markedRange), - marked_range as extern "C" fn(&Object, Sel) -> NSRange, - ); - decl.add_method( - sel!(selectedRange), - selected_range as extern "C" fn(&Object, Sel) -> NSRange, - ); - decl.add_method( - sel!(setMarkedText:selectedRange:replacementRange:), - set_marked_text as extern "C" fn(&mut Object, Sel, id, NSRange, NSRange), - ); - decl.add_method(sel!(unmarkText), unmark_text as extern "C" fn(&Object, Sel)); - decl.add_method( - sel!(validAttributesForMarkedText), - valid_attributes_for_marked_text as extern "C" fn(&Object, Sel) -> id, - ); - decl.add_method( - sel!(attributedSubstringForProposedRange:actualRange:), - attributed_substring_for_proposed_range - as extern "C" fn(&Object, Sel, NSRange, *mut c_void) -> id, - ); - decl.add_method( - sel!(insertText:replacementRange:), - insert_text as extern "C" fn(&Object, Sel, id, NSRange), - ); - decl.add_method( - sel!(characterIndexForPoint:), - character_index_for_point as extern "C" fn(&Object, Sel, NSPoint) -> NSUInteger, - ); - decl.add_method( - sel!(firstRectForCharacterRange:actualRange:), - first_rect_for_character_range - as extern "C" fn(&Object, Sel, NSRange, *mut c_void) -> NSRect, - ); - decl.add_method( - sel!(doCommandBySelector:), - do_command_by_selector as extern "C" fn(&Object, Sel, Sel), - ); - // ------------------------------------------------------------------ - - decl.add_method(sel!(keyDown:), key_down as extern "C" fn(&Object, Sel, id)); - decl.add_method(sel!(keyUp:), key_up as extern "C" fn(&Object, Sel, id)); - decl.add_method( - sel!(flagsChanged:), - flags_changed as extern "C" fn(&Object, Sel, id), - ); - decl.add_method( - sel!(insertTab:), - insert_tab as extern "C" fn(&Object, Sel, id), - ); - decl.add_method( - sel!(insertBackTab:), - insert_back_tab as extern "C" fn(&Object, Sel, id), - ); - decl.add_method( - sel!(mouseDown:), - mouse_down as extern "C" fn(&Object, Sel, id), - ); - decl.add_method(sel!(mouseUp:), mouse_up as extern "C" fn(&Object, Sel, id)); - decl.add_method( - sel!(rightMouseDown:), - right_mouse_down as extern "C" fn(&Object, Sel, id), - ); - decl.add_method( - sel!(rightMouseUp:), - right_mouse_up as extern "C" fn(&Object, Sel, id), - ); - decl.add_method( - sel!(otherMouseDown:), - other_mouse_down as extern "C" fn(&Object, Sel, id), - ); - decl.add_method( - sel!(otherMouseUp:), - other_mouse_up as extern "C" fn(&Object, Sel, id), - ); - decl.add_method( - sel!(mouseMoved:), - mouse_moved as extern "C" fn(&Object, Sel, id), - ); - decl.add_method( - sel!(mouseDragged:), - mouse_dragged as extern "C" fn(&Object, Sel, id), - ); - decl.add_method( - sel!(rightMouseDragged:), - right_mouse_dragged as extern "C" fn(&Object, Sel, id), - ); - decl.add_method( - sel!(otherMouseDragged:), - other_mouse_dragged as extern "C" fn(&Object, Sel, id), - ); - decl.add_method( - sel!(mouseEntered:), - mouse_entered as extern "C" fn(&Object, Sel, id), - ); - decl.add_method( - sel!(mouseExited:), - mouse_exited as extern "C" fn(&Object, Sel, id), - ); - decl.add_method( - sel!(scrollWheel:), - scroll_wheel as extern "C" fn(&Object, Sel, id), - ); - decl.add_method( - sel!(pressureChangeWithEvent:), - pressure_change_with_event as extern "C" fn(&Object, Sel, id), - ); - decl.add_method( - sel!(_wantsKeyDownForEvent:), - wants_key_down_for_event as extern "C" fn(&Object, Sel, id) -> BOOL, - ); - decl.add_method( - sel!(cancelOperation:), - cancel_operation as extern "C" fn(&Object, Sel, id), - ); - decl.add_method( - sel!(frameDidChange:), - frame_did_change as extern "C" fn(&Object, Sel, id), - ); - decl.add_method( - sel!(acceptsFirstMouse:), - accepts_first_mouse as extern "C" fn(&Object, Sel, id) -> BOOL, - ); - decl.add_ivar::<*mut c_void>("winitState"); - decl.add_ivar::("markedText"); - let protocol = Protocol::get("NSTextInputClient").unwrap(); - decl.add_protocol(protocol); - ViewClass(decl.register()) - }; -} +static VIEW_CLASS: Lazy = Lazy::new(|| unsafe { + let superclass = class!(NSView); + let mut decl = ClassDecl::new("WinitView", superclass).unwrap(); + decl.add_method(sel!(dealloc), dealloc as extern "C" fn(&Object, Sel)); + decl.add_method( + sel!(initWithWinit:), + init_with_winit as extern "C" fn(&Object, Sel, *mut c_void) -> id, + ); + decl.add_method( + sel!(viewDidMoveToWindow), + view_did_move_to_window as extern "C" fn(&Object, Sel), + ); + decl.add_method( + sel!(drawRect:), + draw_rect as extern "C" fn(&Object, Sel, NSRect), + ); + decl.add_method( + sel!(acceptsFirstResponder), + accepts_first_responder as extern "C" fn(&Object, Sel) -> BOOL, + ); + decl.add_method( + sel!(touchBar), + touch_bar as extern "C" fn(&Object, Sel) -> BOOL, + ); + decl.add_method( + sel!(resetCursorRects), + reset_cursor_rects as extern "C" fn(&Object, Sel), + ); + + // ------------------------------------------------------------------ + // NSTextInputClient + decl.add_method( + sel!(hasMarkedText), + has_marked_text as extern "C" fn(&Object, Sel) -> BOOL, + ); + decl.add_method( + sel!(markedRange), + marked_range as extern "C" fn(&Object, Sel) -> NSRange, + ); + decl.add_method( + sel!(selectedRange), + selected_range as extern "C" fn(&Object, Sel) -> NSRange, + ); + decl.add_method( + sel!(setMarkedText:selectedRange:replacementRange:), + set_marked_text as extern "C" fn(&mut Object, Sel, id, NSRange, NSRange), + ); + decl.add_method(sel!(unmarkText), unmark_text as extern "C" fn(&Object, Sel)); + decl.add_method( + sel!(validAttributesForMarkedText), + valid_attributes_for_marked_text as extern "C" fn(&Object, Sel) -> id, + ); + decl.add_method( + sel!(attributedSubstringForProposedRange:actualRange:), + attributed_substring_for_proposed_range + as extern "C" fn(&Object, Sel, NSRange, *mut c_void) -> id, + ); + decl.add_method( + sel!(insertText:replacementRange:), + insert_text as extern "C" fn(&Object, Sel, id, NSRange), + ); + decl.add_method( + sel!(characterIndexForPoint:), + character_index_for_point as extern "C" fn(&Object, Sel, NSPoint) -> NSUInteger, + ); + decl.add_method( + sel!(firstRectForCharacterRange:actualRange:), + first_rect_for_character_range + as extern "C" fn(&Object, Sel, NSRange, *mut c_void) -> NSRect, + ); + decl.add_method( + sel!(doCommandBySelector:), + do_command_by_selector as extern "C" fn(&Object, Sel, Sel), + ); + // ------------------------------------------------------------------ + + decl.add_method(sel!(keyDown:), key_down as extern "C" fn(&Object, Sel, id)); + decl.add_method(sel!(keyUp:), key_up as extern "C" fn(&Object, Sel, id)); + decl.add_method( + sel!(flagsChanged:), + flags_changed as extern "C" fn(&Object, Sel, id), + ); + decl.add_method( + sel!(insertTab:), + insert_tab as extern "C" fn(&Object, Sel, id), + ); + decl.add_method( + sel!(insertBackTab:), + insert_back_tab as extern "C" fn(&Object, Sel, id), + ); + decl.add_method( + sel!(mouseDown:), + mouse_down as extern "C" fn(&Object, Sel, id), + ); + decl.add_method(sel!(mouseUp:), mouse_up as extern "C" fn(&Object, Sel, id)); + decl.add_method( + sel!(rightMouseDown:), + right_mouse_down as extern "C" fn(&Object, Sel, id), + ); + decl.add_method( + sel!(rightMouseUp:), + right_mouse_up as extern "C" fn(&Object, Sel, id), + ); + decl.add_method( + sel!(otherMouseDown:), + other_mouse_down as extern "C" fn(&Object, Sel, id), + ); + decl.add_method( + sel!(otherMouseUp:), + other_mouse_up as extern "C" fn(&Object, Sel, id), + ); + decl.add_method( + sel!(mouseMoved:), + mouse_moved as extern "C" fn(&Object, Sel, id), + ); + decl.add_method( + sel!(mouseDragged:), + mouse_dragged as extern "C" fn(&Object, Sel, id), + ); + decl.add_method( + sel!(rightMouseDragged:), + right_mouse_dragged as extern "C" fn(&Object, Sel, id), + ); + decl.add_method( + sel!(otherMouseDragged:), + other_mouse_dragged as extern "C" fn(&Object, Sel, id), + ); + decl.add_method( + sel!(mouseEntered:), + mouse_entered as extern "C" fn(&Object, Sel, id), + ); + decl.add_method( + sel!(mouseExited:), + mouse_exited as extern "C" fn(&Object, Sel, id), + ); + decl.add_method( + sel!(scrollWheel:), + scroll_wheel as extern "C" fn(&Object, Sel, id), + ); + decl.add_method( + sel!(pressureChangeWithEvent:), + pressure_change_with_event as extern "C" fn(&Object, Sel, id), + ); + decl.add_method( + sel!(_wantsKeyDownForEvent:), + wants_key_down_for_event as extern "C" fn(&Object, Sel, id) -> BOOL, + ); + decl.add_method( + sel!(cancelOperation:), + cancel_operation as extern "C" fn(&Object, Sel, id), + ); + decl.add_method( + sel!(frameDidChange:), + frame_did_change as extern "C" fn(&Object, Sel, id), + ); + decl.add_method( + sel!(acceptsFirstMouse:), + accepts_first_mouse as extern "C" fn(&Object, Sel, id) -> BOOL, + ); + decl.add_ivar::<*mut c_void>("winitState"); + decl.add_ivar::("markedText"); + let protocol = Protocol::get("NSTextInputClient").unwrap(); + decl.add_protocol(protocol); + ViewClass(decl.register()) +}); extern "C" fn dealloc(this: &Object, _sel: Sel) { unsafe { diff --git a/src/platform_impl/macos/window.rs b/src/platform_impl/macos/window.rs index 6526629ec5..cd8ac3e695 100644 --- a/src/platform_impl/macos/window.rs +++ b/src/platform_impl/macos/window.rs @@ -46,6 +46,7 @@ use objc::{ rc::autoreleasepool, runtime::{Class, Object, Sel, BOOL, NO, YES}, }; +use once_cell::sync::Lazy; #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct WindowId(pub usize); @@ -246,32 +247,30 @@ struct WindowClass(*const Class); unsafe impl Send for WindowClass {} unsafe impl Sync for WindowClass {} -lazy_static! { - static ref WINDOW_CLASS: WindowClass = unsafe { - let window_superclass = class!(NSWindow); - let mut decl = ClassDecl::new("WinitWindow", window_superclass).unwrap(); +static WINDOW_CLASS: Lazy = Lazy::new(|| unsafe { + let window_superclass = class!(NSWindow); + let mut decl = ClassDecl::new("WinitWindow", window_superclass).unwrap(); - pub extern "C" fn can_become_main_window(_: &Object, _: Sel) -> BOOL { - trace_scope!("canBecomeMainWindow"); - YES - } + pub extern "C" fn can_become_main_window(_: &Object, _: Sel) -> BOOL { + trace_scope!("canBecomeMainWindow"); + YES + } - pub extern "C" fn can_become_key_window(_: &Object, _: Sel) -> BOOL { - trace_scope!("canBecomeKeyWindow"); - YES - } + pub extern "C" fn can_become_key_window(_: &Object, _: Sel) -> BOOL { + trace_scope!("canBecomeKeyWindow"); + YES + } - decl.add_method( - sel!(canBecomeMainWindow), - can_become_main_window as extern "C" fn(&Object, Sel) -> BOOL, - ); - decl.add_method( - sel!(canBecomeKeyWindow), - can_become_key_window as extern "C" fn(&Object, Sel) -> BOOL, - ); - WindowClass(decl.register()) - }; -} + decl.add_method( + sel!(canBecomeMainWindow), + can_become_main_window as extern "C" fn(&Object, Sel) -> BOOL, + ); + decl.add_method( + sel!(canBecomeKeyWindow), + can_become_key_window as extern "C" fn(&Object, Sel) -> BOOL, + ); + WindowClass(decl.register()) +}); #[derive(Default)] pub struct SharedState { diff --git a/src/platform_impl/macos/window_delegate.rs b/src/platform_impl/macos/window_delegate.rs index bb3abd8450..08e78e094e 100644 --- a/src/platform_impl/macos/window_delegate.rs +++ b/src/platform_impl/macos/window_delegate.rs @@ -14,6 +14,7 @@ use objc::{ rc::autoreleasepool, runtime::{Class, Object, Sel, BOOL, NO, YES}, }; +use once_cell::sync::Lazy; use crate::{ dpi::{LogicalPosition, LogicalSize}, @@ -134,97 +135,95 @@ struct WindowDelegateClass(*const Class); unsafe impl Send for WindowDelegateClass {} unsafe impl Sync for WindowDelegateClass {} -lazy_static! { - static ref WINDOW_DELEGATE_CLASS: WindowDelegateClass = unsafe { - let superclass = class!(NSResponder); - let mut decl = ClassDecl::new("WinitWindowDelegate", superclass).unwrap(); - - decl.add_method(sel!(dealloc), dealloc as extern "C" fn(&Object, Sel)); - decl.add_method( - sel!(initWithWinit:), - init_with_winit as extern "C" fn(&Object, Sel, *mut c_void) -> id, - ); - - decl.add_method( - sel!(windowShouldClose:), - window_should_close as extern "C" fn(&Object, Sel, id) -> BOOL, - ); - decl.add_method( - sel!(windowWillClose:), - window_will_close as extern "C" fn(&Object, Sel, id), - ); - decl.add_method( - sel!(windowDidResize:), - window_did_resize as extern "C" fn(&Object, Sel, id), - ); - decl.add_method( - sel!(windowDidMove:), - window_did_move as extern "C" fn(&Object, Sel, id), - ); - decl.add_method( - sel!(windowDidChangeBackingProperties:), - window_did_change_backing_properties as extern "C" fn(&Object, Sel, id), - ); - decl.add_method( - sel!(windowDidBecomeKey:), - window_did_become_key as extern "C" fn(&Object, Sel, id), - ); - decl.add_method( - sel!(windowDidResignKey:), - window_did_resign_key as extern "C" fn(&Object, Sel, id), - ); - - decl.add_method( - sel!(draggingEntered:), - dragging_entered as extern "C" fn(&Object, Sel, id) -> BOOL, - ); - decl.add_method( - sel!(prepareForDragOperation:), - prepare_for_drag_operation as extern "C" fn(&Object, Sel, id) -> BOOL, - ); - decl.add_method( - sel!(performDragOperation:), - perform_drag_operation as extern "C" fn(&Object, Sel, id) -> BOOL, - ); - decl.add_method( - sel!(concludeDragOperation:), - conclude_drag_operation as extern "C" fn(&Object, Sel, id), - ); - decl.add_method( - sel!(draggingExited:), - dragging_exited as extern "C" fn(&Object, Sel, id), - ); - - decl.add_method( - sel!(window:willUseFullScreenPresentationOptions:), - window_will_use_fullscreen_presentation_options - as extern "C" fn(&Object, Sel, id, NSUInteger) -> NSUInteger, - ); - decl.add_method( - sel!(windowDidEnterFullScreen:), - window_did_enter_fullscreen as extern "C" fn(&Object, Sel, id), - ); - decl.add_method( - sel!(windowWillEnterFullScreen:), - window_will_enter_fullscreen as extern "C" fn(&Object, Sel, id), - ); - decl.add_method( - sel!(windowDidExitFullScreen:), - window_did_exit_fullscreen as extern "C" fn(&Object, Sel, id), - ); - decl.add_method( - sel!(windowWillExitFullScreen:), - window_will_exit_fullscreen as extern "C" fn(&Object, Sel, id), - ); - decl.add_method( - sel!(windowDidFailToEnterFullScreen:), - window_did_fail_to_enter_fullscreen as extern "C" fn(&Object, Sel, id), - ); - - decl.add_ivar::<*mut c_void>("winitState"); - WindowDelegateClass(decl.register()) - }; -} +static WINDOW_DELEGATE_CLASS: Lazy = Lazy::new(|| unsafe { + let superclass = class!(NSResponder); + let mut decl = ClassDecl::new("WinitWindowDelegate", superclass).unwrap(); + + decl.add_method(sel!(dealloc), dealloc as extern "C" fn(&Object, Sel)); + decl.add_method( + sel!(initWithWinit:), + init_with_winit as extern "C" fn(&Object, Sel, *mut c_void) -> id, + ); + + decl.add_method( + sel!(windowShouldClose:), + window_should_close as extern "C" fn(&Object, Sel, id) -> BOOL, + ); + decl.add_method( + sel!(windowWillClose:), + window_will_close as extern "C" fn(&Object, Sel, id), + ); + decl.add_method( + sel!(windowDidResize:), + window_did_resize as extern "C" fn(&Object, Sel, id), + ); + decl.add_method( + sel!(windowDidMove:), + window_did_move as extern "C" fn(&Object, Sel, id), + ); + decl.add_method( + sel!(windowDidChangeBackingProperties:), + window_did_change_backing_properties as extern "C" fn(&Object, Sel, id), + ); + decl.add_method( + sel!(windowDidBecomeKey:), + window_did_become_key as extern "C" fn(&Object, Sel, id), + ); + decl.add_method( + sel!(windowDidResignKey:), + window_did_resign_key as extern "C" fn(&Object, Sel, id), + ); + + decl.add_method( + sel!(draggingEntered:), + dragging_entered as extern "C" fn(&Object, Sel, id) -> BOOL, + ); + decl.add_method( + sel!(prepareForDragOperation:), + prepare_for_drag_operation as extern "C" fn(&Object, Sel, id) -> BOOL, + ); + decl.add_method( + sel!(performDragOperation:), + perform_drag_operation as extern "C" fn(&Object, Sel, id) -> BOOL, + ); + decl.add_method( + sel!(concludeDragOperation:), + conclude_drag_operation as extern "C" fn(&Object, Sel, id), + ); + decl.add_method( + sel!(draggingExited:), + dragging_exited as extern "C" fn(&Object, Sel, id), + ); + + decl.add_method( + sel!(window:willUseFullScreenPresentationOptions:), + window_will_use_fullscreen_presentation_options + as extern "C" fn(&Object, Sel, id, NSUInteger) -> NSUInteger, + ); + decl.add_method( + sel!(windowDidEnterFullScreen:), + window_did_enter_fullscreen as extern "C" fn(&Object, Sel, id), + ); + decl.add_method( + sel!(windowWillEnterFullScreen:), + window_will_enter_fullscreen as extern "C" fn(&Object, Sel, id), + ); + decl.add_method( + sel!(windowDidExitFullScreen:), + window_did_exit_fullscreen as extern "C" fn(&Object, Sel, id), + ); + decl.add_method( + sel!(windowWillExitFullScreen:), + window_will_exit_fullscreen as extern "C" fn(&Object, Sel, id), + ); + decl.add_method( + sel!(windowDidFailToEnterFullScreen:), + window_did_fail_to_enter_fullscreen as extern "C" fn(&Object, Sel, id), + ); + + decl.add_ivar::<*mut c_void>("winitState"); + WindowDelegateClass(decl.register()) +}); // This function is definitely unsafe, but labeling that would increase // boilerplate and wouldn't really clarify anything... diff --git a/src/platform_impl/windows/dark_mode.rs b/src/platform_impl/windows/dark_mode.rs index 8e1deac3ae..6487617f2c 100644 --- a/src/platform_impl/windows/dark_mode.rs +++ b/src/platform_impl/windows/dark_mode.rs @@ -2,6 +2,7 @@ /// which is inspired by the solution in https://github.com/ysc3839/win32-darkmode use std::{ffi::c_void, ptr}; +use once_cell::sync::Lazy; use windows_sys::{ core::PCSTR, Win32::{ @@ -22,47 +23,45 @@ use crate::window::Theme; use super::util; -lazy_static! { - static ref WIN10_BUILD_VERSION: Option = { - type RtlGetVersion = unsafe extern "system" fn (*mut OSVERSIONINFOW) -> NTSTATUS; - let handle = get_function!("ntdll.dll", RtlGetVersion); - - if let Some(rtl_get_version) = handle { - unsafe { - let mut vi = OSVERSIONINFOW { - dwOSVersionInfoSize: 0, - dwMajorVersion: 0, - dwMinorVersion: 0, - dwBuildNumber: 0, - dwPlatformId: 0, - szCSDVersion: [0; 128], - }; - - let status = (rtl_get_version)(&mut vi); - - if status >= 0 && vi.dwMajorVersion == 10 && vi.dwMinorVersion == 0 { - Some(vi.dwBuildNumber) - } else { - None - } - } - } else { - None - } - }; +static WIN10_BUILD_VERSION: Lazy> = Lazy::new(|| { + type RtlGetVersion = unsafe extern "system" fn(*mut OSVERSIONINFOW) -> NTSTATUS; + let handle = get_function!("ntdll.dll", RtlGetVersion); + + if let Some(rtl_get_version) = handle { + unsafe { + let mut vi = OSVERSIONINFOW { + dwOSVersionInfoSize: 0, + dwMajorVersion: 0, + dwMinorVersion: 0, + dwBuildNumber: 0, + dwPlatformId: 0, + szCSDVersion: [0; 128], + }; + + let status = (rtl_get_version)(&mut vi); - static ref DARK_MODE_SUPPORTED: bool = { - // We won't try to do anything for windows versions < 17763 - // (Windows 10 October 2018 update) - match *WIN10_BUILD_VERSION { - Some(v) => v >= 17763, - None => false + if status >= 0 && vi.dwMajorVersion == 10 && vi.dwMinorVersion == 0 { + Some(vi.dwBuildNumber) + } else { + None + } } - }; + } else { + None + } +}); + +static DARK_MODE_SUPPORTED: Lazy = Lazy::new(|| { + // We won't try to do anything for windows versions < 17763 + // (Windows 10 October 2018 update) + match *WIN10_BUILD_VERSION { + Some(v) => v >= 17763, + None => false, + } +}); - static ref DARK_THEME_NAME: Vec = util::encode_wide("DarkMode_Explorer"); - static ref LIGHT_THEME_NAME: Vec = util::encode_wide(""); -} +static DARK_THEME_NAME: Lazy> = Lazy::new(|| util::encode_wide("DarkMode_Explorer")); +static LIGHT_THEME_NAME: Lazy> = Lazy::new(|| util::encode_wide("")); /// Attempt to set a theme on a window, if necessary. /// Returns the theme that was picked @@ -113,10 +112,8 @@ fn set_dark_mode_for_window(hwnd: HWND, is_dark_mode: bool) -> bool { cbData: usize, } - lazy_static! { - static ref SET_WINDOW_COMPOSITION_ATTRIBUTE: Option = - get_function!("user32.dll", SetWindowCompositionAttribute); - } + static SET_WINDOW_COMPOSITION_ATTRIBUTE: Lazy> = + Lazy::new(|| get_function!("user32.dll", SetWindowCompositionAttribute)); if let Some(set_window_composition_attribute) = *SET_WINDOW_COMPOSITION_ATTRIBUTE { unsafe { @@ -144,23 +141,19 @@ fn should_use_dark_mode() -> bool { fn should_apps_use_dark_mode() -> bool { type ShouldAppsUseDarkMode = unsafe extern "system" fn() -> bool; - lazy_static! { - static ref SHOULD_APPS_USE_DARK_MODE: Option = { - unsafe { - const UXTHEME_SHOULDAPPSUSEDARKMODE_ORDINAL: PCSTR = 132 as PCSTR; + static SHOULD_APPS_USE_DARK_MODE: Lazy> = Lazy::new(|| unsafe { + const UXTHEME_SHOULDAPPSUSEDARKMODE_ORDINAL: PCSTR = 132 as PCSTR; - let module = LoadLibraryA("uxtheme.dll\0".as_ptr()); + let module = LoadLibraryA("uxtheme.dll\0".as_ptr()); - if module == 0 { - return None; - } + if module == 0 { + return None; + } - let handle = GetProcAddress(module, UXTHEME_SHOULDAPPSUSEDARKMODE_ORDINAL); + let handle = GetProcAddress(module, UXTHEME_SHOULDAPPSUSEDARKMODE_ORDINAL); - handle.map(|handle| std::mem::transmute(handle)) - } - }; - } + handle.map(|handle| std::mem::transmute(handle)) + }); SHOULD_APPS_USE_DARK_MODE .map(|should_apps_use_dark_mode| unsafe { (should_apps_use_dark_mode)() }) diff --git a/src/platform_impl/windows/event_loop.rs b/src/platform_impl/windows/event_loop.rs index b3e9826649..b23f320346 100644 --- a/src/platform_impl/windows/event_loop.rs +++ b/src/platform_impl/windows/event_loop.rs @@ -2,6 +2,7 @@ mod runner; +use once_cell::sync::Lazy; use parking_lot::Mutex; use std::{ cell::Cell, @@ -112,18 +113,17 @@ type GetPointerTouchInfo = type GetPointerPenInfo = unsafe extern "system" fn(pointId: u32, penInfo: *mut POINTER_PEN_INFO) -> BOOL; -lazy_static! { - static ref GET_POINTER_FRAME_INFO_HISTORY: Option = - get_function!("user32.dll", GetPointerFrameInfoHistory); - static ref SKIP_POINTER_FRAME_MESSAGES: Option = - get_function!("user32.dll", SkipPointerFrameMessages); - static ref GET_POINTER_DEVICE_RECTS: Option = - get_function!("user32.dll", GetPointerDeviceRects); - static ref GET_POINTER_TOUCH_INFO: Option = - get_function!("user32.dll", GetPointerTouchInfo); - static ref GET_POINTER_PEN_INFO: Option = - get_function!("user32.dll", GetPointerPenInfo); -} +static GET_POINTER_FRAME_INFO_HISTORY: Lazy> = + Lazy::new(|| get_function!("user32.dll", GetPointerFrameInfoHistory)); +static SKIP_POINTER_FRAME_MESSAGES: Lazy> = + Lazy::new(|| get_function!("user32.dll", SkipPointerFrameMessages)); +static GET_POINTER_DEVICE_RECTS: Lazy> = + Lazy::new(|| get_function!("user32.dll", GetPointerDeviceRects)); +static GET_POINTER_TOUCH_INFO: Lazy> = + Lazy::new(|| get_function!("user32.dll", GetPointerTouchInfo)); +static GET_POINTER_PEN_INFO: Lazy> = + Lazy::new(|| get_function!("user32.dll", GetPointerPenInfo)); + pub(crate) struct WindowData { pub window_state: Arc>, pub event_loop_runner: EventLoopRunnerShared, @@ -375,19 +375,17 @@ fn get_wait_thread_id() -> u32 { } } -lazy_static! { - static ref WAIT_PERIOD_MIN: Option = unsafe { - let mut caps = TIMECAPS { - wPeriodMin: 0, - wPeriodMax: 0, - }; - if timeGetDevCaps(&mut caps, mem::size_of::() as u32) == TIMERR_NOERROR { - Some(caps.wPeriodMin) - } else { - None - } +static WAIT_PERIOD_MIN: Lazy> = Lazy::new(|| unsafe { + let mut caps = TIMECAPS { + wPeriodMin: 0, + wPeriodMax: 0, }; -} + if timeGetDevCaps(&mut caps, mem::size_of::() as u32) == TIMERR_NOERROR { + Some(caps.wPeriodMin) + } else { + None + } +}); fn wait_thread(parent_thread_id: u32, msg_window_id: HWND) { unsafe { @@ -582,59 +580,36 @@ impl EventLoopProxy { type WaitUntilInstantBox = Box; -lazy_static! { - // Message sent by the `EventLoopProxy` when we want to wake up the thread. - // WPARAM and LPARAM are unused. - static ref USER_EVENT_MSG_ID: u32 = { - unsafe { - RegisterWindowMessageA("Winit::WakeupMsg\0".as_ptr()) - } - }; - // Message sent when we want to execute a closure in the thread. - // WPARAM contains a Box> that must be retrieved with `Box::from_raw`, - // and LPARAM is unused. - static ref EXEC_MSG_ID: u32 = { - unsafe { - RegisterWindowMessageA("Winit::ExecMsg\0".as_ptr()) - } - }; - static ref PROCESS_NEW_EVENTS_MSG_ID: u32 = { - unsafe { - RegisterWindowMessageA("Winit::ProcessNewEvents\0".as_ptr()) - } - }; - /// lparam is the wait thread's message id. - static ref SEND_WAIT_THREAD_ID_MSG_ID: u32 = { - unsafe { - RegisterWindowMessageA("Winit::SendWaitThreadId\0".as_ptr()) - } - }; - /// lparam points to a `Box` signifying the time `PROCESS_NEW_EVENTS_MSG_ID` should - /// be sent. - static ref WAIT_UNTIL_MSG_ID: u32 = { - unsafe { - RegisterWindowMessageA("Winit::WaitUntil\0".as_ptr()) - } - }; - static ref CANCEL_WAIT_UNTIL_MSG_ID: u32 = { - unsafe { - RegisterWindowMessageA("Winit::CancelWaitUntil\0".as_ptr()) - } - }; - // Message sent by a `Window` when it wants to be destroyed by the main thread. - // WPARAM and LPARAM are unused. - pub static ref DESTROY_MSG_ID: u32 = { - unsafe { - RegisterWindowMessageA("Winit::DestroyMsg\0".as_ptr()) - } - }; - // WPARAM is a bool specifying the `WindowFlags::MARKER_RETAIN_STATE_ON_SIZE` flag. See the - // documentation in the `window_state` module for more information. - pub static ref SET_RETAIN_STATE_ON_SIZE_MSG_ID: u32 = unsafe { - RegisterWindowMessageA("Winit::SetRetainMaximized\0".as_ptr()) - }; - static ref THREAD_EVENT_TARGET_WINDOW_CLASS: Vec = util::encode_wide("Winit Thread Event Target"); -} +// Message sent by the `EventLoopProxy` when we want to wake up the thread. +// WPARAM and LPARAM are unused. +static USER_EVENT_MSG_ID: Lazy = + Lazy::new(|| unsafe { RegisterWindowMessageA("Winit::WakeupMsg\0".as_ptr()) }); +// Message sent when we want to execute a closure in the thread. +// WPARAM contains a Box> that must be retrieved with `Box::from_raw`, +// and LPARAM is unused. +static EXEC_MSG_ID: Lazy = + Lazy::new(|| unsafe { RegisterWindowMessageA("Winit::ExecMsg\0".as_ptr()) }); +static PROCESS_NEW_EVENTS_MSG_ID: Lazy = + Lazy::new(|| unsafe { RegisterWindowMessageA("Winit::ProcessNewEvents\0".as_ptr()) }); +/// lparam is the wait thread's message id. +static SEND_WAIT_THREAD_ID_MSG_ID: Lazy = + Lazy::new(|| unsafe { RegisterWindowMessageA("Winit::SendWaitThreadId\0".as_ptr()) }); +/// lparam points to a `Box` signifying the time `PROCESS_NEW_EVENTS_MSG_ID` should +/// be sent. +static WAIT_UNTIL_MSG_ID: Lazy = + Lazy::new(|| unsafe { RegisterWindowMessageA("Winit::WaitUntil\0".as_ptr()) }); +static CANCEL_WAIT_UNTIL_MSG_ID: Lazy = + Lazy::new(|| unsafe { RegisterWindowMessageA("Winit::CancelWaitUntil\0".as_ptr()) }); +// Message sent by a `Window` when it wants to be destroyed by the main thread. +// WPARAM and LPARAM are unused. +pub static DESTROY_MSG_ID: Lazy = + Lazy::new(|| unsafe { RegisterWindowMessageA("Winit::DestroyMsg\0".as_ptr()) }); +// WPARAM is a bool specifying the `WindowFlags::MARKER_RETAIN_STATE_ON_SIZE` flag. See the +// documentation in the `window_state` module for more information. +pub static SET_RETAIN_STATE_ON_SIZE_MSG_ID: Lazy = + Lazy::new(|| unsafe { RegisterWindowMessageA("Winit::SetRetainMaximized\0".as_ptr()) }); +static THREAD_EVENT_TARGET_WINDOW_CLASS: Lazy> = + Lazy::new(|| util::encode_wide("Winit Thread Event Target")); fn create_event_target_window() -> HWND { unsafe { diff --git a/src/platform_impl/windows/util.rs b/src/platform_impl/windows/util.rs index a327701ffa..fd74b674f6 100644 --- a/src/platform_impl/windows/util.rs +++ b/src/platform_impl/windows/util.rs @@ -9,6 +9,7 @@ use std::{ sync::atomic::{AtomicBool, Ordering}, }; +use once_cell::sync::Lazy; use windows_sys::{ core::{HRESULT, PCWSTR}, Win32::{ @@ -298,19 +299,17 @@ pub type AdjustWindowRectExForDpi = unsafe extern "system" fn( dpi: u32, ) -> BOOL; -lazy_static! { - pub static ref GET_DPI_FOR_WINDOW: Option = - get_function!("user32.dll", GetDpiForWindow); - pub static ref ADJUST_WINDOW_RECT_EX_FOR_DPI: Option = - get_function!("user32.dll", AdjustWindowRectExForDpi); - pub static ref GET_DPI_FOR_MONITOR: Option = - get_function!("shcore.dll", GetDpiForMonitor); - pub static ref ENABLE_NON_CLIENT_DPI_SCALING: Option = - get_function!("user32.dll", EnableNonClientDpiScaling); - pub static ref SET_PROCESS_DPI_AWARENESS_CONTEXT: Option = - get_function!("user32.dll", SetProcessDpiAwarenessContext); - pub static ref SET_PROCESS_DPI_AWARENESS: Option = - get_function!("shcore.dll", SetProcessDpiAwareness); - pub static ref SET_PROCESS_DPI_AWARE: Option = - get_function!("user32.dll", SetProcessDPIAware); -} +pub static GET_DPI_FOR_WINDOW: Lazy> = + Lazy::new(|| get_function!("user32.dll", GetDpiForWindow)); +pub static ADJUST_WINDOW_RECT_EX_FOR_DPI: Lazy> = + Lazy::new(|| get_function!("user32.dll", AdjustWindowRectExForDpi)); +pub static GET_DPI_FOR_MONITOR: Lazy> = + Lazy::new(|| get_function!("shcore.dll", GetDpiForMonitor)); +pub static ENABLE_NON_CLIENT_DPI_SCALING: Lazy> = + Lazy::new(|| get_function!("user32.dll", EnableNonClientDpiScaling)); +pub static SET_PROCESS_DPI_AWARENESS_CONTEXT: Lazy> = + Lazy::new(|| get_function!("user32.dll", SetProcessDpiAwarenessContext)); +pub static SET_PROCESS_DPI_AWARENESS: Lazy> = + Lazy::new(|| get_function!("shcore.dll", SetProcessDpiAwareness)); +pub static SET_PROCESS_DPI_AWARE: Lazy> = + Lazy::new(|| get_function!("user32.dll", SetProcessDPIAware)); From c5eaa0ab699be019c76b9b24a0d4f9b9aa72f11b Mon Sep 17 00:00:00 2001 From: tinaun Date: Thu, 9 Jun 2022 10:08:52 -0400 Subject: [PATCH 07/60] macOS: Emit LoopDestroyed on CMD+Q (#2073) override applicationWillTerminate: Co-authored-by: Mads Marquart --- CHANGELOG.md | 1 + src/platform_impl/macos/app_delegate.rs | 12 ++++++++++++ 2 files changed, 13 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8c3e879499..235a8ab1f2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ And please only add new entries to the top of this list, right below the `# Unre # Unreleased +- On macOS, Fix emitting `Event::LoopDestroyed` on CMD+Q. - On macOS, fixed an issue where having multiple windows would prevent run_return from ever returning. - On Wayland, fix bug where the cursor wouldn't hide in GNOME. - On macOS, Windows, and Wayland, add `set_cursor_hittest` to let the window ignore mouse events. diff --git a/src/platform_impl/macos/app_delegate.rs b/src/platform_impl/macos/app_delegate.rs index 5ff8be2fb0..0ba31ecd40 100644 --- a/src/platform_impl/macos/app_delegate.rs +++ b/src/platform_impl/macos/app_delegate.rs @@ -34,6 +34,11 @@ pub static APP_DELEGATE_CLASS: Lazy = Lazy::new(|| unsafe { sel!(applicationDidFinishLaunching:), did_finish_launching as extern "C" fn(&Object, Sel, id), ); + decl.add_method( + sel!(applicationWillTerminate:), + will_terminate as extern "C" fn(&Object, Sel, id), + ); + decl.add_ivar::<*mut c_void>(AUX_DELEGATE_STATE_NAME); AppDelegateClass(decl.register()) @@ -75,3 +80,10 @@ extern "C" fn did_finish_launching(this: &Object, _: Sel, _: id) { trace_scope!("applicationDidFinishLaunching:"); AppState::launched(this); } + +extern "C" fn will_terminate(_this: &Object, _: Sel, _: id) { + trace!("Triggered `applicationWillTerminate`"); + // TODO: Notify every window that it will be destroyed, like done in iOS? + AppState::exit(); + trace!("Completed `applicationWillTerminate`"); +} From 57981b533d9ce217345bfcee0c24d0619563e2bc Mon Sep 17 00:00:00 2001 From: Marijn Suijten Date: Fri, 10 Jun 2022 11:37:06 +0200 Subject: [PATCH 08/60] On Android, use `HasRawWindowHandle` directly from the `ndk` crate (#2318) The `ndk` crate now implements [`HasRawWindowHandle` directly on `NativeWindow`], relieving the burden to reimplement it on `winit`. [`HasRawWindowHandle` directly on `NativeWindow`]: https://github.com/rust-windowing/android-ndk-rs/pull/274 --- Cargo.toml | 6 +++--- src/platform_impl/android/mod.rs | 8 +++----- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 764da52e81..d98a6bc1c2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -38,9 +38,9 @@ image = { version = "0.24.0", default-features = false, features = ["png"] } simple_logger = "2.1.0" [target.'cfg(target_os = "android")'.dependencies] -ndk = "0.6" -ndk-sys = "0.3" -ndk-glue = "0.6" +# Coordinate the next winit release with android-ndk-rs: https://github.com/rust-windowing/winit/issues/1995 +ndk = { git = "https://github.com/rust-windowing/android-ndk-rs", rev = "7e33384" } +ndk-glue = { git = "https://github.com/rust-windowing/android-ndk-rs", rev = "7e33384" } [target.'cfg(any(target_os = "ios", target_os = "macos"))'.dependencies] objc = "0.2.7" diff --git a/src/platform_impl/android/mod.rs b/src/platform_impl/android/mod.rs index b67f47eb12..255f921118 100644 --- a/src/platform_impl/android/mod.rs +++ b/src/platform_impl/android/mod.rs @@ -13,7 +13,7 @@ use ndk::{ }; use ndk_glue::{Event, Rect}; use once_cell::sync::Lazy; -use raw_window_handle::{AndroidNdkHandle, RawWindowHandle}; +use raw_window_handle::{HasRawWindowHandle, RawWindowHandle}; use crate::{ dpi::{PhysicalPosition, PhysicalSize, Position, Size}, @@ -786,13 +786,11 @@ impl Window { } pub fn raw_window_handle(&self) -> RawWindowHandle { - let mut handle = AndroidNdkHandle::empty(); if let Some(native_window) = ndk_glue::native_window().as_ref() { - handle.a_native_window = unsafe { native_window.ptr().as_mut() as *mut _ as *mut _ } + native_window.raw_window_handle() } else { panic!("Cannot get the native window, it's null and will always be null before Event::Resumed and after Event::Suspended. Make sure you only call this function between those events."); - }; - RawWindowHandle::AndroidNdk(handle) + } } pub fn config(&self) -> Configuration { From 10419ff441266f6f73612dd08a423bb0b8ed74a8 Mon Sep 17 00:00:00 2001 From: Kirill Chibisov Date: Fri, 10 Jun 2022 13:43:33 +0300 Subject: [PATCH 09/60] Run clippy on CI Fixes #1402. --- .github/workflows/ci.yml | 8 +- CHANGELOG.md | 2 + examples/control_flow.rs | 2 + examples/cursor.rs | 2 + examples/cursor_grab.rs | 2 + examples/custom_events.rs | 2 + examples/drag_window.rs | 2 + examples/fullscreen.rs | 2 + examples/handling_close.rs | 2 + examples/ime.rs | 2 + examples/min_max_size.rs | 2 + examples/minimize.rs | 2 +- examples/monitor_list.rs | 2 + examples/mouse_wheel.rs | 2 + examples/multithreaded.rs | 2 + examples/multiwindow.rs | 2 + examples/request_redraw.rs | 2 + examples/request_redraw_threaded.rs | 10 +- examples/resizable.rs | 2 + examples/timer.rs | 2 + examples/transparent.rs | 2 + examples/video_modes.rs | 2 + examples/web.rs | 3 + examples/window.rs | 2 + examples/window_debug.rs | 104 +++++++++--------- examples/window_icon.rs | 3 +- examples/window_run_return.rs | 2 + src/event_loop.rs | 8 ++ src/lib.rs | 3 + src/platform_impl/android/mod.rs | 10 +- src/platform_impl/ios/app_state.rs | 47 ++++---- src/platform_impl/ios/event_loop.rs | 2 +- src/platform_impl/ios/ffi.rs | 17 ++- src/platform_impl/ios/mod.rs | 7 +- src/platform_impl/ios/monitor.rs | 6 +- src/platform_impl/ios/view.rs | 42 +++---- src/platform_impl/ios/window.rs | 50 ++++----- src/platform_impl/linux/mod.rs | 15 +-- src/platform_impl/linux/wayland/output.rs | 2 +- .../linux/wayland/seat/pointer/handlers.rs | 16 +-- .../linux/wayland/seat/pointer/mod.rs | 2 +- .../linux/wayland/window/shim.rs | 3 +- .../linux/x11/event_processor.rs | 76 ++++++------- src/platform_impl/linux/x11/ime/context.rs | 10 +- src/platform_impl/linux/x11/mod.rs | 17 ++- src/platform_impl/linux/x11/monitor.rs | 4 +- src/platform_impl/linux/x11/util/geometry.rs | 12 +- src/platform_impl/linux/x11/util/hint.rs | 8 +- src/platform_impl/linux/x11/util/icon.rs | 2 + src/platform_impl/linux/x11/util/randr.rs | 9 +- src/platform_impl/linux/x11/util/wm.rs | 4 +- src/platform_impl/macos/app_state.rs | 2 +- src/platform_impl/macos/event_loop.rs | 8 +- src/platform_impl/macos/mod.rs | 1 + src/platform_impl/macos/util/async.rs | 8 +- src/platform_impl/macos/util/mod.rs | 6 +- src/platform_impl/macos/view.rs | 20 ++-- src/platform_impl/macos/window.rs | 6 +- src/platform_impl/macos/window_delegate.rs | 13 ++- src/platform_impl/web/event_loop/mod.rs | 2 +- src/platform_impl/web/event_loop/runner.rs | 7 +- src/platform_impl/web/event_loop/state.rs | 5 +- src/platform_impl/web/web_sys/canvas.rs | 14 +-- .../web/web_sys/canvas/mouse_handler.rs | 8 +- .../web/web_sys/media_query_handle.rs | 2 +- src/platform_impl/web/web_sys/mod.rs | 2 +- src/platform_impl/web/web_sys/scaling.rs | 4 +- src/platform_impl/web/web_sys/timeout.rs | 4 +- src/platform_impl/web/window.rs | 10 +- src/platform_impl/windows/event_loop.rs | 2 +- src/platform_impl/windows/ime.rs | 15 ++- src/window.rs | 6 +- 72 files changed, 377 insertions(+), 312 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 10eabd8f74..fb7eb20364 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,7 +17,8 @@ jobs: - name: Check Formatting run: cargo +stable fmt --all -- --check - Tests: + tests: + name: Tests strategy: fail-fast: false matrix: @@ -62,6 +63,7 @@ jobs: with: rust-version: ${{ matrix.rust_version }}${{ matrix.platform.host }} targets: ${{ matrix.platform.target }} + components: clippy - name: Install Linux dependencies if: (matrix.platform.os == 'ubuntu-latest') @@ -93,6 +95,10 @@ jobs: !contains(matrix.platform.target, 'wasm32')) run: cargo $CMD test --verbose --target ${{ matrix.platform.target }} $OPTIONS --features $FEATURES + - name: Lint with clippy + shell: bash + if: (matrix.rust_version != 'nightly') && !contains(matrix.platform.options, '--no-default-features') + run: cargo clippy --all-targets --target ${{ matrix.platform.target }} $OPTIONS --features $FEATURES -- -Dwarnings - name: Build with serde enabled shell: bash diff --git a/CHANGELOG.md b/CHANGELOG.md index 235a8ab1f2..b182f9f80d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -53,6 +53,8 @@ And please only add new entries to the top of this list, right below the `# Unre - **Breaking:** IME input on desktop platforms won't be received unless it's explicitly allowed via `Window::set_ime_allowed` and new `WindowEvent::Ime` events are handled. - On macOS, `WindowEvent::Resized` is now emitted in `frameDidChange` instead of `windowDidResize`. - **Breaking:** On X11, device events are now ignored for unfocused windows by default, use `EventLoopWindowTarget::set_device_event_filter` to set the filter level. +- Implemented `Default` on `EventLoop<()>`. +- Implemented `Eq` for `Fullscreen`, `Theme`, and `UserAttentionType`. # 0.26.1 (2022-01-05) diff --git a/examples/control_flow.rs b/examples/control_flow.rs index 0946dbe794..94926d7764 100644 --- a/examples/control_flow.rs +++ b/examples/control_flow.rs @@ -1,3 +1,5 @@ +#![allow(clippy::single_match)] + use std::{thread, time}; use simple_logger::SimpleLogger; diff --git a/examples/cursor.rs b/examples/cursor.rs index 86bed427c8..fdef7ef971 100644 --- a/examples/cursor.rs +++ b/examples/cursor.rs @@ -1,3 +1,5 @@ +#![allow(clippy::single_match)] + use simple_logger::SimpleLogger; use winit::{ event::{ElementState, Event, KeyboardInput, WindowEvent}, diff --git a/examples/cursor_grab.rs b/examples/cursor_grab.rs index 979fed65e1..496b67704d 100644 --- a/examples/cursor_grab.rs +++ b/examples/cursor_grab.rs @@ -1,3 +1,5 @@ +#![allow(clippy::single_match)] + use simple_logger::SimpleLogger; use winit::{ event::{DeviceEvent, ElementState, Event, KeyboardInput, ModifiersState, WindowEvent}, diff --git a/examples/custom_events.rs b/examples/custom_events.rs index 5ba5d4e341..c1b7133de6 100644 --- a/examples/custom_events.rs +++ b/examples/custom_events.rs @@ -1,3 +1,5 @@ +#![allow(clippy::single_match)] + #[cfg(not(target_arch = "wasm32"))] fn main() { use simple_logger::SimpleLogger; diff --git a/examples/drag_window.rs b/examples/drag_window.rs index 753958c8f1..813e9b00c9 100644 --- a/examples/drag_window.rs +++ b/examples/drag_window.rs @@ -1,3 +1,5 @@ +#![allow(clippy::single_match)] + use simple_logger::SimpleLogger; use winit::{ event::{ diff --git a/examples/fullscreen.rs b/examples/fullscreen.rs index b5a9ebb2de..4b19efdc56 100644 --- a/examples/fullscreen.rs +++ b/examples/fullscreen.rs @@ -1,3 +1,5 @@ +#![allow(clippy::single_match)] + use std::io::{stdin, stdout, Write}; use simple_logger::SimpleLogger; diff --git a/examples/handling_close.rs b/examples/handling_close.rs index 2fd2d084b7..1fe4ad3708 100644 --- a/examples/handling_close.rs +++ b/examples/handling_close.rs @@ -1,3 +1,5 @@ +#![allow(clippy::single_match)] + use simple_logger::SimpleLogger; use winit::{ event::{Event, KeyboardInput, WindowEvent}, diff --git a/examples/ime.rs b/examples/ime.rs index 5f38447a85..bdf67a8624 100644 --- a/examples/ime.rs +++ b/examples/ime.rs @@ -1,3 +1,5 @@ +#![allow(clippy::single_match)] + use log::LevelFilter; use simple_logger::SimpleLogger; use winit::{ diff --git a/examples/min_max_size.rs b/examples/min_max_size.rs index 56877999e0..84852b3e71 100644 --- a/examples/min_max_size.rs +++ b/examples/min_max_size.rs @@ -1,3 +1,5 @@ +#![allow(clippy::single_match)] + use simple_logger::SimpleLogger; use winit::{ dpi::LogicalSize, diff --git a/examples/minimize.rs b/examples/minimize.rs index 8af561eeaa..55a62d1542 100644 --- a/examples/minimize.rs +++ b/examples/minimize.rs @@ -1,4 +1,4 @@ -extern crate winit; +#![allow(clippy::single_match)] use simple_logger::SimpleLogger; use winit::event::{Event, VirtualKeyCode, WindowEvent}; diff --git a/examples/monitor_list.rs b/examples/monitor_list.rs index 9c8b77e61c..fc52fa5e3f 100644 --- a/examples/monitor_list.rs +++ b/examples/monitor_list.rs @@ -1,3 +1,5 @@ +#![allow(clippy::single_match)] + use simple_logger::SimpleLogger; use winit::{event_loop::EventLoop, window::WindowBuilder}; diff --git a/examples/mouse_wheel.rs b/examples/mouse_wheel.rs index d6e665b1a2..c1d8f0cb99 100644 --- a/examples/mouse_wheel.rs +++ b/examples/mouse_wheel.rs @@ -1,3 +1,5 @@ +#![allow(clippy::single_match)] + use simple_logger::SimpleLogger; use winit::{ event::{Event, WindowEvent}, diff --git a/examples/multithreaded.rs b/examples/multithreaded.rs index 9ed28cdbd6..d4f7652950 100644 --- a/examples/multithreaded.rs +++ b/examples/multithreaded.rs @@ -1,3 +1,5 @@ +#![allow(clippy::single_match)] + #[cfg(not(target_arch = "wasm32"))] fn main() { use std::{collections::HashMap, sync::mpsc, thread, time::Duration}; diff --git a/examples/multiwindow.rs b/examples/multiwindow.rs index c6bff90afc..10d3fad3f5 100644 --- a/examples/multiwindow.rs +++ b/examples/multiwindow.rs @@ -1,3 +1,5 @@ +#![allow(clippy::single_match)] + use std::collections::HashMap; use simple_logger::SimpleLogger; diff --git a/examples/request_redraw.rs b/examples/request_redraw.rs index 28275a29f6..93bb43983e 100644 --- a/examples/request_redraw.rs +++ b/examples/request_redraw.rs @@ -1,3 +1,5 @@ +#![allow(clippy::single_match)] + use simple_logger::SimpleLogger; use winit::{ event::{ElementState, Event, WindowEvent}, diff --git a/examples/request_redraw_threaded.rs b/examples/request_redraw_threaded.rs index f6fba32afb..2500a8c02c 100644 --- a/examples/request_redraw_threaded.rs +++ b/examples/request_redraw_threaded.rs @@ -1,3 +1,5 @@ +#![allow(clippy::single_match)] + #[cfg(not(target_arch = "wasm32"))] fn main() { use std::{thread, time}; @@ -28,10 +30,10 @@ fn main() { control_flow.set_wait(); match event { - Event::WindowEvent { event, .. } => match event { - WindowEvent::CloseRequested => control_flow.set_exit(), - _ => (), - }, + Event::WindowEvent { + event: WindowEvent::CloseRequested, + .. + } => control_flow.set_exit(), Event::RedrawRequested(_) => { println!("\nredrawing!\n"); } diff --git a/examples/resizable.rs b/examples/resizable.rs index 07678756b9..1d10d4d83b 100644 --- a/examples/resizable.rs +++ b/examples/resizable.rs @@ -1,3 +1,5 @@ +#![allow(clippy::single_match)] + use simple_logger::SimpleLogger; use winit::{ dpi::LogicalSize, diff --git a/examples/timer.rs b/examples/timer.rs index 6ea4fc09ea..c312460b22 100644 --- a/examples/timer.rs +++ b/examples/timer.rs @@ -1,3 +1,5 @@ +#![allow(clippy::single_match)] + use instant::Instant; use std::time::Duration; diff --git a/examples/transparent.rs b/examples/transparent.rs index bed8de72b0..9080cbef12 100644 --- a/examples/transparent.rs +++ b/examples/transparent.rs @@ -1,3 +1,5 @@ +#![allow(clippy::single_match)] + use simple_logger::SimpleLogger; use winit::{ event::{Event, WindowEvent}, diff --git a/examples/video_modes.rs b/examples/video_modes.rs index 341f43855b..afc4ad6be6 100644 --- a/examples/video_modes.rs +++ b/examples/video_modes.rs @@ -1,3 +1,5 @@ +#![allow(clippy::single_match)] + use simple_logger::SimpleLogger; use winit::event_loop::EventLoop; diff --git a/examples/web.rs b/examples/web.rs index 600a3d8d74..71e5dfd60a 100644 --- a/examples/web.rs +++ b/examples/web.rs @@ -1,3 +1,5 @@ +#![allow(clippy::single_match)] + use winit::{ event::{Event, WindowEvent}, event_loop::EventLoop, @@ -43,6 +45,7 @@ mod wasm { pub fn run() { console_log::init_with_level(log::Level::Debug).expect("error initializing logger"); + #[allow(clippy::main_recursion)] super::main(); } diff --git a/examples/window.rs b/examples/window.rs index 0d0fd74a46..23a633d2ed 100644 --- a/examples/window.rs +++ b/examples/window.rs @@ -1,3 +1,5 @@ +#![allow(clippy::single_match)] + use simple_logger::SimpleLogger; use winit::{ event::{Event, WindowEvent}, diff --git a/examples/window_debug.rs b/examples/window_debug.rs index 8c4f42c366..be1d251956 100644 --- a/examples/window_debug.rs +++ b/examples/window_debug.rs @@ -1,3 +1,5 @@ +#![allow(clippy::single_match)] + // This example is used by developers to test various window functions. use simple_logger::SimpleLogger; @@ -58,61 +60,63 @@ fn main() { _ => (), }, Event::WindowEvent { - event: WindowEvent::KeyboardInput { input, .. }, + event: + WindowEvent::KeyboardInput { + input: + KeyboardInput { + virtual_keycode: Some(key), + state: ElementState::Pressed, + .. + }, + .. + }, .. - } => match input { - KeyboardInput { - virtual_keycode: Some(key), - state: ElementState::Pressed, - .. - } => match key { - VirtualKeyCode::E => { - fn area(size: PhysicalSize) -> u32 { - size.width * size.height - } - - let monitor = window.current_monitor().unwrap(); - if let Some(mode) = monitor - .video_modes() - .max_by(|a, b| area(a.size()).cmp(&area(b.size()))) - { - window.set_fullscreen(Some(Fullscreen::Exclusive(mode))); - } else { - eprintln!("no video modes available"); - } - } - VirtualKeyCode::F => { - if window.fullscreen().is_some() { - window.set_fullscreen(None); - } else { - let monitor = window.current_monitor(); - window.set_fullscreen(Some(Fullscreen::Borderless(monitor))); - } - } - VirtualKeyCode::P => { - if window.fullscreen().is_some() { - window.set_fullscreen(None); - } else { - window.set_fullscreen(Some(Fullscreen::Borderless(None))); - } - } - VirtualKeyCode::M => { - minimized = !minimized; - window.set_minimized(minimized); + } => match key { + VirtualKeyCode::E => { + fn area(size: PhysicalSize) -> u32 { + size.width * size.height } - VirtualKeyCode::Q => { - control_flow.set_exit(); + + let monitor = window.current_monitor().unwrap(); + if let Some(mode) = monitor + .video_modes() + .max_by(|a, b| area(a.size()).cmp(&area(b.size()))) + { + window.set_fullscreen(Some(Fullscreen::Exclusive(mode))); + } else { + eprintln!("no video modes available"); } - VirtualKeyCode::V => { - visible = !visible; - window.set_visible(visible); + } + VirtualKeyCode::F => { + if window.fullscreen().is_some() { + window.set_fullscreen(None); + } else { + let monitor = window.current_monitor(); + window.set_fullscreen(Some(Fullscreen::Borderless(monitor))); } - VirtualKeyCode::X => { - let is_maximized = window.is_maximized(); - window.set_maximized(!is_maximized); + } + VirtualKeyCode::P => { + if window.fullscreen().is_some() { + window.set_fullscreen(None); + } else { + window.set_fullscreen(Some(Fullscreen::Borderless(None))); } - _ => (), - }, + } + VirtualKeyCode::M => { + minimized = !minimized; + window.set_minimized(minimized); + } + VirtualKeyCode::Q => { + control_flow.set_exit(); + } + VirtualKeyCode::V => { + visible = !visible; + window.set_visible(visible); + } + VirtualKeyCode::X => { + let is_maximized = window.is_maximized(); + window.set_maximized(!is_maximized); + } _ => (), }, Event::WindowEvent { diff --git a/examples/window_icon.rs b/examples/window_icon.rs index c120ed904b..ed98b3d577 100644 --- a/examples/window_icon.rs +++ b/examples/window_icon.rs @@ -1,4 +1,5 @@ -extern crate image; +#![allow(clippy::single_match)] + use std::path::Path; use simple_logger::SimpleLogger; diff --git a/examples/window_run_return.rs b/examples/window_run_return.rs index 81ddc5a225..d9a201ac55 100644 --- a/examples/window_run_return.rs +++ b/examples/window_run_return.rs @@ -1,3 +1,5 @@ +#![allow(clippy::single_match)] + // Limit this example to only compatible platforms. #[cfg(any( target_os = "windows", diff --git a/src/event_loop.rs b/src/event_loop.rs index 58181b3f80..d972962766 100644 --- a/src/event_loop.rs +++ b/src/event_loop.rs @@ -94,6 +94,8 @@ impl EventLoopBuilder { /// will fall back on X11. If this variable is set with any other value, winit will panic. #[inline] pub fn build(&mut self) -> EventLoop { + // Certain platforms accept a mutable reference in their API. + #[allow(clippy::unnecessary_mut_passed)] EventLoop { event_loop: platform_impl::EventLoop::new(&mut self.platform_specific), _marker: PhantomData, @@ -221,6 +223,12 @@ impl EventLoop<()> { } } +impl Default for EventLoop<()> { + fn default() -> Self { + Self::new() + } +} + impl EventLoop { #[deprecated = "Use `EventLoopBuilder::::with_user_event().build()` instead."] pub fn with_user_event() -> EventLoop { diff --git a/src/lib.rs b/src/lib.rs index 4ad8f806fc..3b519f002a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -132,6 +132,9 @@ #![deny(rust_2018_idioms)] #![deny(rustdoc::broken_intra_doc_links)] +#![deny(clippy::all)] +#![cfg_attr(feature = "cargo-clippy", deny(warnings))] +#![allow(clippy::missing_safety_doc)] #[allow(unused_imports)] #[macro_use] diff --git a/src/platform_impl/android/mod.rs b/src/platform_impl/android/mod.rs index 255f921118..e8d79a5d97 100644 --- a/src/platform_impl/android/mod.rs +++ b/src/platform_impl/android/mod.rs @@ -240,7 +240,7 @@ pub struct EventLoop { running: bool, } -#[derive(Default, Debug, Copy, Clone, PartialEq, Hash)] +#[derive(Default, Debug, Copy, Clone, PartialEq, Eq, Hash)] pub(crate) struct PlatformSpecificEventLoopAttributes {} macro_rules! call_event_handler { @@ -846,18 +846,16 @@ impl MonitorHandle { pub fn video_modes(&self) -> impl Iterator { let size = self.size().into(); - let mut v = Vec::new(); // FIXME this is not the real refresh rate - // (it is guarunteed to support 32 bit color though) - v.push(monitor::VideoMode { + // (it is guaranteed to support 32 bit color though) + std::iter::once(monitor::VideoMode { video_mode: VideoMode { size, bit_depth: 32, refresh_rate: 60, monitor: self.clone(), }, - }); - v.into_iter() + }) } } diff --git a/src/platform_impl/ios/app_state.rs b/src/platform_impl/ios/app_state.rs index c21ceff53b..ed7592bdf3 100644 --- a/src/platform_impl/ios/app_state.rs +++ b/src/platform_impl/ios/app_state.rs @@ -53,11 +53,7 @@ enum UserCallbackTransitionResult<'a> { impl Event<'static, Never> { fn is_redraw(&self) -> bool { - if let Event::RedrawRequested(_) = self { - true - } else { - false - } + matches!(self, Event::RedrawRequested(_)) } } @@ -120,7 +116,7 @@ impl Drop for AppState { } => { for &mut window in queued_windows { unsafe { - let () = msg_send![window, release]; + let _: () = msg_send![window, release]; } } } @@ -201,10 +197,10 @@ impl AppState { } fn has_launched(&self) -> bool { - match self.state() { - &AppStateImpl::NotLaunched { .. } | &AppStateImpl::Launching { .. } => false, - _ => true, - } + !matches!( + self.state(), + AppStateImpl::NotLaunched { .. } | AppStateImpl::Launching { .. } + ) } fn will_launch_transition(&mut self, queued_event_handler: Box) { @@ -529,7 +525,9 @@ pub unsafe fn queue_gl_or_metal_redraw(window: id) { | &mut AppStateImpl::InUserCallback { ref mut queued_gpu_redraws, .. - } => drop(queued_gpu_redraws.insert(window)), + } => { + let _ = queued_gpu_redraws.insert(window); + } s @ &mut AppStateImpl::ProcessingRedraws { .. } | s @ &mut AppStateImpl::Waiting { .. } | s @ &mut AppStateImpl::PollFinished { .. } => bug!("unexpected state {:?}", s), @@ -537,6 +535,7 @@ pub unsafe fn queue_gl_or_metal_redraw(window: id) { panic!("Attempt to create a `Window` after the app has terminated") } } + drop(this); } @@ -549,7 +548,7 @@ pub unsafe fn will_launch(queued_event_handler: Box) { pub unsafe fn did_finish_launching() { let mut this = AppState::get_mut(); let windows = match this.state_mut() { - AppStateImpl::Launching { queued_windows, .. } => mem::replace(queued_windows, Vec::new()), + AppStateImpl::Launching { queued_windows, .. } => mem::take(queued_windows), s => bug!("unexpected state {:?}", s), }; @@ -579,15 +578,15 @@ pub unsafe fn did_finish_launching() { // ``` let screen: id = msg_send![window, screen]; let _: id = msg_send![screen, retain]; - let () = msg_send![window, setScreen:0 as id]; - let () = msg_send![window, setScreen: screen]; - let () = msg_send![screen, release]; + let _: () = msg_send![window, setScreen:0 as id]; + let _: () = msg_send![window, setScreen: screen]; + let _: () = msg_send![screen, release]; let controller: id = msg_send![window, rootViewController]; - let () = msg_send![window, setRootViewController:ptr::null::<()>()]; - let () = msg_send![window, setRootViewController: controller]; - let () = msg_send![window, makeKeyAndVisible]; + let _: () = msg_send![window, setRootViewController:ptr::null::<()>()]; + let _: () = msg_send![window, setRootViewController: controller]; + let _: () = msg_send![window, makeKeyAndVisible]; } - let () = msg_send![window, release]; + let _: () = msg_send![window, release]; } let (windows, events) = AppState::get_mut().did_finish_launching_transition(); @@ -604,9 +603,9 @@ pub unsafe fn did_finish_launching() { let count: NSUInteger = msg_send![window, retainCount]; // make sure the window is still referenced if count > 1 { - let () = msg_send![window, makeKeyAndVisible]; + let _: () = msg_send![window, makeKeyAndVisible]; } - let () = msg_send![window, release]; + let _: () = msg_send![window, release]; } } @@ -671,7 +670,7 @@ pub unsafe fn handle_nonuser_events>(events &mut AppStateImpl::InUserCallback { ref mut queued_events, queued_gpu_redraws: _, - } => mem::replace(queued_events, Vec::new()), + } => mem::take(queued_events), s => bug!("unexpected state {:?}", s), }; if queued_events.is_empty() { @@ -752,7 +751,7 @@ unsafe fn handle_user_events() { &mut AppStateImpl::InUserCallback { ref mut queued_events, queued_gpu_redraws: _, - } => mem::replace(queued_events, Vec::new()), + } => mem::take(queued_events), s => bug!("unexpected state {:?}", s), }; if queued_events.is_empty() { @@ -876,7 +875,7 @@ fn handle_hidpi_proxy( let size = CGSize::new(logical_size); let new_frame: CGRect = CGRect::new(screen_frame.origin, size); unsafe { - let () = msg_send![view, setFrame: new_frame]; + let _: () = msg_send![view, setFrame: new_frame]; } } diff --git a/src/platform_impl/ios/event_loop.rs b/src/platform_impl/ios/event_loop.rs index feb6b7ea53..1a32fe56b2 100644 --- a/src/platform_impl/ios/event_loop.rs +++ b/src/platform_impl/ios/event_loop.rs @@ -69,7 +69,7 @@ pub struct EventLoop { window_target: RootEventLoopWindowTarget, } -#[derive(Default, Debug, Copy, Clone, PartialEq, Hash)] +#[derive(Default, Debug, Copy, Clone, PartialEq, Eq, Hash)] pub(crate) struct PlatformSpecificEventLoopAttributes {} impl EventLoop { diff --git a/src/platform_impl/ios/ffi.rs b/src/platform_impl/ios/ffi.rs index 726723f30b..7f560514d1 100644 --- a/src/platform_impl/ios/ffi.rs +++ b/src/platform_impl/ios/ffi.rs @@ -88,7 +88,7 @@ pub enum UITouchPhase { Cancelled, } -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Eq)] #[allow(dead_code)] #[repr(isize)] pub enum UIForceTouchCapability { @@ -97,7 +97,7 @@ pub enum UIForceTouchCapability { Available, } -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Eq)] #[allow(dead_code)] #[repr(isize)] pub enum UITouchType { @@ -144,10 +144,9 @@ impl From for UIUserInterfaceIdiom { } } } - -impl Into for UIUserInterfaceIdiom { - fn into(self) -> Idiom { - match self { +impl From for Idiom { + fn from(ui_idiom: UIUserInterfaceIdiom) -> Idiom { + match ui_idiom { UIUserInterfaceIdiom::Unspecified => Idiom::Unspecified, UIUserInterfaceIdiom::Phone => Idiom::Phone, UIUserInterfaceIdiom::Pad => Idiom::Pad, @@ -230,9 +229,9 @@ impl From for UIRectEdge { } } -impl Into for UIRectEdge { - fn into(self) -> ScreenEdge { - let bits: u8 = self.0.try_into().expect("invalid `UIRectEdge`"); +impl From for ScreenEdge { + fn from(ui_rect_edge: UIRectEdge) -> ScreenEdge { + let bits: u8 = ui_rect_edge.0.try_into().expect("invalid `UIRectEdge`"); ScreenEdge::from_bits(bits).expect("invalid `ScreenEdge`") } } diff --git a/src/platform_impl/ios/mod.rs b/src/platform_impl/ios/mod.rs index 53c230f3f0..37677918e5 100644 --- a/src/platform_impl/ios/mod.rs +++ b/src/platform_impl/ios/mod.rs @@ -56,6 +56,7 @@ //! Also note that app may not receive the LoopDestroyed event if suspended; it might be SIGKILL'ed. #![cfg(target_os = "ios")] +#![allow(clippy::let_unit_value)] // TODO: (mtak-) UIKit requires main thread for virtually all function/method calls. This could be // worked around in the future by using GCD (grand central dispatch) and/or caching of values like @@ -108,9 +109,7 @@ unsafe impl Sync for DeviceId {} pub enum OsError {} impl fmt::Display for OsError { - fn fmt(&self, _: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - _ => unreachable!(), - } + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "os error") } } diff --git a/src/platform_impl/ios/monitor.rs b/src/platform_impl/ios/monitor.rs index 4ba3a3dbb3..26f4af06b3 100644 --- a/src/platform_impl/ios/monitor.rs +++ b/src/platform_impl/ios/monitor.rs @@ -30,7 +30,7 @@ unsafe impl Send for NativeDisplayMode {} impl Drop for NativeDisplayMode { fn drop(&mut self) { unsafe { - let () = msg_send![self.0, release]; + let _: () = msg_send![self.0, release]; } } } @@ -115,7 +115,7 @@ pub struct Inner { impl Drop for Inner { fn drop(&mut self) { unsafe { - let () = msg_send![self.uiscreen, release]; + let _: () = msg_send![self.uiscreen, release]; } } } @@ -193,7 +193,7 @@ impl MonitorHandle { pub fn retained_new(uiscreen: id) -> MonitorHandle { unsafe { assert_main_thread!("`MonitorHandle` can only be cloned on the main thread on iOS"); - let () = msg_send![uiscreen, retain]; + let _: () = msg_send![uiscreen, retain]; } MonitorHandle { inner: Inner { uiscreen }, diff --git a/src/platform_impl/ios/view.rs b/src/platform_impl/ios/view.rs index ccdec6bfbf..e92b119b61 100644 --- a/src/platform_impl/ios/view.rs +++ b/src/platform_impl/ios/view.rs @@ -112,14 +112,14 @@ unsafe fn get_view_class(root_view_class: &'static Class) -> &'static Class { ))), ); let superclass: &'static Class = msg_send![object, superclass]; - let () = msg_send![super(object, superclass), drawRect: rect]; + let _: () = msg_send![super(object, superclass), drawRect: rect]; } } extern "C" fn layout_subviews(object: &Object, _: Sel) { unsafe { let superclass: &'static Class = msg_send![object, superclass]; - let () = msg_send![super(object, superclass), layoutSubviews]; + let _: () = msg_send![super(object, superclass), layoutSubviews]; let window: id = msg_send![object, window]; assert!(!window.is_null()); @@ -133,14 +133,14 @@ unsafe fn get_view_class(root_view_class: &'static Class) -> &'static Class { width: screen_frame.size.width as f64, height: screen_frame.size.height as f64, } - .to_physical(scale_factor.into()); + .to_physical(scale_factor as f64); // If the app is started in landscape, the view frame and window bounds can be mismatched. // The view frame will be in portrait and the window bounds in landscape. So apply the // window bounds to the view frame to make it consistent. let view_frame: CGRect = msg_send![object, frame]; if view_frame != window_bounds { - let () = msg_send![object, setFrame: window_bounds]; + let _: () = msg_send![object, setFrame: window_bounds]; } app_state::handle_nonuser_event(EventWrapper::StaticEvent(Event::WindowEvent { @@ -157,7 +157,7 @@ unsafe fn get_view_class(root_view_class: &'static Class) -> &'static Class { ) { unsafe { let superclass: &'static Class = msg_send![object, superclass]; - let () = msg_send![ + let _: () = msg_send![ super(object, superclass), setContentScaleFactor: untrusted_scale_factor ]; @@ -180,7 +180,7 @@ unsafe fn get_view_class(root_view_class: &'static Class) -> &'static Class { && scale_factor > 0.0, "invalid scale_factor set on UIView", ); - let scale_factor: f64 = scale_factor.into(); + let scale_factor = scale_factor as f64; let bounds: CGRect = msg_send![object, bounds]; let screen: id = msg_send![window, screen]; let screen_space: id = msg_send![screen, coordinateSpace]; @@ -340,7 +340,7 @@ unsafe fn get_view_controller_class() -> &'static Class { prefers_status_bar_hidden: BOOL, setPrefersStatusBarHidden: |object| { unsafe { - let () = msg_send![object, setNeedsStatusBarAppearanceUpdate]; + let _: () = msg_send![object, setNeedsStatusBarAppearanceUpdate]; } }, prefersStatusBarHidden, @@ -353,7 +353,7 @@ unsafe fn get_view_controller_class() -> &'static Class { OSCapabilities::home_indicator_hidden_err_msg; |object| { unsafe { - let () = msg_send![object, setNeedsUpdateOfHomeIndicatorAutoHidden]; + let _: () = msg_send![object, setNeedsUpdateOfHomeIndicatorAutoHidden]; } }, prefersHomeIndicatorAutoHidden, @@ -363,7 +363,7 @@ unsafe fn get_view_controller_class() -> &'static Class { supported_orientations: UIInterfaceOrientationMask, setSupportedInterfaceOrientations: |object| { unsafe { - let () = msg_send![class!(UIViewController), attemptRotationToDeviceOrientation]; + let _: () = msg_send![class!(UIViewController), attemptRotationToDeviceOrientation]; } }, supportedInterfaceOrientations, @@ -376,7 +376,7 @@ unsafe fn get_view_controller_class() -> &'static Class { OSCapabilities::defer_system_gestures_err_msg; |object| { unsafe { - let () = msg_send![object, setNeedsUpdateOfScreenEdgesDeferringSystemGestures]; + let _: () = msg_send![object, setNeedsUpdateOfScreenEdgesDeferringSystemGestures]; } }, preferredScreenEdgesDeferringSystemGestures, @@ -398,7 +398,7 @@ unsafe fn get_window_class() -> &'static Class { window_id: RootWindowId(object.into()), event: WindowEvent::Focused(true), })); - let () = msg_send![super(object, class!(UIWindow)), becomeKeyWindow]; + let _: () = msg_send![super(object, class!(UIWindow)), becomeKeyWindow]; } } @@ -408,7 +408,7 @@ unsafe fn get_window_class() -> &'static Class { window_id: RootWindowId(object.into()), event: WindowEvent::Focused(false), })); - let () = msg_send![super(object, class!(UIWindow)), resignKeyWindow]; + let _: () = msg_send![super(object, class!(UIWindow)), resignKeyWindow]; } } @@ -440,9 +440,9 @@ pub unsafe fn create_view( assert!(!view.is_null(), "Failed to create `UIView` instance"); let view: id = msg_send![view, initWithFrame: frame]; assert!(!view.is_null(), "Failed to initialize `UIView` instance"); - let () = msg_send![view, setMultipleTouchEnabled: YES]; + let _: () = msg_send![view, setMultipleTouchEnabled: YES]; if let Some(scale_factor) = platform_attributes.scale_factor { - let () = msg_send![view, setContentScaleFactor: scale_factor as CGFloat]; + let _: () = msg_send![view, setContentScaleFactor: scale_factor as CGFloat]; } view @@ -484,23 +484,23 @@ pub unsafe fn create_view_controller( let edges: UIRectEdge = platform_attributes .preferred_screen_edges_deferring_system_gestures .into(); - let () = msg_send![ + let _: () = msg_send![ view_controller, setPrefersStatusBarHidden: status_bar_hidden ]; - let () = msg_send![ + let _: () = msg_send![ view_controller, setSupportedInterfaceOrientations: supported_orientations ]; - let () = msg_send![ + let _: () = msg_send![ view_controller, setPrefersHomeIndicatorAutoHidden: prefers_home_indicator_hidden ]; - let () = msg_send![ + let _: () = msg_send![ view_controller, setPreferredScreenEdgesDeferringSystemGestures: edges ]; - let () = msg_send![view_controller, setView: view]; + let _: () = msg_send![view_controller, setView: view]; view_controller } @@ -520,11 +520,11 @@ pub unsafe fn create_window( !window.is_null(), "Failed to initialize `UIWindow` instance" ); - let () = msg_send![window, setRootViewController: view_controller]; + let _: () = msg_send![window, setRootViewController: view_controller]; match window_attributes.fullscreen { Some(Fullscreen::Exclusive(ref video_mode)) => { let uiscreen = video_mode.monitor().ui_screen() as id; - let () = msg_send![uiscreen, setCurrentMode: video_mode.video_mode.screen_mode.0]; + let _: () = msg_send![uiscreen, setCurrentMode: video_mode.video_mode.screen_mode.0]; msg_send![window, setScreen:video_mode.monitor().ui_screen()] } Some(Fullscreen::Borderless(ref monitor)) => { diff --git a/src/platform_impl/ios/window.rs b/src/platform_impl/ios/window.rs index 5df7734a34..7e3e0eaac7 100644 --- a/src/platform_impl/ios/window.rs +++ b/src/platform_impl/ios/window.rs @@ -37,9 +37,9 @@ pub struct Inner { impl Drop for Inner { fn drop(&mut self) { unsafe { - let () = msg_send![self.view, release]; - let () = msg_send![self.view_controller, release]; - let () = msg_send![self.window, release]; + let _: () = msg_send![self.view, release]; + let _: () = msg_send![self.view_controller, release]; + let _: () = msg_send![self.window, release]; } } } @@ -52,10 +52,10 @@ impl Inner { pub fn set_visible(&self, visible: bool) { match visible { true => unsafe { - let () = msg_send![self.window, setHidden: NO]; + let _: () = msg_send![self.window, setHidden: NO]; }, false => unsafe { - let () = msg_send![self.window, setHidden: YES]; + let _: () = msg_send![self.window, setHidden: YES]; }, } } @@ -78,7 +78,7 @@ impl Inner { // https://developer.apple.com/documentation/uikit/uiview/1622437-setneedsdisplay?language=objc app_state::queue_gl_or_metal_redraw(self.window); } else { - let () = msg_send![self.view, setNeedsDisplay]; + let _: () = msg_send![self.view, setNeedsDisplay]; } } } @@ -119,8 +119,8 @@ impl Inner { }, size: screen_frame.size, }; - let bounds = self.from_screen_space(new_screen_frame); - let () = msg_send![self.window, setBounds: bounds]; + let bounds = self.rect_from_screen_space(new_screen_frame); + let _: () = msg_send![self.window, setBounds: bounds]; } } @@ -218,7 +218,7 @@ impl Inner { let uiscreen = match monitor { Some(Fullscreen::Exclusive(video_mode)) => { let uiscreen = video_mode.video_mode.monitor.ui_screen() as id; - let () = + let _: () = msg_send![uiscreen, setCurrentMode: video_mode.video_mode.screen_mode.0]; uiscreen } @@ -234,16 +234,16 @@ impl Inner { // this is pretty slow on iOS, so avoid doing it if we can let current: id = msg_send![self.window, screen]; if uiscreen != current { - let () = msg_send![self.window, setScreen: uiscreen]; + let _: () = msg_send![self.window, setScreen: uiscreen]; } let bounds: CGRect = msg_send![uiscreen, bounds]; - let () = msg_send![self.window, setFrame: bounds]; + let _: () = msg_send![self.window, setFrame: bounds]; // For external displays, we must disable overscan compensation or // the displayed image will have giant black bars surrounding it on // each side - let () = msg_send![ + let _: () = msg_send![ uiscreen, setOverscanCompensation: UIScreenOverscanCompensation::None ]; @@ -380,10 +380,10 @@ impl Window { window_attributes: WindowAttributes, platform_attributes: PlatformSpecificWindowBuilderAttributes, ) -> Result { - if let Some(_) = window_attributes.min_inner_size { + if window_attributes.min_inner_size.is_some() { warn!("`WindowAttributes::min_inner_size` is ignored on iOS"); } - if let Some(_) = window_attributes.max_inner_size { + if window_attributes.max_inner_size.is_some() { warn!("`WindowAttributes::max_inner_size` is ignored on iOS"); } if window_attributes.always_on_top { @@ -419,7 +419,7 @@ impl Window { None => screen_bounds, }; - let view = view::create_view(&window_attributes, &platform_attributes, frame.clone()); + let view = view::create_view(&window_attributes, &platform_attributes, frame); let gl_or_metal_backed = { let view_class: id = msg_send![view, class]; @@ -452,7 +452,7 @@ impl Window { // Like the Windows and macOS backends, we send a `ScaleFactorChanged` and `Resized` // event on window creation if the DPI factor != 1.0 let scale_factor: CGFloat = msg_send![view, contentScaleFactor]; - let scale_factor: f64 = scale_factor.into(); + let scale_factor = scale_factor as f64; if scale_factor != 1.0 { let bounds: CGRect = msg_send![view, bounds]; let screen: id = msg_send![window, screen]; @@ -502,7 +502,7 @@ impl Inner { "`WindowExtIOS::set_scale_factor` received an invalid hidpi factor" ); let scale_factor = scale_factor as CGFloat; - let () = msg_send![self.view, setContentScaleFactor: scale_factor]; + let _: () = msg_send![self.view, setContentScaleFactor: scale_factor]; } } @@ -523,7 +523,7 @@ impl Inner { pub fn set_prefers_home_indicator_hidden(&self, hidden: bool) { unsafe { let prefers_home_indicator_hidden = if hidden { YES } else { NO }; - let () = msg_send![ + let _: () = msg_send![ self.view_controller, setPrefersHomeIndicatorAutoHidden: prefers_home_indicator_hidden ]; @@ -533,7 +533,7 @@ impl Inner { pub fn set_preferred_screen_edges_deferring_system_gestures(&self, edges: ScreenEdge) { let edges: UIRectEdge = edges.into(); unsafe { - let () = msg_send![ + let _: () = msg_send![ self.view_controller, setPreferredScreenEdgesDeferringSystemGestures: edges ]; @@ -543,7 +543,7 @@ impl Inner { pub fn set_prefers_status_bar_hidden(&self, hidden: bool) { unsafe { let status_bar_hidden = if hidden { YES } else { NO }; - let () = msg_send![ + let _: () = msg_send![ self.view_controller, setPrefersStatusBarHidden: status_bar_hidden ]; @@ -554,11 +554,11 @@ impl Inner { impl Inner { // requires main thread unsafe fn screen_frame(&self) -> CGRect { - self.to_screen_space(msg_send![self.window, bounds]) + self.rect_to_screen_space(msg_send![self.window, bounds]) } // requires main thread - unsafe fn to_screen_space(&self, rect: CGRect) -> CGRect { + unsafe fn rect_to_screen_space(&self, rect: CGRect) -> CGRect { let screen: id = msg_send![self.window, screen]; if !screen.is_null() { let screen_space: id = msg_send![screen, coordinateSpace]; @@ -569,7 +569,7 @@ impl Inner { } // requires main thread - unsafe fn from_screen_space(&self, rect: CGRect) -> CGRect { + unsafe fn rect_from_screen_space(&self, rect: CGRect) -> CGRect { let screen: id = msg_send![self.window, screen]; if !screen.is_null() { let screen_space: id = msg_send![screen, coordinateSpace]; @@ -594,9 +594,9 @@ impl Inner { height: bounds.size.height - safe_area.top - safe_area.bottom, }, }; - self.to_screen_space(safe_bounds) + self.rect_to_screen_space(safe_bounds) } else { - let screen_frame = self.to_screen_space(bounds); + let screen_frame = self.rect_to_screen_space(bounds); let status_bar_frame: CGRect = { let app: id = msg_send![class!(UIApplication), sharedApplication]; assert!( diff --git a/src/platform_impl/linux/mod.rs b/src/platform_impl/linux/mod.rs index 4e5ac66293..b4c44de328 100644 --- a/src/platform_impl/linux/mod.rs +++ b/src/platform_impl/linux/mod.rs @@ -56,7 +56,7 @@ pub mod x11; /// If this variable is set with any other value, winit will panic. const BACKEND_PREFERENCE_ENV_VAR: &str = "WINIT_UNIX_BACKEND"; -#[derive(Debug, Copy, Clone, PartialEq, Hash)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub(crate) enum Backend { #[cfg(feature = "x11")] X, @@ -64,22 +64,13 @@ pub(crate) enum Backend { Wayland, } -#[derive(Debug, Copy, Clone, PartialEq, Hash)] +#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, Hash)] pub(crate) struct PlatformSpecificEventLoopAttributes { pub(crate) forced_backend: Option, pub(crate) any_thread: bool, } -impl Default for PlatformSpecificEventLoopAttributes { - fn default() -> Self { - Self { - forced_backend: None, - any_thread: false, - } - } -} - -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Eq)] pub struct ApplicationName { pub general: String, pub instance: String, diff --git a/src/platform_impl/linux/wayland/output.rs b/src/platform_impl/linux/wayland/output.rs index 79cc523f04..a01b0d83dc 100644 --- a/src/platform_impl/linux/wayland/output.rs +++ b/src/platform_impl/linux/wayland/output.rs @@ -175,7 +175,7 @@ impl MonitorHandle { #[inline] pub fn video_modes(&self) -> impl Iterator { let modes = sctk::output::with_output_info(&self.proxy, |info| info.modes.clone()) - .unwrap_or_else(Vec::new); + .unwrap_or_default(); let monitor = self.clone(); diff --git a/src/platform_impl/linux/wayland/seat/pointer/handlers.rs b/src/platform_impl/linux/wayland/seat/pointer/handlers.rs index 1967861dd0..54ca19394f 100644 --- a/src/platform_impl/linux/wayland/seat/pointer/handlers.rs +++ b/src/platform_impl/linux/wayland/seat/pointer/handlers.rs @@ -300,17 +300,17 @@ pub(super) fn handle_pointer( #[inline] pub(super) fn handle_relative_pointer(event: RelativePointerEvent, winit_state: &mut WinitState) { - match event { - RelativePointerEvent::RelativeMotion { - dx_unaccel, - dy_unaccel, - .. - } => winit_state.event_sink.push_device_event( + if let RelativePointerEvent::RelativeMotion { + dx_unaccel, + dy_unaccel, + .. + } = event + { + winit_state.event_sink.push_device_event( DeviceEvent::MouseMotion { delta: (dx_unaccel, dy_unaccel), }, DeviceId, - ), - _ => (), + ) } } diff --git a/src/platform_impl/linux/wayland/seat/pointer/mod.rs b/src/platform_impl/linux/wayland/seat/pointer/mod.rs index 7ec116405b..e1b34a5e80 100644 --- a/src/platform_impl/linux/wayland/seat/pointer/mod.rs +++ b/src/platform_impl/linux/wayland/seat/pointer/mod.rs @@ -243,7 +243,7 @@ pub(super) fn init_relative_pointer( relative_pointer_manager: &ZwpRelativePointerManagerV1, pointer: &WlPointer, ) -> ZwpRelativePointerV1 { - let relative_pointer = relative_pointer_manager.get_relative_pointer(&*pointer); + let relative_pointer = relative_pointer_manager.get_relative_pointer(pointer); relative_pointer.quick_assign(move |_, event, mut dispatch_data| { let winit_state = dispatch_data.get::().unwrap(); handlers::handle_relative_pointer(event, winit_state); diff --git a/src/platform_impl/linux/wayland/window/shim.rs b/src/platform_impl/linux/wayland/window/shim.rs index 9789d5dbf5..84d511f9fd 100644 --- a/src/platform_impl/linux/wayland/window/shim.rs +++ b/src/platform_impl/linux/wayland/window/shim.rs @@ -406,7 +406,8 @@ pub fn handle_window_requests(winit_state: &mut WinitState) { // Process the rest of the events. for (window_id, window_handle) in window_map.iter_mut() { let mut requests = window_handle.pending_window_requests.lock().unwrap(); - for request in requests.drain(..) { + let requests = requests.drain(..); + for request in requests { match request { WindowRequest::Fullscreen(fullscreen) => { window_handle.window.set_fullscreen(fullscreen.as_ref()); diff --git a/src/platform_impl/linux/x11/event_processor.rs b/src/platform_impl/linux/x11/event_processor.rs index 8ec326526c..1a024ac4e4 100644 --- a/src/platform_impl/linux/x11/event_processor.rs +++ b/src/platform_impl/linux/x11/event_processor.rs @@ -414,7 +414,7 @@ impl EventProcessor { // resizing by dragging across monitors *without* dropping the window. let (width, height) = shared_state_lock .dpi_adjusted - .unwrap_or_else(|| (xev.width as u32, xev.height as u32)); + .unwrap_or((xev.width as u32, xev.height as u32)); let last_scale_factor = shared_state_lock.last_monitor.scale_factor; let new_scale_factor = { @@ -1258,46 +1258,48 @@ impl EventProcessor { } } - match self.ime_event_receiver.try_recv() { - Ok((window, event)) => match event { - ImeEvent::Enabled => { - callback(Event::WindowEvent { - window_id: mkwid(window), - event: WindowEvent::Ime(Ime::Enabled), - }); - } - ImeEvent::Start => { - self.is_composing = true; - callback(Event::WindowEvent { - window_id: mkwid(window), - event: WindowEvent::Ime(Ime::Preedit("".to_owned(), None)), - }); - } - ImeEvent::Update(text, position) => { - if self.is_composing { - callback(Event::WindowEvent { - window_id: mkwid(window), - event: WindowEvent::Ime(Ime::Preedit(text, Some((position, position)))), - }); - } - } - ImeEvent::End => { - self.is_composing = false; - // Issue empty preedit on `Done`. - callback(Event::WindowEvent { - window_id: mkwid(window), - event: WindowEvent::Ime(Ime::Preedit(String::new(), None)), - }); - } - ImeEvent::Disabled => { - self.is_composing = false; + let (window, event) = match self.ime_event_receiver.try_recv() { + Ok((window, event)) => (window, event), + Err(_) => return, + }; + + match event { + ImeEvent::Enabled => { + callback(Event::WindowEvent { + window_id: mkwid(window), + event: WindowEvent::Ime(Ime::Enabled), + }); + } + ImeEvent::Start => { + self.is_composing = true; + callback(Event::WindowEvent { + window_id: mkwid(window), + event: WindowEvent::Ime(Ime::Preedit("".to_owned(), None)), + }); + } + ImeEvent::Update(text, position) => { + if self.is_composing { callback(Event::WindowEvent { window_id: mkwid(window), - event: WindowEvent::Ime(Ime::Disabled), + event: WindowEvent::Ime(Ime::Preedit(text, Some((position, position)))), }); } - }, - Err(_) => (), + } + ImeEvent::End => { + self.is_composing = false; + // Issue empty preedit on `Done`. + callback(Event::WindowEvent { + window_id: mkwid(window), + event: WindowEvent::Ime(Ime::Preedit(String::new(), None)), + }); + } + ImeEvent::Disabled => { + self.is_composing = false; + callback(Event::WindowEvent { + window_id: mkwid(window), + event: WindowEvent::Ime(Ime::Disabled), + }); + } } } diff --git a/src/platform_impl/linux/x11/ime/context.rs b/src/platform_impl/linux/x11/ime/context.rs index f34d4bad32..f0a751b745 100644 --- a/src/platform_impl/linux/x11/ime/context.rs +++ b/src/platform_impl/linux/x11/ime/context.rs @@ -65,12 +65,10 @@ extern "C" fn preedit_done_callback( .expect("failed to send preedit end event"); } -fn calc_byte_position(text: &Vec, pos: usize) -> usize { - let mut byte_pos = 0; - for i in 0..pos { - byte_pos += text[i].len_utf8(); - } - byte_pos +fn calc_byte_position(text: &[char], pos: usize) -> usize { + text.iter() + .take(pos) + .fold(0, |byte_pos, text| byte_pos + text.len_utf8()) } /// Preedit text information to be drawn inline by the client. diff --git a/src/platform_impl/linux/x11/mod.rs b/src/platform_impl/linux/x11/mod.rs index a38b9030ec..c8fc880f70 100644 --- a/src/platform_impl/linux/x11/mod.rs +++ b/src/platform_impl/linux/x11/mod.rs @@ -78,15 +78,16 @@ impl PeekableReceiver { if self.first.is_some() { return true; } + match self.recv.try_recv() { Ok(v) => { self.first = Some(v); - return true; + true } - Err(TryRecvError::Empty) => return false, + Err(TryRecvError::Empty) => false, Err(TryRecvError::Disconnected) => { warn!("Channel was disconnected when checking incoming"); - return false; + false } } } @@ -416,11 +417,12 @@ impl EventLoop { deadline = Some(*wait_deadline); } } - return IterationResult { + + IterationResult { wait_start: start, deadline, timeout, - }; + } } let mut control_flow = ControlFlow::default(); @@ -670,10 +672,7 @@ struct GenericEventCookie<'a> { } impl<'a> GenericEventCookie<'a> { - fn from_event<'b>( - xconn: &'b XConnection, - event: ffi::XEvent, - ) -> Option> { + fn from_event(xconn: &XConnection, event: ffi::XEvent) -> Option> { unsafe { let mut cookie: ffi::XGenericEventCookie = From::from(event); if (xconn.xlib.XGetEventData)(xconn.display, &mut cookie) == ffi::True { diff --git a/src/platform_impl/linux/x11/monitor.rs b/src/platform_impl/linux/x11/monitor.rs index 8249d83caf..9d3d3c8d73 100644 --- a/src/platform_impl/linux/x11/monitor.rs +++ b/src/platform_impl/linux/x11/monitor.rs @@ -229,11 +229,11 @@ impl XConnection { panic!("[winit] `XRRGetScreenResources` returned NULL. That should only happen if the root window doesn't exist."); } - let mut available; let mut has_primary = false; let primary = (self.xrandr.XRRGetOutputPrimary)(self.display, root); - available = Vec::with_capacity((*resources).ncrtc as usize); + let mut available = Vec::with_capacity((*resources).ncrtc as usize); + for crtc_index in 0..(*resources).ncrtc { let crtc_id = *((*resources).crtcs.offset(crtc_index as isize)); let crtc = (self.xrandr.XRRGetCrtcInfo)(self.display, resources, crtc_id); diff --git a/src/platform_impl/linux/x11/util/geometry.rs b/src/platform_impl/linux/x11/util/geometry.rs index 17aeca5584..8a0c8a5b96 100644 --- a/src/platform_impl/linux/x11/util/geometry.rs +++ b/src/platform_impl/linux/x11/util/geometry.rs @@ -98,7 +98,7 @@ pub struct LogicalFrameExtents { pub bottom: f64, } -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Eq)] pub enum FrameExtentsHeuristicPath { Supported, UnsupportedNested, @@ -370,8 +370,12 @@ impl XConnection { let top = offset_y; let bottom = diff_y.saturating_sub(offset_y); - let frame_extents = - FrameExtents::new(left.into(), right.into(), top.into(), bottom.into()); + let frame_extents = FrameExtents::new( + left as c_ulong, + right as c_ulong, + top as c_ulong, + bottom as c_ulong, + ); FrameExtentsHeuristic { frame_extents, heuristic_path: UnsupportedNested, @@ -379,7 +383,7 @@ impl XConnection { } else { // This is the case for xmonad and dwm, AKA the only WMs tested that supplied a // border value. This is convenient, since we can use it to get an accurate frame. - let frame_extents = FrameExtents::from_border(border.into()); + let frame_extents = FrameExtents::from_border(border as c_ulong); FrameExtentsHeuristic { frame_extents, heuristic_path: UnsupportedBordered, diff --git a/src/platform_impl/linux/x11/util/hint.rs b/src/platform_impl/linux/x11/util/hint.rs index 222d81748e..a2dbe6a170 100644 --- a/src/platform_impl/linux/x11/util/hint.rs +++ b/src/platform_impl/linux/x11/util/hint.rs @@ -173,6 +173,12 @@ impl MotifHints { } } +impl Default for MotifHints { + fn default() -> Self { + Self::new() + } +} + impl MwmHints { fn as_slice(&self) -> &[c_ulong] { unsafe { slice::from_raw_parts(self as *const _ as *const c_ulong, 5) } @@ -317,7 +323,7 @@ impl XConnection { let mut hints = MotifHints::new(); if let Ok(props) = self.get_property::(window, motif_hints, motif_hints) { - hints.hints.flags = props.get(0).cloned().unwrap_or(0); + hints.hints.flags = props.first().cloned().unwrap_or(0); hints.hints.functions = props.get(1).cloned().unwrap_or(0); hints.hints.decorations = props.get(2).cloned().unwrap_or(0); hints.hints.input_mode = props.get(3).cloned().unwrap_or(0) as c_long; diff --git a/src/platform_impl/linux/x11/util/icon.rs b/src/platform_impl/linux/x11/util/icon.rs index b0253e1b96..3240a9f896 100644 --- a/src/platform_impl/linux/x11/util/icon.rs +++ b/src/platform_impl/linux/x11/util/icon.rs @@ -1,3 +1,5 @@ +#![allow(clippy::assertions_on_constants)] + use super::*; use crate::icon::{Icon, Pixel, PIXEL_SIZE}; diff --git a/src/platform_impl/linux/x11/util/randr.rs b/src/platform_impl/linux/x11/util/randr.rs index cb380c7d41..3500152439 100644 --- a/src/platform_impl/linux/x11/util/randr.rs +++ b/src/platform_impl/linux/x11/util/randr.rs @@ -164,7 +164,9 @@ impl XConnection { (self.xrandr.XRRFreeOutputInfo)(output_info); Some((name, scale_factor, modes)) } - pub fn set_crtc_config(&self, crtc_id: RRCrtc, mode_id: RRMode) -> Result<(), ()> { + + #[must_use] + pub fn set_crtc_config(&self, crtc_id: RRCrtc, mode_id: RRMode) -> Option<()> { unsafe { let mut major = 0; let mut minor = 0; @@ -195,12 +197,13 @@ impl XConnection { (self.xrandr.XRRFreeScreenResources)(resources); if status == Success as i32 { - Ok(()) + Some(()) } else { - Err(()) + None } } } + pub fn get_crtc_mode(&self, crtc_id: RRCrtc) -> RRMode { unsafe { let mut major = 0; diff --git a/src/platform_impl/linux/x11/util/wm.rs b/src/platform_impl/linux/x11/util/wm.rs index 21fa790d73..89e9a0b8ad 100644 --- a/src/platform_impl/linux/x11/util/wm.rs +++ b/src/platform_impl/linux/x11/util/wm.rs @@ -60,7 +60,7 @@ impl XConnection { let root_window_wm_check = { let result = self.get_property(root, check_atom, ffi::XA_WINDOW); - let wm_check = result.ok().and_then(|wm_check| wm_check.get(0).cloned()); + let wm_check = result.ok().and_then(|wm_check| wm_check.first().cloned()); wm_check? }; @@ -70,7 +70,7 @@ impl XConnection { let child_window_wm_check = { let result = self.get_property(root_window_wm_check, check_atom, ffi::XA_WINDOW); - let wm_check = result.ok().and_then(|wm_check| wm_check.get(0).cloned()); + let wm_check = result.ok().and_then(|wm_check| wm_check.first().cloned()); wm_check? }; diff --git a/src/platform_impl/macos/app_state.rs b/src/platform_impl/macos/app_state.rs index 12b78e332e..98093963cc 100644 --- a/src/platform_impl/macos/app_state.rs +++ b/src/platform_impl/macos/app_state.rs @@ -401,7 +401,7 @@ impl AppState { let app: id = NSApp(); autoreleasepool(|| { - let () = msg_send![app, stop: nil]; + let _: () = msg_send![app, stop: nil]; // To stop event loop immediately, we need to post some event here. post_dummy_event(app); }); diff --git a/src/platform_impl/macos/event_loop.rs b/src/platform_impl/macos/event_loop.rs index cb069f8354..c39036997d 100644 --- a/src/platform_impl/macos/event_loop.rs +++ b/src/platform_impl/macos/event_loop.rs @@ -120,7 +120,7 @@ pub struct EventLoop { _callback: Option>>, } -#[derive(Debug, Copy, Clone, PartialEq, Hash)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub(crate) struct PlatformSpecificEventLoopAttributes { pub(crate) activation_policy: ActivationPolicy, pub(crate) default_menu: bool, @@ -213,7 +213,7 @@ impl EventLoop { mem::drop(callback); AppState::set_callback(weak_cb, Rc::clone(&self.window_target)); - let () = msg_send![app, run]; + let _: () = msg_send![app, run]; if let Some(panic) = self.panic_info.take() { drop(self._callback.take()); @@ -246,7 +246,7 @@ pub unsafe fn post_dummy_event(target: id) { data1: 0 as NSInteger data2: 0 as NSInteger ]; - let () = msg_send![target, postEvent: dummy_event atStart: YES]; + let _: () = msg_send![target, postEvent: dummy_event atStart: YES]; } /// Catches panics that happen inside `f` and when a panic @@ -270,7 +270,7 @@ pub fn stop_app_on_panic R + UnwindSafe, R>( unsafe { let app_class = class!(NSApplication); let app: id = msg_send![app_class, sharedApplication]; - let () = msg_send![app, stop: nil]; + let _: () = msg_send![app, stop: nil]; // Posting a dummy event to get `stop` to take effect immediately. // See: https://stackoverflow.com/questions/48041279/stopping-the-nsapplication-main-event-loop/48064752#48064752 diff --git a/src/platform_impl/macos/mod.rs b/src/platform_impl/macos/mod.rs index 50e438ae44..301c00039a 100644 --- a/src/platform_impl/macos/mod.rs +++ b/src/platform_impl/macos/mod.rs @@ -1,4 +1,5 @@ #![cfg(target_os = "macos")] +#![allow(clippy::let_unit_value)] #[macro_use] mod util; diff --git a/src/platform_impl/macos/util/async.rs b/src/platform_impl/macos/util/async.rs index afd62c2e9b..bf7e33ea72 100644 --- a/src/platform_impl/macos/util/async.rs +++ b/src/platform_impl/macos/util/async.rs @@ -167,11 +167,15 @@ pub unsafe fn set_maximized_async( shared_state_lock.maximized = maximized; - let curr_mask = ns_window.styleMask(); if shared_state_lock.fullscreen.is_some() { // Handle it in window_did_exit_fullscreen return; - } else if curr_mask.contains(NSWindowStyleMask::NSResizableWindowMask) { + } + + if ns_window + .styleMask() + .contains(NSWindowStyleMask::NSResizableWindowMask) + { // Just use the native zoom if resizable ns_window.zoom_(nil); } else { diff --git a/src/platform_impl/macos/util/mod.rs b/src/platform_impl/macos/util/mod.rs index 75ff703a3d..61c6f66e88 100644 --- a/src/platform_impl/macos/util/mod.rs +++ b/src/platform_impl/macos/util/mod.rs @@ -33,7 +33,7 @@ pub const EMPTY_RANGE: ffi::NSRange = ffi::NSRange { length: 0, }; -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Eq)] pub struct IdRef(id); impl IdRef { @@ -61,7 +61,7 @@ impl Drop for IdRef { fn drop(&mut self) { if self.0 != nil { unsafe { - let () = msg_send![self.0, release]; + let _: () = msg_send![self.0, release]; }; } } @@ -150,7 +150,7 @@ pub unsafe fn superclass(this: &Object) -> &Class { #[allow(dead_code)] pub unsafe fn open_emoji_picker() { - let () = msg_send![NSApp(), orderFrontCharacterPalette: nil]; + let _: () = msg_send![NSApp(), orderFrontCharacterPalette: nil]; } pub unsafe fn toggle_style_mask(window: id, view: id, mask: NSWindowStyleMask, on: bool) { diff --git a/src/platform_impl/macos/view.rs b/src/platform_impl/macos/view.rs index 94e3c73863..55e3f7dc41 100644 --- a/src/platform_impl/macos/view.rs +++ b/src/platform_impl/macos/view.rs @@ -84,11 +84,9 @@ impl ViewState { fn get_scale_factor(&self) -> f64 { (unsafe { NSWindow::backingScaleFactor(self.ns_window) }) as f64 } + fn is_ime_enabled(&self) -> bool { - match self.ime_state { - ImeState::Disabled => false, - _ => true, - } + !matches!(self.ime_state, ImeState::Disabled) } } @@ -422,7 +420,7 @@ extern "C" fn draw_rect(this: &Object, _sel: Sel, rect: NSRect) { AppState::handle_redraw(WindowId(get_window_id(state.ns_window))); let superclass = util::superclass(this); - let () = msg_send![super(this, superclass), drawRect: rect]; + let _: () = msg_send![super(this, superclass), drawRect: rect]; } } @@ -648,11 +646,9 @@ extern "C" fn do_command_by_selector(this: &Object, _sel: Sel, _command: Sel) { state.forward_key_to_app = true; let has_marked_text: BOOL = msg_send![this, hasMarkedText]; - if has_marked_text == NO { - if state.ime_state == ImeState::Preedit { - // Leave preedit so that we also report the keyup for this key - state.ime_state = ImeState::Enabled; - } + if has_marked_text == NO && state.ime_state == ImeState::Preedit { + // Leave preedit so that we also report the keyup for this key + state.ime_state = ImeState::Enabled; } } } @@ -900,7 +896,7 @@ extern "C" fn insert_tab(this: &Object, _sel: Sel, _sender: id) { let first_responder: id = msg_send![window, firstResponder]; let this_ptr = this as *const _ as *mut _; if first_responder == this_ptr { - let (): _ = msg_send![window, selectNextKeyView: this]; + let _: () = msg_send![window, selectNextKeyView: this]; } } } @@ -912,7 +908,7 @@ extern "C" fn insert_back_tab(this: &Object, _sel: Sel, _sender: id) { let first_responder: id = msg_send![window, firstResponder]; let this_ptr = this as *const _ as *mut _; if first_responder == this_ptr { - let (): _ = msg_send![window, selectPreviousKeyView: this]; + let _: () = msg_send![window, selectPreviousKeyView: this]; } } } diff --git a/src/platform_impl/macos/window.rs b/src/platform_impl/macos/window.rs index cd8ac3e695..145906c498 100644 --- a/src/platform_impl/macos/window.rs +++ b/src/platform_impl/macos/window.rs @@ -414,7 +414,7 @@ impl UnownedWindow { use cocoa::foundation::NSArray; // register for drag and drop operations. - let () = msg_send![ + let _: () = msg_send![ *ns_window, registerForDraggedTypes: NSArray::arrayWithObject(nil, appkit::NSFilenamesPboardType) @@ -951,7 +951,7 @@ impl UnownedWindow { | NSApplicationPresentationOptions::NSApplicationPresentationHideMenuBar; app.setPresentationOptions_(presentation_options); - let () = msg_send![*self.ns_window, setLevel: ffi::CGShieldingWindowLevel() + 1]; + let _: () = msg_send![*self.ns_window, setLevel: ffi::CGShieldingWindowLevel() + 1]; }, ( &Some(Fullscreen::Exclusive(RootVideoMode { ref video_mode })), @@ -969,7 +969,7 @@ impl UnownedWindow { // Restore the normal window level following the Borderless fullscreen // `CGShieldingWindowLevel() + 1` hack. - let () = msg_send![ + let _: () = msg_send![ *self.ns_window, setLevel: ffi::NSWindowLevel::NSNormalWindowLevel ]; diff --git a/src/platform_impl/macos/window_delegate.rs b/src/platform_impl/macos/window_delegate.rs index 08e78e094e..3a8d713f5b 100644 --- a/src/platform_impl/macos/window_delegate.rs +++ b/src/platform_impl/macos/window_delegate.rs @@ -247,7 +247,7 @@ extern "C" fn init_with_winit(this: &Object, _sel: Sel, state: *mut c_void) -> i if this != nil { (*this).set_ivar("winitState", state); with_state(&*this, |state| { - let () = msg_send![*state.ns_window, setDelegate: this]; + let _: () = msg_send![*state.ns_window, setDelegate: this]; }); } this @@ -267,7 +267,7 @@ extern "C" fn window_will_close(this: &Object, _: Sel, _: id) { autoreleasepool(|| { // Since El Capitan, we need to be careful that delegate methods can't // be called after the window closes. - let () = msg_send![*state.ns_window, setDelegate: nil]; + let _: () = msg_send![*state.ns_window, setDelegate: nil]; }); state.emit_event(WindowEvent::Destroyed); }); @@ -416,7 +416,8 @@ extern "C" fn window_will_enter_fullscreen(this: &Object, _: Sel, _: id) { state.with_window(|window| { let mut shared_state = window.lock_shared_state("window_will_enter_fullscreen"); shared_state.maximized = window.is_zoomed(); - match shared_state.fullscreen { + let fullscreen = shared_state.fullscreen.as_ref(); + match 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) @@ -541,12 +542,12 @@ extern "C" fn window_did_fail_to_enter_fullscreen(this: &Object, _: Sel, _: id) shared_state.target_fullscreen = None; }); if state.initial_fullscreen { - let _: () = unsafe { - msg_send![*state.ns_window, + unsafe { + let _: () = msg_send![*state.ns_window, performSelector:sel!(toggleFullScreen:) withObject:nil afterDelay: 0.5 - ] + ]; }; } else { state.with_window(|window| window.restore_state_from_fullscreen()); diff --git a/src/platform_impl/web/event_loop/mod.rs b/src/platform_impl/web/event_loop/mod.rs index d193094ea7..a8d234cf29 100644 --- a/src/platform_impl/web/event_loop/mod.rs +++ b/src/platform_impl/web/event_loop/mod.rs @@ -16,7 +16,7 @@ pub struct EventLoop { elw: RootEventLoopWindowTarget, } -#[derive(Default, Debug, Copy, Clone, PartialEq, Hash)] +#[derive(Default, Debug, Copy, Clone, PartialEq, Eq, Hash)] pub(crate) struct PlatformSpecificEventLoopAttributes {} impl EventLoop { diff --git a/src/platform_impl/web/event_loop/runner.rs b/src/platform_impl/web/event_loop/runner.rs index d476a4eeeb..840ba558e6 100644 --- a/src/platform_impl/web/event_loop/runner.rs +++ b/src/platform_impl/web/event_loop/runner.rs @@ -456,11 +456,8 @@ impl Shared { ControlFlow::ExitWithCode(_) => State::Exit, }; - match *self.0.runner.borrow_mut() { - RunnerEnum::Running(ref mut runner) => { - runner.state = new_state; - } - _ => (), + if let RunnerEnum::Running(ref mut runner) = *self.0.runner.borrow_mut() { + runner.state = new_state; } } diff --git a/src/platform_impl/web/event_loop/state.rs b/src/platform_impl/web/event_loop/state.rs index 16b6e6232c..d7c7fb65c6 100644 --- a/src/platform_impl/web/event_loop/state.rs +++ b/src/platform_impl/web/event_loop/state.rs @@ -22,10 +22,7 @@ pub enum State { impl State { pub fn is_exit(&self) -> bool { - match self { - State::Exit => true, - _ => false, - } + matches!(self, State::Exit) } pub fn control_flow(&self) -> ControlFlow { diff --git a/src/platform_impl/web/web_sys/canvas.rs b/src/platform_impl/web/web_sys/canvas.rs index 8d68e5aaab..83669b2dcd 100644 --- a/src/platform_impl/web/web_sys/canvas.rs +++ b/src/platform_impl/web/web_sys/canvas.rs @@ -44,11 +44,11 @@ impl Canvas { Some(canvas) => canvas, None => { let window = web_sys::window() - .ok_or(os_error!(OsError("Failed to obtain window".to_owned())))?; + .ok_or_else(|| os_error!(OsError("Failed to obtain window".to_owned())))?; let document = window .document() - .ok_or(os_error!(OsError("Failed to obtain document".to_owned())))?; + .ok_or_else(|| os_error!(OsError("Failed to obtain document".to_owned())))?; document .create_element("canvas") @@ -94,10 +94,10 @@ impl Canvas { self.raw().request_pointer_lock(); } else { let window = web_sys::window() - .ok_or(os_error!(OsError("Failed to obtain window".to_owned())))?; + .ok_or_else(|| os_error!(OsError("Failed to obtain window".to_owned())))?; let document = window .document() - .ok_or(os_error!(OsError("Failed to obtain document".to_owned())))?; + .ok_or_else(|| os_error!(OsError("Failed to obtain document".to_owned())))?; document.exit_pointer_lock(); } Ok(()) @@ -107,7 +107,7 @@ impl Canvas { self.common .raw .set_attribute(attribute, value) - .expect(&format!("Set attribute: {}", attribute)); + .unwrap_or_else(|err| panic!("error: {:?}\nSet attribute: {}", err, attribute)) } pub fn position(&self) -> LogicalPosition { @@ -339,9 +339,7 @@ impl Common { handler(event); }) as Box); - let listener = EventListenerHandle::new(&self.raw, event_name, closure); - - listener + EventListenerHandle::new(&self.raw, event_name, closure) } // The difference between add_event and add_user_event is that the latter has a special meaning diff --git a/src/platform_impl/web/web_sys/canvas/mouse_handler.rs b/src/platform_impl/web/web_sys/canvas/mouse_handler.rs index 81e2aead02..1effb455c7 100644 --- a/src/platform_impl/web/web_sys/canvas/mouse_handler.rs +++ b/src/platform_impl/web/web_sys/canvas/mouse_handler.rs @@ -8,6 +8,8 @@ use std::rc::Rc; use web_sys::{EventTarget, MouseEvent}; +type MouseLeaveHandler = Rc>>>; + #[allow(dead_code)] pub(super) struct MouseHandler { on_mouse_leave: Option>, @@ -15,7 +17,7 @@ pub(super) struct MouseHandler { on_mouse_move: Option>, on_mouse_press: Option>, on_mouse_release: Option>, - on_mouse_leave_handler: Rc>>>, + on_mouse_leave_handler: MouseLeaveHandler, mouse_capture_state: Rc>, } @@ -175,8 +177,8 @@ impl MouseHandler { .map_or(false, |target| target == EventTarget::from(canvas.clone())); match &*mouse_capture_state { // Don't handle hover events outside of canvas. - MouseCaptureState::NotCaptured if !is_over_canvas => return, - MouseCaptureState::OtherElement if !is_over_canvas => return, + MouseCaptureState::NotCaptured | MouseCaptureState::OtherElement + if !is_over_canvas => {} // If hovering over the canvas, just send the cursor move event. MouseCaptureState::NotCaptured | MouseCaptureState::OtherElement diff --git a/src/platform_impl/web/web_sys/media_query_handle.rs b/src/platform_impl/web/web_sys/media_query_handle.rs index c2ab40da77..dc9595617c 100644 --- a/src/platform_impl/web/web_sys/media_query_handle.rs +++ b/src/platform_impl/web/web_sys/media_query_handle.rs @@ -17,7 +17,7 @@ impl MediaQueryListHandle { .ok() .flatten() .and_then(|mql| { - mql.add_listener_with_opt_callback(Some(&listener.as_ref().unchecked_ref())) + mql.add_listener_with_opt_callback(Some(listener.as_ref().unchecked_ref())) .map(|_| mql) .ok() }); diff --git a/src/platform_impl/web/web_sys/mod.rs b/src/platform_impl/web/web_sys/mod.rs index 8057f7756a..5e2d6c3820 100644 --- a/src/platform_impl/web/web_sys/mod.rs +++ b/src/platform_impl/web/web_sys/mod.rs @@ -98,7 +98,7 @@ pub fn set_canvas_style_property(raw: &HtmlCanvasElement, property: &str, value: let style = raw.style(); style .set_property(property, value) - .expect(&format!("Failed to set {}", property)); + .unwrap_or_else(|err| panic!("error: {:?}\nFailed to set {}", err, property)) } pub fn is_fullscreen(canvas: &HtmlCanvasElement) -> bool { diff --git a/src/platform_impl/web/web_sys/scaling.rs b/src/platform_impl/web/web_sys/scaling.rs index 08b52ae306..7eb82c9fb5 100644 --- a/src/platform_impl/web/web_sys/scaling.rs +++ b/src/platform_impl/web/web_sys/scaling.rs @@ -65,13 +65,13 @@ impl ScaleChangeDetectorInternal { ); let mql = MediaQueryListHandle::new(&media_query, closure); if let Some(mql) = &mql { - assert_eq!(mql.mql().matches(), true); + assert!(mql.mql().matches()); } mql } fn handler(&mut self, event: MediaQueryListEvent) { - assert_eq!(event.matches(), false); + assert!(!event.matches()); let mql = self .mql .take() diff --git a/src/platform_impl/web/web_sys/timeout.rs b/src/platform_impl/web/web_sys/timeout.rs index e95c54ed51..1978cbb38d 100644 --- a/src/platform_impl/web/web_sys/timeout.rs +++ b/src/platform_impl/web/web_sys/timeout.rs @@ -21,7 +21,7 @@ impl Timeout { let handle = window .set_timeout_with_callback_and_timeout_and_arguments_0( - &closure.as_ref().unchecked_ref(), + closure.as_ref().unchecked_ref(), duration.as_millis() as i32, ) .expect("Failed to set timeout"); @@ -64,7 +64,7 @@ impl AnimationFrameRequest { }) as Box); let handle = window - .request_animation_frame(&closure.as_ref().unchecked_ref()) + .request_animation_frame(closure.as_ref().unchecked_ref()) .expect("Failed to request animation frame"); AnimationFrameRequest { diff --git a/src/platform_impl/web/window.rs b/src/platform_impl/web/window.rs index 6f5201b222..aa48569464 100644 --- a/src/platform_impl/web/window.rs +++ b/src/platform_impl/web/window.rs @@ -36,11 +36,11 @@ impl Window { let id = target.generate_id(); let canvas = backend::Canvas::create(platform_attr)?; - let mut canvas = Rc::new(RefCell::new(canvas)); + let canvas = Rc::new(RefCell::new(canvas)); let register_redraw_request = Box::new(move || runner.request_redraw(RootWI(id))); - target.register(&mut canvas, id); + target.register(&canvas, id); let runner = target.runner.clone(); let resize_notify_fn = Box::new(move |new_size| { @@ -77,7 +77,7 @@ impl Window { Ok(window) } - pub fn canvas<'a>(&'a self) -> Ref<'a, backend::Canvas> { + pub fn canvas(&self) -> Ref<'_, backend::Canvas> { self.canvas.borrow() } @@ -220,7 +220,7 @@ impl Window { self.canvas .borrow() .set_cursor_grab(grab) - .map_err(|e| ExternalError::Os(e)) + .map_err(ExternalError::Os) } #[inline] @@ -344,7 +344,7 @@ impl Window { #[inline] pub fn id(&self) -> WindowId { - return self.id; + self.id } #[inline] diff --git a/src/platform_impl/windows/event_loop.rs b/src/platform_impl/windows/event_loop.rs index b23f320346..ff6ad5f76c 100644 --- a/src/platform_impl/windows/event_loop.rs +++ b/src/platform_impl/windows/event_loop.rs @@ -1976,7 +1976,7 @@ unsafe fn public_window_callback_inner( false => old_physical_inner_size, }; - let _ = userdata.send_event(Event::WindowEvent { + userdata.send_event(Event::WindowEvent { window_id: RootWindowId(WindowId(window)), event: ScaleFactorChanged { scale_factor: new_scale_factor, diff --git a/src/platform_impl/windows/ime.rs b/src/platform_impl/windows/ime.rs index d6c5c79b27..6e6d31ea5e 100644 --- a/src/platform_impl/windows/ime.rs +++ b/src/platform_impl/windows/ime.rs @@ -80,19 +80,18 @@ impl ImeContext { let data = self.get_composition_data(gcs_mode)?; let (prefix, shorts, suffix) = data.align_to::(); if prefix.is_empty() && suffix.is_empty() { - OsString::from_wide(&shorts).into_string().ok() + OsString::from_wide(shorts).into_string().ok() } else { None } } unsafe fn get_composition_data(&self, gcs_mode: u32) -> Option> { - let size = ImmGetCompositionStringW(self.himc, gcs_mode, null_mut(), 0); - if size < 0 { - return None; - } else if size == 0 { - return Some(Vec::new()); - } + let size = match ImmGetCompositionStringW(self.himc, gcs_mode, null_mut(), 0) { + 0 => return Some(Vec::new()), + size if size < 0 => return None, + size => size, + }; let mut buf = Vec::::with_capacity(size as _); let size = ImmGetCompositionStringW( @@ -139,7 +138,7 @@ impl ImeContext { } unsafe fn system_has_ime() -> bool { - return GetSystemMetrics(SM_IMMENABLED) != 0; + GetSystemMetrics(SM_IMMENABLED) != 0 } } diff --git a/src/window.rs b/src/window.rs index 27184d4c76..485c720d2e 100644 --- a/src/window.rs +++ b/src/window.rs @@ -1126,7 +1126,7 @@ impl Default for CursorIcon { } /// Fullscreen modes. -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug, PartialEq, Eq)] pub enum Fullscreen { Exclusive(VideoMode), @@ -1134,7 +1134,7 @@ pub enum Fullscreen { Borderless(Option), } -#[derive(Clone, Copy, Debug, PartialEq)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum Theme { Light, Dark, @@ -1143,7 +1143,7 @@ pub enum Theme { /// ## Platform-specific /// /// - **X11:** Sets the WM's `XUrgencyHint`. No distinction between `Critical` and `Informational`. -#[derive(Debug, Clone, Copy, PartialEq)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum UserAttentionType { /// ## Platform-specific /// - **macOS:** Bounces the dock icon until the application is in focus. From eec84ade86dc6e30dabc79ff0b279402a95cc6ba Mon Sep 17 00:00:00 2001 From: Kirill Chibisov Date: Fri, 10 Jun 2022 15:39:02 +0300 Subject: [PATCH 10/60] Make `set_device_event_filter` non-mut Commit f10a984 added `EventLoopWindowTarget::set_device_event_filter` with for a mutable reference, however most winit APIs work with immutable references, so altering API to play nicely with existing APIs. This also disables device event filtering on debug example. --- examples/window_debug.rs | 4 +++- src/event_loop.rs | 2 +- src/platform_impl/linux/mod.rs | 4 ++-- src/platform_impl/linux/x11/mod.rs | 12 ++++++------ 4 files changed, 12 insertions(+), 10 deletions(-) diff --git a/examples/window_debug.rs b/examples/window_debug.rs index be1d251956..47a130178c 100644 --- a/examples/window_debug.rs +++ b/examples/window_debug.rs @@ -6,7 +6,7 @@ use simple_logger::SimpleLogger; use winit::{ dpi::{LogicalSize, PhysicalSize}, event::{DeviceEvent, ElementState, Event, KeyboardInput, VirtualKeyCode, WindowEvent}, - event_loop::EventLoop, + event_loop::{DeviceEventFilter, EventLoop}, window::{Fullscreen, WindowBuilder}, }; @@ -32,6 +32,8 @@ fn main() { let mut minimized = false; let mut visible = true; + event_loop.set_device_event_filter(DeviceEventFilter::Never); + event_loop.run(move |event, _, control_flow| { control_flow.set_wait(); diff --git a/src/event_loop.rs b/src/event_loop.rs index d972962766..b768b643c9 100644 --- a/src/event_loop.rs +++ b/src/event_loop.rs @@ -306,7 +306,7 @@ impl EventLoopWindowTarget { /// - **Wayland / Windows / macOS / iOS / Android / Web**: Unsupported. /// /// [`DeviceEvent`]: crate::event::DeviceEvent - pub fn set_device_event_filter(&mut self, _filter: DeviceEventFilter) { + pub fn set_device_event_filter(&self, _filter: DeviceEventFilter) { #[cfg(any( target_os = "linux", target_os = "dragonfly", diff --git a/src/platform_impl/linux/mod.rs b/src/platform_impl/linux/mod.rs index b4c44de328..6263d1168a 100644 --- a/src/platform_impl/linux/mod.rs +++ b/src/platform_impl/linux/mod.rs @@ -788,12 +788,12 @@ impl EventLoopWindowTarget { } #[inline] - pub fn set_device_event_filter(&mut self, _filter: DeviceEventFilter) { + pub fn set_device_event_filter(&self, _filter: DeviceEventFilter) { match *self { #[cfg(feature = "wayland")] EventLoopWindowTarget::Wayland(_) => (), #[cfg(feature = "x11")] - EventLoopWindowTarget::X(ref mut evlp) => evlp.set_device_event_filter(_filter), + EventLoopWindowTarget::X(ref evlp) => evlp.set_device_event_filter(_filter), } } } diff --git a/src/platform_impl/linux/x11/mod.rs b/src/platform_impl/linux/x11/mod.rs index c8fc880f70..c2bc4edc5d 100644 --- a/src/platform_impl/linux/x11/mod.rs +++ b/src/platform_impl/linux/x11/mod.rs @@ -23,7 +23,7 @@ pub use self::{ }; use std::{ - cell::RefCell, + cell::{Cell, RefCell}, collections::{HashMap, HashSet}, ffi::CStr, mem::{self, MaybeUninit}, @@ -108,7 +108,7 @@ pub struct EventLoopWindowTarget { ime: RefCell, windows: RefCell>>, redraw_sender: WakeSender, - device_event_filter: DeviceEventFilter, + device_event_filter: Cell, _marker: ::std::marker::PhantomData, } @@ -536,14 +536,14 @@ impl EventLoopWindowTarget { &self.xconn } - pub fn set_device_event_filter(&mut self, filter: DeviceEventFilter) { - self.device_event_filter = filter; + pub fn set_device_event_filter(&self, filter: DeviceEventFilter) { + self.device_event_filter.set(filter); } /// Update the device event filter based on window focus. pub fn update_device_event_filter(&self, focus: bool) { - let filter_events = self.device_event_filter == DeviceEventFilter::Never - || (self.device_event_filter == DeviceEventFilter::Unfocused && !focus); + let filter_events = self.device_event_filter.get() == DeviceEventFilter::Never + || (self.device_event_filter.get() == DeviceEventFilter::Unfocused && !focus); let mut mask = 0; if !filter_events { From 44288f62800a9ebabcf770ddf20279b131fd58d0 Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Fri, 10 Jun 2022 19:05:28 +0200 Subject: [PATCH 11/60] Make `WindowAttributes` private (#2134) * Make `WindowAttributes` private, and move its documentation * Reorder WindowAttributes title and fullscreen to match method order --- CHANGELOG.md | 1 + src/platform_impl/android/mod.rs | 2 +- src/platform_impl/ios/view.rs | 6 +- src/platform_impl/ios/window.rs | 2 +- src/platform_impl/linux/mod.rs | 2 +- src/platform_impl/linux/wayland/window/mod.rs | 2 +- src/platform_impl/linux/x11/mod.rs | 2 +- src/platform_impl/linux/x11/window.rs | 2 +- src/platform_impl/macos/mod.rs | 2 +- src/platform_impl/macos/window.rs | 2 +- src/platform_impl/web/window.rs | 2 +- src/platform_impl/windows/window.rs | 2 +- src/platform_impl/windows/window_state.rs | 2 +- src/window.rs | 143 ++++++++---------- 14 files changed, 74 insertions(+), 98 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b182f9f80d..9e13902286 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ And please only add new entries to the top of this list, right below the `# Unre # Unreleased +- **Breaking:** Removed the `WindowAttributes` struct, since all its functionality is accessible from `WindowBuilder`. - On macOS, Fix emitting `Event::LoopDestroyed` on CMD+Q. - On macOS, fixed an issue where having multiple windows would prevent run_return from ever returning. - On Wayland, fix bug where the cursor wouldn't hide in GNOME. diff --git a/src/platform_impl/android/mod.rs b/src/platform_impl/android/mod.rs index e8d79a5d97..c472e10723 100644 --- a/src/platform_impl/android/mod.rs +++ b/src/platform_impl/android/mod.rs @@ -641,7 +641,7 @@ pub struct PlatformSpecificWindowBuilderAttributes; pub struct Window; impl Window { - pub fn new( + pub(crate) fn new( _el: &EventLoopWindowTarget, _window_attrs: window::WindowAttributes, _: PlatformSpecificWindowBuilderAttributes, diff --git a/src/platform_impl/ios/view.rs b/src/platform_impl/ios/view.rs index e92b119b61..b4cb4225f1 100644 --- a/src/platform_impl/ios/view.rs +++ b/src/platform_impl/ios/view.rs @@ -429,7 +429,7 @@ unsafe fn get_window_class() -> &'static Class { } // requires main thread -pub unsafe fn create_view( +pub(crate) unsafe fn create_view( _window_attributes: &WindowAttributes, platform_attributes: &PlatformSpecificWindowBuilderAttributes, frame: CGRect, @@ -449,7 +449,7 @@ pub unsafe fn create_view( } // requires main thread -pub unsafe fn create_view_controller( +pub(crate) unsafe fn create_view_controller( _window_attributes: &WindowAttributes, platform_attributes: &PlatformSpecificWindowBuilderAttributes, view: id, @@ -505,7 +505,7 @@ pub unsafe fn create_view_controller( } // requires main thread -pub unsafe fn create_window( +pub(crate) unsafe fn create_window( window_attributes: &WindowAttributes, _platform_attributes: &PlatformSpecificWindowBuilderAttributes, frame: CGRect, diff --git a/src/platform_impl/ios/window.rs b/src/platform_impl/ios/window.rs index 7e3e0eaac7..b597f93486 100644 --- a/src/platform_impl/ios/window.rs +++ b/src/platform_impl/ios/window.rs @@ -375,7 +375,7 @@ impl DerefMut for Window { } impl Window { - pub fn new( + pub(crate) fn new( _event_loop: &EventLoopWindowTarget, window_attributes: WindowAttributes, platform_attributes: PlatformSpecificWindowBuilderAttributes, diff --git a/src/platform_impl/linux/mod.rs b/src/platform_impl/linux/mod.rs index 6263d1168a..919d4d740b 100644 --- a/src/platform_impl/linux/mod.rs +++ b/src/platform_impl/linux/mod.rs @@ -295,7 +295,7 @@ impl VideoMode { impl Window { #[inline] - pub fn new( + pub(crate) fn new( window_target: &EventLoopWindowTarget, attribs: WindowAttributes, pl_attribs: PlatformSpecificWindowBuilderAttributes, diff --git a/src/platform_impl/linux/wayland/window/mod.rs b/src/platform_impl/linux/wayland/window/mod.rs index 6cb3daed75..f9c1079785 100644 --- a/src/platform_impl/linux/wayland/window/mod.rs +++ b/src/platform_impl/linux/wayland/window/mod.rs @@ -75,7 +75,7 @@ pub struct Window { } impl Window { - pub fn new( + pub(crate) fn new( event_loop_window_target: &EventLoopWindowTarget, attributes: WindowAttributes, platform_attributes: PlatformAttributes, diff --git a/src/platform_impl/linux/x11/mod.rs b/src/platform_impl/linux/x11/mod.rs index c2bc4edc5d..966d526188 100644 --- a/src/platform_impl/linux/x11/mod.rs +++ b/src/platform_impl/linux/x11/mod.rs @@ -638,7 +638,7 @@ impl Deref for Window { } impl Window { - pub fn new( + pub(crate) fn new( event_loop: &EventLoopWindowTarget, attribs: WindowAttributes, pl_attribs: PlatformSpecificWindowBuilderAttributes, diff --git a/src/platform_impl/linux/x11/window.rs b/src/platform_impl/linux/x11/window.rs index fb2047cc5a..89ac8e46c6 100644 --- a/src/platform_impl/linux/x11/window.rs +++ b/src/platform_impl/linux/x11/window.rs @@ -114,7 +114,7 @@ pub struct UnownedWindow { } impl UnownedWindow { - pub fn new( + pub(crate) fn new( event_loop: &EventLoopWindowTarget, window_attrs: WindowAttributes, pl_attribs: PlatformSpecificWindowBuilderAttributes, diff --git a/src/platform_impl/macos/mod.rs b/src/platform_impl/macos/mod.rs index 301c00039a..4608e2fa42 100644 --- a/src/platform_impl/macos/mod.rs +++ b/src/platform_impl/macos/mod.rs @@ -70,7 +70,7 @@ impl Deref for Window { } impl Window { - pub fn new( + pub(crate) fn new( _window_target: &EventLoopWindowTarget, attributes: WindowAttributes, pl_attribs: PlatformSpecificWindowBuilderAttributes, diff --git a/src/platform_impl/macos/window.rs b/src/platform_impl/macos/window.rs index 145906c498..dde004ac5c 100644 --- a/src/platform_impl/macos/window.rs +++ b/src/platform_impl/macos/window.rs @@ -371,7 +371,7 @@ unsafe impl Send for UnownedWindow {} unsafe impl Sync for UnownedWindow {} impl UnownedWindow { - pub fn new( + pub(crate) fn new( mut win_attribs: WindowAttributes, pl_attribs: PlatformSpecificWindowBuilderAttributes, ) -> Result<(Arc, IdRef), RootOsError> { diff --git a/src/platform_impl/web/window.rs b/src/platform_impl/web/window.rs index aa48569464..bd5791cbb5 100644 --- a/src/platform_impl/web/window.rs +++ b/src/platform_impl/web/window.rs @@ -26,7 +26,7 @@ pub struct Window { } impl Window { - pub fn new( + pub(crate) fn new( target: &EventLoopWindowTarget, attr: WindowAttributes, platform_attr: PlatformSpecificWindowBuilderAttributes, diff --git a/src/platform_impl/windows/window.rs b/src/platform_impl/windows/window.rs index 29c4d3898d..3e0d98fd5a 100644 --- a/src/platform_impl/windows/window.rs +++ b/src/platform_impl/windows/window.rs @@ -85,7 +85,7 @@ pub struct Window { } impl Window { - pub fn new( + pub(crate) fn new( event_loop: &EventLoopWindowTarget, w_attr: WindowAttributes, pl_attr: PlatformSpecificWindowBuilderAttributes, diff --git a/src/platform_impl/windows/window_state.rs b/src/platform_impl/windows/window_state.rs index ed71204f73..77f8dcda2f 100644 --- a/src/platform_impl/windows/window_state.rs +++ b/src/platform_impl/windows/window_state.rs @@ -112,7 +112,7 @@ pub enum ImeState { } impl WindowState { - pub fn new( + pub(crate) fn new( attributes: &WindowAttributes, taskbar_icon: Option, scale_factor: f64, diff --git a/src/window.rs b/src/window.rs index 485c720d2e..2f8cbbf50c 100644 --- a/src/window.rs +++ b/src/window.rs @@ -88,7 +88,7 @@ impl WindowId { #[must_use] pub struct WindowBuilder { /// The attributes to use to create the window. - pub window: WindowAttributes, + pub(crate) window: WindowAttributes, // Platform-specific configuration. pub(crate) platform_specific: platform_impl::PlatformSpecificWindowBuilderAttributes, @@ -104,92 +104,19 @@ impl fmt::Debug for WindowBuilder { /// Attributes to use when creating a window. #[derive(Debug, Clone)] -pub struct WindowAttributes { - /// The dimensions of the window. If this is `None`, some platform-specific dimensions will be - /// used. - /// - /// The default is `None`. +pub(crate) struct WindowAttributes { pub inner_size: Option, - - /// The minimum dimensions a window can be, If this is `None`, the window will have no minimum dimensions (aside from reserved). - /// - /// The default is `None`. pub min_inner_size: Option, - - /// The maximum dimensions a window can be, If this is `None`, the maximum will have no maximum or will be set to the primary monitor's dimensions by the platform. - /// - /// The default is `None`. pub max_inner_size: Option, - - /// The desired position of the window. If this is `None`, some platform-specific position - /// will be chosen. - /// - /// The default is `None`. - /// - /// ## Platform-specific - /// - /// - **macOS**: The top left corner position of the window content, the window's "inner" - /// position. The window title bar will be placed above it. - /// The window will be positioned such that it fits on screen, maintaining - /// set `inner_size` if any. - /// If you need to precisely position the top left corner of the whole window you have to - /// use [`Window::set_outer_position`] after creating the window. - /// - **Windows**: The top left corner position of the window title bar, the window's "outer" - /// position. - /// There may be a small gap between this position and the window due to the specifics of the - /// Window Manager. - /// - **X11**: The top left corner of the window, the window's "outer" position. - /// - **Others**: Ignored. - /// - /// See [`Window::set_outer_position`]. - /// - /// [`Window::set_outer_position`]: crate::window::Window::set_outer_position pub position: Option, - - /// Whether the window is resizable or not. - /// - /// The default is `true`. pub resizable: bool, - - /// Whether the window should be set as fullscreen upon creation. - /// - /// The default is `None`. - pub fullscreen: Option, - - /// The title of the window in the title bar. - /// - /// The default is `"winit window"`. pub title: String, - - /// Whether the window should be maximized upon creation. - /// - /// The default is `false`. + pub fullscreen: Option, pub maximized: bool, - - /// Whether the window should be immediately visible upon creation. - /// - /// The default is `true`. pub visible: bool, - - /// Whether the the window should be transparent. If this is true, writing colors - /// with alpha values different than `1.0` will produce a transparent window. - /// - /// The default is `false`. pub transparent: bool, - - /// Whether the window should have borders and bars. - /// - /// The default is `true`. pub decorations: bool, - - /// Whether the window should always be on top of other windows. - /// - /// The default is `false`. pub always_on_top: bool, - - /// The window icon. - /// - /// The default is `None`. pub window_icon: Option, } @@ -223,6 +150,8 @@ impl WindowBuilder { /// Requests the window to be of specific dimensions. /// + /// If this is not set, some platform-specific dimensions will be used. + /// /// See [`Window::set_inner_size`] for details. /// /// [`Window::set_inner_size`]: crate::window::Window::set_inner_size @@ -232,7 +161,10 @@ impl WindowBuilder { self } - /// Sets a minimum dimension size for the window. + /// Sets the minimum dimensions a window can have. + /// + /// If this is not set, the window will have no minimum dimensions (aside + /// from reserved). /// /// See [`Window::set_min_inner_size`] for details. /// @@ -243,7 +175,10 @@ impl WindowBuilder { self } - /// Sets a maximum dimension size for the window. + /// Sets the maximum dimensions a window can have. + /// + /// If this is not set, the window will have no maximum or will be set to + /// the primary monitor's dimensions by the platform. /// /// See [`Window::set_max_inner_size`] for details. /// @@ -256,9 +191,28 @@ impl WindowBuilder { /// Sets a desired initial position for the window. /// - /// See [`WindowAttributes::position`] for details. + /// If this is not set, some platform-specific position will be chosen. + /// + /// See [`Window::set_outer_position`] for details. + /// + /// ## Platform-specific + /// + /// - **macOS**: The top left corner position of the window content, the + /// window's "inner" position. The window title bar will be placed above + /// it. The window will be positioned such that it fits on screen, + /// maintaining set `inner_size` if any. + /// If you need to precisely position the top left corner of the whole + /// window you have to use [`Window::set_outer_position`] after creating + /// the window. + /// - **Windows**: The top left corner position of the window title bar, + /// the window's "outer" position. + /// There may be a small gap between this position and the window due to + /// the specifics of the Window Manager. + /// - **X11**: The top left corner of the window, the window's "outer" + /// position. + /// - **Others**: Ignored. /// - /// [`WindowAttributes::position`]: crate::window::WindowAttributes::position + /// [`Window::set_outer_position`]: crate::window::Window::set_outer_position #[inline] pub fn with_position>(mut self, position: P) -> Self { self.window.position = Some(position.into()); @@ -267,6 +221,8 @@ impl WindowBuilder { /// Sets whether the window is resizable or not. /// + /// The default is `true`. + /// /// See [`Window::set_resizable`] for details. /// /// [`Window::set_resizable`]: crate::window::Window::set_resizable @@ -276,7 +232,9 @@ impl WindowBuilder { self } - /// Requests a specific title for the window. + /// Sets the initial title of the window in the title bar. + /// + /// The default is `"winit window"`. /// /// See [`Window::set_title`] for details. /// @@ -287,7 +245,9 @@ impl WindowBuilder { self } - /// Sets the window fullscreen state. + /// Sets whether the window should be put into fullscreen upon creation. + /// + /// The default is `None`. /// /// See [`Window::set_fullscreen`] for details. /// @@ -298,7 +258,9 @@ impl WindowBuilder { self } - /// Requests maximized mode. + /// Request that the window is maximized upon creation. + /// + /// The default is `false`. /// /// See [`Window::set_maximized`] for details. /// @@ -309,7 +271,9 @@ impl WindowBuilder { self } - /// Sets whether the window will be initially hidden or visible. + /// Sets whether the window will be initially visible or hidden. + /// + /// The default is to show the window. /// /// See [`Window::set_visible`] for details. /// @@ -321,6 +285,11 @@ impl WindowBuilder { } /// Sets whether the background of the window should be transparent. + /// + /// If this is `true`, writing colors with alpha values different than + /// `1.0` will produce a transparent window. + /// + /// The default is `false`. #[inline] pub fn with_transparent(mut self, transparent: bool) -> Self { self.window.transparent = transparent; @@ -329,6 +298,8 @@ impl WindowBuilder { /// Sets whether the window should have a border, a title bar, etc. /// + /// The default is `true`. + /// /// See [`Window::set_decorations`] for details. /// /// [`Window::set_decorations`]: crate::window::Window::set_decorations @@ -340,6 +311,8 @@ impl WindowBuilder { /// Sets whether or not the window will always be on top of other windows. /// + /// The default is `false`. + /// /// See [`Window::set_always_on_top`] for details. /// /// [`Window::set_always_on_top`]: crate::window::Window::set_always_on_top @@ -351,6 +324,8 @@ impl WindowBuilder { /// Sets the window icon. /// + /// The default is `None`. + /// /// See [`Window::set_window_icon`] for details. /// /// [`Window::set_window_icon`]: crate::window::Window::set_window_icon From c532d910c09f7c41ab0c106cfb63c068d7de7229 Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Sat, 11 Jun 2022 00:45:24 +0200 Subject: [PATCH 12/60] Build docs on `docs.rs` for iOS and Android as well (#2324) --- .github/workflows/ci.yml | 2 +- CHANGELOG.md | 1 + Cargo.toml | 18 +++++++++++++++++- 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fb7eb20364..843632add7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,6 +24,7 @@ jobs: matrix: rust_version: [stable, nightly] platform: + # Note: Make sure that we test all the `docs.rs` targets defined in Cargo.toml! - { target: x86_64-pc-windows-msvc, os: windows-latest, } - { target: i686-pc-windows-msvc, os: windows-latest, } - { target: x86_64-pc-windows-gnu, os: windows-latest, host: -x86_64-pc-windows-gnu } @@ -77,7 +78,6 @@ jobs: - name: Check documentation shell: bash - if: matrix.platform.target != 'wasm32-unknown-unknown' run: cargo $CMD doc --no-deps --target ${{ matrix.platform.target }} $OPTIONS --features $FEATURES - name: Build diff --git a/CHANGELOG.md b/CHANGELOG.md index 9e13902286..5d103cdb8e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ And please only add new entries to the top of this list, right below the `# Unre # Unreleased +- Build docs on `docs.rs` for iOS and Android as well. - **Breaking:** Removed the `WindowAttributes` struct, since all its functionality is accessible from `WindowBuilder`. - On macOS, Fix emitting `Event::LoopDestroyed` on CMD+Q. - On macOS, fixed an issue where having multiple windows would prevent run_return from ever returning. diff --git a/Cargo.toml b/Cargo.toml index d98a6bc1c2..e7a45ce0a4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,7 +14,23 @@ categories = ["gui"] [package.metadata.docs.rs] features = ["serde"] default-target = "x86_64-unknown-linux-gnu" -targets = ["i686-pc-windows-msvc", "x86_64-pc-windows-msvc", "i686-unknown-linux-gnu", "x86_64-unknown-linux-gnu", "x86_64-apple-darwin", "wasm32-unknown-unknown"] +# These are all tested in CI +targets = [ + # Windows + "i686-pc-windows-msvc", + "x86_64-pc-windows-msvc", + # macOS + "x86_64-apple-darwin", + # Unix (X11 & Wayland) + "i686-unknown-linux-gnu", + "x86_64-unknown-linux-gnu", + # iOS + "x86_64-apple-ios", + # Android + "aarch64-linux-android", + # WebAssembly + "wasm32-unknown-unknown", +] [features] default = ["x11", "wayland", "wayland-dlopen", "wayland-csd-adwaita"] From 40abb526cc58e3315335fb490ff2b5cee1a90e8c Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Sat, 11 Jun 2022 02:37:46 +0200 Subject: [PATCH 13/60] Remove core-video-sys dependency (#2326) Hasn't been updated in over 2 years - many open PRs, seems abandoned. Is the cause of several duplicate dependencies in our dependency tree! --- Cargo.toml | 5 ---- src/platform_impl/macos/ffi.rs | 42 ++++++++++++++++++++++++++++++ src/platform_impl/macos/monitor.rs | 16 +++++------- 3 files changed, 48 insertions(+), 15 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index e7a45ce0a4..8709e748b6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -67,11 +67,6 @@ core-foundation = "0.9" core-graphics = "0.22" dispatch = "0.2.0" -[target.'cfg(target_os = "macos")'.dependencies.core-video-sys] -version = "0.1.4" -default_features = false -features = ["display_link"] - [target.'cfg(target_os = "windows")'.dependencies] parking_lot = "0.12" diff --git a/src/platform_impl/macos/ffi.rs b/src/platform_impl/macos/ffi.rs index 229bd6598d..0a23e60d41 100644 --- a/src/platform_impl/macos/ffi.rs +++ b/src/platform_impl/macos/ffi.rs @@ -221,3 +221,45 @@ extern "C" { pub fn CGDisplayModeRetain(mode: CGDisplayModeRef); pub fn CGDisplayModeRelease(mode: CGDisplayModeRef); } + +mod core_video { + use super::*; + + #[link(name = "CoreVideo", kind = "framework")] + extern "C" {} + + // CVBase.h + + pub type CVTimeFlags = i32; // int32_t + pub const kCVTimeIsIndefinite: CVTimeFlags = 1 << 0; + + #[repr(C)] + #[derive(Debug, Clone)] + pub struct CVTime { + pub time_value: i64, // int64_t + pub time_scale: i32, // int32_t + pub flags: i32, // int32_t + } + + // CVReturn.h + + pub type CVReturn = i32; // int32_t + pub const kCVReturnSuccess: CVReturn = 0; + + // CVDisplayLink.h + + pub type CVDisplayLinkRef = *mut c_void; + + extern "C" { + pub fn CVDisplayLinkCreateWithCGDisplay( + displayID: CGDirectDisplayID, + displayLinkOut: *mut CVDisplayLinkRef, + ) -> CVReturn; + pub fn CVDisplayLinkGetNominalOutputVideoRefreshPeriod( + displayLink: CVDisplayLinkRef, + ) -> CVTime; + pub fn CVDisplayLinkRelease(displayLink: CVDisplayLinkRef); + } +} + +pub use core_video::*; diff --git a/src/platform_impl/macos/monitor.rs b/src/platform_impl/macos/monitor.rs index 08dba6c9f9..d421951e1d 100644 --- a/src/platform_impl/macos/monitor.rs +++ b/src/platform_impl/macos/monitor.rs @@ -16,10 +16,6 @@ use core_foundation::{ string::CFString, }; use core_graphics::display::{CGDirectDisplayID, CGDisplay, CGDisplayBounds}; -use core_video_sys::{ - kCVReturnSuccess, kCVTimeIsIndefinite, CVDisplayLinkCreateWithCGDisplay, - CVDisplayLinkGetNominalOutputVideoRefreshPeriod, CVDisplayLinkRelease, -}; #[derive(Clone)] pub struct VideoMode { @@ -228,16 +224,16 @@ impl MonitorHandle { let cv_refresh_rate = unsafe { let mut display_link = std::ptr::null_mut(); assert_eq!( - CVDisplayLinkCreateWithCGDisplay(self.0, &mut display_link), - kCVReturnSuccess + ffi::CVDisplayLinkCreateWithCGDisplay(self.0, &mut display_link), + ffi::kCVReturnSuccess ); - let time = CVDisplayLinkGetNominalOutputVideoRefreshPeriod(display_link); - CVDisplayLinkRelease(display_link); + let time = ffi::CVDisplayLinkGetNominalOutputVideoRefreshPeriod(display_link); + ffi::CVDisplayLinkRelease(display_link); // This value is indefinite if an invalid display link was specified - assert!(time.flags & kCVTimeIsIndefinite == 0); + assert!(time.flags & ffi::kCVTimeIsIndefinite == 0); - time.timeScale as i64 / time.timeValue + time.time_scale as i64 / time.time_value }; let monitor = self.clone(); From 6474891f1e04d66704e3ed84ee585cd8b02ba19f Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Sat, 11 Jun 2022 03:43:51 +0200 Subject: [PATCH 14/60] Fix macOS 32bit (#2327) --- src/platform_impl/macos/util/mod.rs | 8 ++++---- src/platform_impl/macos/view.rs | 2 +- src/platform_impl/macos/window.rs | 10 ++++++---- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/platform_impl/macos/util/mod.rs b/src/platform_impl/macos/util/mod.rs index 61c6f66e88..859bdb9c9d 100644 --- a/src/platform_impl/macos/util/mod.rs +++ b/src/platform_impl/macos/util/mod.rs @@ -7,7 +7,7 @@ use std::ops::{BitAnd, Deref}; use std::os::raw::c_uchar; use cocoa::{ - appkit::{NSApp, NSWindowStyleMask}, + appkit::{CGFloat, NSApp, NSWindowStyleMask}, base::{id, nil}, foundation::{NSPoint, NSRect, NSString, NSUInteger}, }; @@ -113,7 +113,7 @@ impl Drop for TraceGuard { // 1. translate the bottom-left window corner into the top-left window corner // 2. translate the coordinate from a bottom-left origin coordinate system to a top-left one pub fn bottom_left_to_top_left(rect: NSRect) -> f64 { - CGDisplay::main().pixels_high() as f64 - (rect.origin.y + rect.size.height) + CGDisplay::main().pixels_high() as f64 - (rect.origin.y + rect.size.height) as f64 } /// Converts from winit screen-coordinates to macOS screen-coordinates. @@ -121,8 +121,8 @@ pub fn bottom_left_to_top_left(rect: NSRect) -> f64 { /// macOS: bottom-left is (0, 0) and y increasing upwards pub fn window_position(position: LogicalPosition) -> NSPoint { NSPoint::new( - position.x, - CGDisplay::main().pixels_high() as f64 - position.y, + position.x as CGFloat, + CGDisplay::main().pixels_high() as CGFloat - position.y as CGFloat, ) } diff --git a/src/platform_impl/macos/view.rs b/src/platform_impl/macos/view.rs index 55e3f7dc41..b8c93cb5e2 100644 --- a/src/platform_impl/macos/view.rs +++ b/src/platform_impl/macos/view.rs @@ -1168,7 +1168,7 @@ extern "C" fn pressure_change_with_event(this: &Object, _sel: Sel, event: id) { event: WindowEvent::TouchpadPressure { device_id: DEVICE_ID, pressure, - stage, + stage: stage as i64, }, }; diff --git a/src/platform_impl/macos/window.rs b/src/platform_impl/macos/window.rs index dde004ac5c..4c1ec0bec0 100644 --- a/src/platform_impl/macos/window.rs +++ b/src/platform_impl/macos/window.rs @@ -1255,10 +1255,11 @@ unsafe fn set_min_inner_size(window: V, mut min_size: Logica // Convert from client area size to window size min_size.width += (current_rect.size.width - content_rect.size.width) as f64; // this tends to be 0 min_size.height += (current_rect.size.height - content_rect.size.height) as f64; - window.setMinSize_(NSSize { + let min_size = NSSize { width: min_size.width as CGFloat, height: min_size.height as CGFloat, - }); + }; + window.setMinSize_(min_size); // If necessary, resize the window to match constraint if current_rect.size.width < min_size.width { current_rect.size.width = min_size.width; @@ -1279,10 +1280,11 @@ unsafe fn set_max_inner_size(window: V, mut max_size: Logica // Convert from client area size to window size max_size.width += (current_rect.size.width - content_rect.size.width) as f64; // this tends to be 0 max_size.height += (current_rect.size.height - content_rect.size.height) as f64; - window.setMaxSize_(NSSize { + let max_size = NSSize { width: max_size.width as CGFloat, height: max_size.height as CGFloat, - }); + }; + window.setMaxSize_(max_size); // If necessary, resize the window to match constraint if current_rect.size.width > max_size.width { current_rect.size.width = max_size.width; From 3e0a544eb8ed114ed664ff22026a56253baf192a Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Sat, 11 Jun 2022 18:57:19 +0200 Subject: [PATCH 15/60] Documentation cleanup (#2328) * Remove redundant documentation links * Add note to README about windows not showing up on Wayland * Fix documentation links * Small documentation fixes * Add note about doing stuff after StartCause::Init on macOS --- README.md | 21 +++++++ src/dpi.rs | 24 ++++---- src/event.rs | 51 +++++++++------- src/event_loop.rs | 93 ++++++++++++++++------------ src/icon.rs | 8 +-- src/monitor.rs | 19 ++---- src/platform/android.rs | 8 +-- src/platform/macos.rs | 26 ++++---- src/platform/mod.rs | 2 +- src/platform/run_return.rs | 9 +-- src/platform/unix.rs | 30 ++++----- src/platform/web.rs | 4 +- src/window.rs | 122 +++++++++++++++++-------------------- 13 files changed, 221 insertions(+), 196 deletions(-) diff --git a/README.md b/README.md index 88e44f80f8..0caad194c7 100644 --- a/README.md +++ b/README.md @@ -69,6 +69,14 @@ Winit provides the following features, which can be enabled in your `Cargo.toml` ### Platform-specific usage +#### Wayland + +Note that windows don't appear on Wayland until you draw/present to them. + +`winit` doesn't do drawing, try the examples in [`glutin`] instead. + +[`glutin`]: https://github.com/rust-windowing/glutin + #### WebAssembly To run the web example: `cargo run-wasm --example web` @@ -121,3 +129,16 @@ fn main() { ``` And run the application with `cargo apk run --example request_redraw_threaded` + +#### MacOS + +A lot of functionality expects the application to be ready before you start +doing anything; this includes creating windows, fetching monitors, drawing, +and so on, see issues [#2238], [#2051] and [#2087]. + +If you encounter problems, you should try doing your initialization inside +`Event::NewEvents(StartCause::Init)`. + +[#2238]: https://github.com/rust-windowing/winit/issues/2238 +[#2051]: https://github.com/rust-windowing/winit/issues/2051 +[#2087]: https://github.com/rust-windowing/winit/issues/2087 diff --git a/src/dpi.rs b/src/dpi.rs index be6d8177f8..bf234007b9 100644 --- a/src/dpi.rs +++ b/src/dpi.rs @@ -35,8 +35,9 @@ //! //! ### Position and Size types //! -//! Winit's `Physical(Position|Size)` types correspond with the actual pixels on the device, and the -//! `Logical(Position|Size)` types correspond to the physical pixels divided by the scale factor. +//! Winit's [`PhysicalPosition`] / [`PhysicalSize`] types correspond with the actual pixels on the +//! device, and the [`LogicalPosition`] / [`LogicalSize`] types correspond to the physical pixels +//! divided by the scale factor. //! All of Winit's functions return physical types, but can take either logical or physical //! coordinates as input, allowing you to use the most convenient coordinate system for your //! particular application. @@ -46,19 +47,18 @@ //! floating precision when necessary (e.g. logical sizes for fractional scale factors and touch //! input). If `P` is a floating-point type, please do not cast the values with `as {int}`. Doing so //! will truncate the fractional part of the float, rather than properly round to the nearest -//! integer. Use the provided `cast` function or `From`/`Into` conversions, which handle the +//! integer. Use the provided `cast` function or [`From`]/[`Into`] conversions, which handle the //! rounding properly. Note that precision loss will still occur when rounding from a float to an //! int, although rounding lessens the problem. //! //! ### Events //! -//! Winit will dispatch a [`ScaleFactorChanged`](crate::event::WindowEvent::ScaleFactorChanged) -//! event whenever a window's scale factor has changed. This can happen if the user drags their -//! window from a standard-resolution monitor to a high-DPI monitor, or if the user changes their -//! DPI settings. This gives you a chance to rescale your application's UI elements and adjust how -//! the platform changes the window's size to reflect the new scale factor. If a window hasn't -//! received a [`ScaleFactorChanged`](crate::event::WindowEvent::ScaleFactorChanged) event, -//! then its scale factor can be found by calling [window.scale_factor()]. +//! Winit will dispatch a [`ScaleFactorChanged`] event whenever a window's scale factor has changed. +//! This can happen if the user drags their window from a standard-resolution monitor to a high-DPI +//! monitor, or if the user changes their DPI settings. This gives you a chance to rescale your +//! application's UI elements and adjust how the platform changes the window's size to reflect the new +//! scale factor. If a window hasn't received a [`ScaleFactorChanged`] event, then its scale factor +//! can be found by calling [`window.scale_factor()`]. //! //! ## How is the scale factor calculated? //! @@ -93,9 +93,11 @@ //! In other words, it is the value of [`window.devicePixelRatio`][web_1]. It is affected by //! both the screen scaling and the browser zoom level and can go below `1.0`. //! +//! //! [points]: https://en.wikipedia.org/wiki/Point_(typography) //! [picas]: https://en.wikipedia.org/wiki/Pica_(typography) -//! [window.scale_factor()]: crate::window::Window::scale_factor +//! [`ScaleFactorChanged`]: crate::event::WindowEvent::ScaleFactorChanged +//! [`window.scale_factor()`]: crate::window::Window::scale_factor //! [windows_1]: https://docs.microsoft.com/en-us/windows/win32/hidpi/high-dpi-desktop-application-development-on-windows //! [apple_1]: https://developer.apple.com/library/archive/documentation/DeviceInformation/Reference/iOSDeviceCompatibility/Displays/Displays.html //! [apple_2]: https://developer.apple.com/design/human-interface-guidelines/macos/icons-and-images/image-size-and-resolution/ diff --git a/src/event.rs b/src/event.rs index 65e556a2f7..29e860e699 100644 --- a/src/event.rs +++ b/src/event.rs @@ -1,10 +1,10 @@ -//! The `Event` enum and assorted supporting types. +//! The [`Event`] enum and assorted supporting types. //! -//! These are sent to the closure given to [`EventLoop::run(...)`][event_loop_run], where they get +//! These are sent to the closure given to [`EventLoop::run(...)`], where they get //! processed and used to modify the program state. For more details, see the root-level documentation. //! //! Some of these events represent different "parts" of a traditional event-handling loop. You could -//! approximate the basic ordering loop of [`EventLoop::run(...)`][event_loop_run] like this: +//! approximate the basic ordering loop of [`EventLoop::run(...)`] like this: //! //! ```rust,ignore //! let mut control_flow = ControlFlow::Poll; @@ -29,10 +29,11 @@ //! event_handler(LoopDestroyed, ..., &mut control_flow); //! ``` //! -//! This leaves out timing details like `ControlFlow::WaitUntil` but hopefully +//! This leaves out timing details like [`ControlFlow::WaitUntil`] but hopefully //! describes what happens in what order. //! -//! [event_loop_run]: crate::event_loop::EventLoop::run +//! [`EventLoop::run(...)`]: crate::event_loop::EventLoop::run +//! [`ControlFlow::WaitUntil`]: crate::event_loop::ControlFlow::WaitUntil use instant::Instant; use std::path::PathBuf; @@ -90,7 +91,7 @@ pub enum Event<'a, T: 'static> { /// can render here unconditionally for simplicity. MainEventsCleared, - /// Emitted after `MainEventsCleared` when a window should be redrawn. + /// Emitted after [`MainEventsCleared`] when a window should be redrawn. /// /// This gets triggered in two scenarios: /// - The OS has performed an operation that's invalidated the window's contents (such as @@ -102,14 +103,18 @@ pub enum Event<'a, T: 'static> { /// /// Mainly of interest to applications with mostly-static graphics that avoid redrawing unless /// something changes, like most non-game GUIs. + /// + /// [`MainEventsCleared`]: Self::MainEventsCleared RedrawRequested(WindowId), - /// Emitted after all `RedrawRequested` events have been processed and control flow is about to + /// Emitted after all [`RedrawRequested`] events have been processed and control flow is about to /// be taken away from the program. If there are no `RedrawRequested` events, it is emitted /// immediately after `MainEventsCleared`. /// /// This event is useful for doing any cleanup or bookkeeping work after all the rendering /// tasks have been completed. + /// + /// [`RedrawRequested`]: Self::RedrawRequested RedrawEventsCleared, /// Emitted when the event loop is being shut down. @@ -184,9 +189,11 @@ impl<'a, T> Event<'a, T> { /// Describes the reason the event loop is resuming. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum StartCause { - /// Sent if the time specified by `ControlFlow::WaitUntil` has been reached. Contains the + /// Sent if the time specified by [`ControlFlow::WaitUntil`] has been reached. Contains the /// moment the timeout was requested and the requested resume time. The actual resume time is /// guaranteed to be equal to or after the requested resume time. + /// + /// [`ControlFlow::WaitUntil`]: crate::event_loop::ControlFlow::WaitUntil ResumeTimeReached { start: Instant, requested_resume: Instant, @@ -200,7 +207,9 @@ pub enum StartCause { }, /// Sent if the event loop is being resumed after the loop's control flow was set to - /// `ControlFlow::Poll`. + /// [`ControlFlow::Poll`]. + /// + /// [`ControlFlow::Poll`]: crate::event_loop::ControlFlow::Poll Poll, /// Sent once, immediately after `run` is called. Indicates that the loop was just initialized. @@ -550,7 +559,7 @@ impl<'a> WindowEvent<'a> { pub struct DeviceId(pub(crate) platform_impl::DeviceId); impl DeviceId { - /// Returns a dummy `DeviceId`, useful for unit testing. + /// Returns a dummy id, useful for unit testing. /// /// # Safety /// @@ -579,7 +588,7 @@ pub enum DeviceEvent { /// Change in physical position of a pointing device. /// - /// This represents raw, unfiltered physical motion. Not to be confused with `WindowEvent::CursorMoved`. + /// This represents raw, unfiltered physical motion. Not to be confused with [`WindowEvent::CursorMoved`]. MouseMotion { /// (x, y) change in position in unspecified units. /// @@ -592,7 +601,7 @@ pub enum DeviceEvent { delta: MouseScrollDelta, }, - /// Motion on some analog axis. This event will be reported for all arbitrary input devices + /// Motion on some analog axis. This event will be reported for all arbitrary input devices /// that winit supports on this platform, including mouse devices. If the device is a mouse /// device then this will be reported alongside the MouseMotion event. Motion { @@ -673,7 +682,6 @@ pub struct KeyboardInput { /// // Press space key /// Ime::Commit("啊不") /// ``` -/// #[derive(Debug, Clone, PartialEq, Eq, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub enum Ime { @@ -718,18 +726,18 @@ pub enum TouchPhase { /// Represents a touch event /// -/// Every time the user touches the screen, a new `Start` event with an unique -/// identifier for the finger is generated. When the finger is lifted, an `End` +/// Every time the user touches the screen, a new [`TouchPhase::Started`] event with an unique +/// identifier for the finger is generated. When the finger is lifted, an [`TouchPhase::Ended`] /// event is generated with the same finger id. /// -/// After a `Start` event has been emitted, there may be zero or more `Move` +/// After a `Started` event has been emitted, there may be zero or more `Move` /// events when the finger is moved or the touch pressure changes. /// -/// The finger id may be reused by the system after an `End` event. The user -/// should assume that a new `Start` event received with the same id has nothing +/// The finger id may be reused by the system after an `Ended` event. The user +/// should assume that a new `Started` event received with the same id has nothing /// to do with the old finger and is a new finger. /// -/// A `Cancelled` event is emitted when the system has canceled tracking this +/// A [`TouchPhase::Cancelled`] event is emitted when the system has canceled tracking this /// touch, such as when the window loses focus, or on iOS if the user moves the /// device against their face. #[derive(Debug, Clone, Copy, PartialEq)] @@ -783,8 +791,9 @@ pub enum Force { impl Force { /// Returns the force normalized to the range between 0.0 and 1.0 inclusive. + /// /// Instead of normalizing the force, you should prefer to handle - /// `Force::Calibrated` so that the amount of force the user has to apply is + /// [`Force::Calibrated`] so that the amount of force the user has to apply is /// consistent across devices. pub fn normalized(&self) -> f64 { match self { @@ -845,7 +854,7 @@ pub enum MouseScrollDelta { /// Amount in pixels to scroll in the horizontal and /// vertical direction. /// - /// Scroll events are expressed as a PixelDelta if + /// Scroll events are expressed as a `PixelDelta` if /// supported by the device (eg. a touchpad) and /// platform. /// diff --git a/src/event_loop.rs b/src/event_loop.rs index b768b643c9..92b807fdcf 100644 --- a/src/event_loop.rs +++ b/src/event_loop.rs @@ -1,14 +1,12 @@ -//! The `EventLoop` struct and assorted supporting types, including `ControlFlow`. +//! The [`EventLoop`] struct and assorted supporting types, including +//! [`ControlFlow`]. //! -//! If you want to send custom events to the event loop, use [`EventLoop::create_proxy()`][create_proxy] -//! to acquire an [`EventLoopProxy`][event_loop_proxy] and call its [`send_event`][send_event] method. +//! If you want to send custom events to the event loop, use +//! [`EventLoop::create_proxy`] to acquire an [`EventLoopProxy`] and call its +//! [`send_event`](`EventLoopProxy::send_event`) method. //! //! See the root-level documentation for information on how to create and use an event loop to //! handle events. -//! -//! [create_proxy]: crate::event_loop::EventLoop::create_proxy -//! [event_loop_proxy]: crate::event_loop::EventLoopProxy -//! [send_event]: crate::event_loop::EventLoopProxy::send_event use instant::Instant; use std::marker::PhantomData; use std::ops::Deref; @@ -19,26 +17,27 @@ 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. /// -/// An `EventLoop` can be seen more or less as a "context". Calling `EventLoop::new()` +/// An `EventLoop` can be seen more or less as a "context". Calling [`EventLoop::new`] /// initializes everything that will be required to create windows. For example on Linux creating /// an event loop opens a connection to the X or Wayland server. /// -/// To wake up an `EventLoop` from a another thread, see the `EventLoopProxy` docs. +/// To wake up an `EventLoop` from a another thread, see the [`EventLoopProxy`] docs. /// -/// Note that the `EventLoop` cannot be shared across threads (due to platform-dependant logic -/// forbidding it), as such it is neither `Send` nor `Sync`. If you need cross-thread access, the -/// `Window` created from this `EventLoop` _can_ be sent to an other thread, and the -/// `EventLoopProxy` allows you to wake up an `EventLoop` from another thread. +/// Note that this cannot be shared across threads (due to platform-dependant logic +/// forbidding it), as such it is neither [`Send`] nor [`Sync`]. If you need cross-thread access, the +/// [`Window`] created from this _can_ be sent to an other thread, and the +/// [`EventLoopProxy`] allows you to wake up an `EventLoop` from another thread. /// +/// [`Window`]: crate::window::Window pub struct EventLoop { pub(crate) event_loop: platform_impl::EventLoop, pub(crate) _marker: PhantomData<*mut ()>, // Not Send nor Sync } -/// Target that associates windows with an `EventLoop`. +/// Target that associates windows with an [`EventLoop`]. /// /// This type exists to allow you to create new windows while Winit executes -/// your callback. `EventLoop` will coerce into this type (`impl Deref for +/// your callback. [`EventLoop`] will coerce into this type (`impl Deref for /// EventLoop`), so functions that take this as a parameter can also take /// `&EventLoop`. pub struct EventLoopWindowTarget { @@ -77,11 +76,11 @@ impl EventLoopBuilder { /// Builds a new event loop. /// - /// ***For cross-platform compatibility, the `EventLoop` must be created on the main thread.*** + /// ***For cross-platform compatibility, the [`EventLoop`] must be created on the main thread.*** /// Attempting to create the event loop on a different thread will panic. This restriction isn't /// strictly necessary on all platforms, but is imposed to eliminate any nasty surprises when /// porting to platforms that require it. `EventLoopBuilderExt::any_thread` functions are exposed - /// in the relevant `platform` module if the target platform supports creating an event loop on + /// in the relevant [`platform`] module if the target platform supports creating an event loop on /// any thread. /// /// Calling this function will result in display backend initialisation. @@ -92,6 +91,8 @@ impl EventLoopBuilder { /// `WINIT_UNIX_BACKEND`. Legal values are `x11` and `wayland`. /// If it is not set, winit will try to connect to a Wayland connection, and if that fails, /// will fall back on X11. If this variable is set with any other value, winit will panic. + /// + /// [`platform`]: crate::platform #[inline] pub fn build(&mut self) -> EventLoop { // Certain platforms accept a mutable reference in their API. @@ -115,18 +116,21 @@ impl fmt::Debug for EventLoopWindowTarget { } } -/// Set by the user callback given to the `EventLoop::run` method. +/// Set by the user callback given to the [`EventLoop::run`] method. +/// +/// Indicates the desired behavior of the event loop after [`Event::RedrawEventsCleared`] is emitted. /// -/// Indicates the desired behavior of the event loop after [`Event::RedrawEventsCleared`][events_cleared] -/// is emitted. Defaults to `Poll`. +/// Defaults to [`Poll`]. /// /// ## Persistency +/// /// Almost every change is persistent between multiple calls to the event loop closure within a -/// given run loop. The only exception to this is `ExitWithCode` which, once set, cannot be unset. +/// given run loop. The only exception to this is [`ExitWithCode`] which, once set, cannot be unset. /// Changes are **not** persistent between multiple calls to `run_return` - issuing a new call will -/// reset the control flow to `Poll`. +/// reset the control flow to [`Poll`]. /// -/// [events_cleared]: crate::event::Event::RedrawEventsCleared +/// [`ExitWithCode`]: Self::ExitWithCode +/// [`Poll`]: Self::Poll #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum ControlFlow { /// When the current loop iteration finishes, immediately begin a new iteration regardless of @@ -144,10 +148,12 @@ pub enum ControlFlow { /// arrives or the given time is reached. /// /// Useful for implementing efficient timers. Applications which want to render at the display's - /// native refresh rate should instead use `Poll` and the VSync functionality of a graphics API + /// native refresh rate should instead use [`Poll`] and the VSync functionality of a graphics API /// to reduce odds of missed frames. + /// + /// [`Poll`]: Self::Poll WaitUntil(Instant), - /// Send a `LoopDestroyed` event and stop the event loop. This variant is *sticky* - once set, + /// Send a [`LoopDestroyed`] event and stop the event loop. This variant is *sticky* - once set, /// `control_flow` cannot be changed from `ExitWithCode`, and any future attempts to do so will /// result in the `control_flow` parameter being reset to `ExitWithCode`. /// @@ -161,6 +167,7 @@ pub enum ControlFlow { /// which can cause surprises with negative exit values (`-42` would end up as `214`). See /// [`std::process::exit`]. /// + /// [`LoopDestroyed`]: Event::LoopDestroyed /// [`Exit`]: ControlFlow::Exit ExitWithCode(i32), } @@ -168,41 +175,41 @@ pub enum ControlFlow { impl ControlFlow { /// Alias for [`ExitWithCode`]`(0)`. /// - /// [`ExitWithCode`]: ControlFlow::ExitWithCode + /// [`ExitWithCode`]: Self::ExitWithCode #[allow(non_upper_case_globals)] pub const Exit: Self = Self::ExitWithCode(0); /// Sets this to [`Poll`]. /// - /// [`Poll`]: ControlFlow::Poll + /// [`Poll`]: Self::Poll pub fn set_poll(&mut self) { *self = Self::Poll; } /// Sets this to [`Wait`]. /// - /// [`Wait`]: ControlFlow::Wait + /// [`Wait`]: Self::Wait pub fn set_wait(&mut self) { *self = Self::Wait; } /// Sets this to [`WaitUntil`]`(instant)`. /// - /// [`WaitUntil`]: ControlFlow::WaitUntil + /// [`WaitUntil`]: Self::WaitUntil pub fn set_wait_until(&mut self, instant: Instant) { *self = Self::WaitUntil(instant); } /// Sets this to [`ExitWithCode`]`(code)`. /// - /// [`ExitWithCode`]: ControlFlow::ExitWithCode + /// [`ExitWithCode`]: Self::ExitWithCode pub fn set_exit_with_code(&mut self, code: i32) { *self = Self::ExitWithCode(code); } /// Sets this to [`Exit`]. /// - /// [`Exit`]: ControlFlow::Exit + /// [`Exit`]: Self::Exit pub fn set_exit(&mut self) { *self = Self::Exit; } @@ -210,13 +217,15 @@ impl ControlFlow { impl Default for ControlFlow { #[inline(always)] - fn default() -> ControlFlow { - ControlFlow::Poll + fn default() -> Self { + Self::Poll } } impl EventLoop<()> { - /// Alias for `EventLoopBuilder::new().build()`. + /// Alias for [`EventLoopBuilder::new().build()`]. + /// + /// [`EventLoopBuilder::new().build()`]: EventLoopBuilder::build #[inline] pub fn new() -> EventLoop<()> { EventLoopBuilder::new().build() @@ -258,7 +267,7 @@ impl EventLoop { self.event_loop.run(event_handler) } - /// Creates an `EventLoopProxy` that can be used to dispatch user events to the main event loop. + /// Creates an [`EventLoopProxy`] that can be used to dispatch user events to the main event loop. pub fn create_proxy(&self) -> EventLoopProxy { EventLoopProxy { event_loop_proxy: self.event_loop.create_proxy(), @@ -318,7 +327,7 @@ impl EventLoopWindowTarget { } } -/// Used to send custom events to `EventLoop`. +/// Used to send custom events to [`EventLoop`]. pub struct EventLoopProxy { event_loop_proxy: platform_impl::EventLoopProxy, } @@ -332,11 +341,13 @@ impl Clone for EventLoopProxy { } impl EventLoopProxy { - /// Send an event to the `EventLoop` from which this proxy was created. This emits a + /// Send an event to the [`EventLoop`] from which this proxy was created. This emits a /// `UserEvent(event)` event in the event loop, where `event` is the value passed to this /// function. /// - /// Returns an `Err` if the associated `EventLoop` no longer exists. + /// Returns an `Err` if the associated [`EventLoop`] no longer exists. + /// + /// [`UserEvent(event)`]: Event::UserEvent pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed> { self.event_loop_proxy.send_event(event) } @@ -348,8 +359,10 @@ impl fmt::Debug for EventLoopProxy { } } -/// The error that is returned when an `EventLoopProxy` attempts to wake up an `EventLoop` that -/// no longer exists. Contains the original event given to `send_event`. +/// The error that is returned when an [`EventLoopProxy`] attempts to wake up an [`EventLoop`] that +/// no longer exists. +/// +/// Contains the original event given to [`EventLoopProxy::send_event`]. #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] pub struct EventLoopClosed(pub T); diff --git a/src/icon.rs b/src/icon.rs index 418ea03afa..982e5f5d81 100644 --- a/src/icon.rs +++ b/src/icon.rs @@ -13,7 +13,7 @@ pub(crate) struct Pixel { pub(crate) const PIXEL_SIZE: usize = mem::size_of::(); #[derive(Debug)] -/// An error produced when using `Icon::from_rgba` with invalid arguments. +/// An error produced when using [`Icon::from_rgba`] with invalid arguments. pub enum BadIcon { /// Produced when the length of the `rgba` argument isn't divisible by 4, thus `rgba` can't be /// safely interpreted as 32bpp RGBA pixels. @@ -73,10 +73,6 @@ mod constructors { use super::*; impl RgbaIcon { - /// Creates an `Icon` from 32bpp RGBA data. - /// - /// The length of `rgba` must be divisible by 4, and `width * height` must equal - /// `rgba.len() / 4`. Otherwise, this will return a `BadIcon` error. pub fn from_rgba(rgba: Vec, width: u32, height: u32) -> Result { if rgba.len() % PIXEL_SIZE != 0 { return Err(BadIcon::ByteCountNotDivisibleBy4 { @@ -123,7 +119,7 @@ impl fmt::Debug for Icon { } impl Icon { - /// Creates an `Icon` from 32bpp RGBA data. + /// Creates an icon from 32bpp RGBA data. /// /// The length of `rgba` must be divisible by 4, and `width * height` must equal /// `rgba.len() / 4`. Otherwise, this will return a `BadIcon` error. diff --git a/src/monitor.rs b/src/monitor.rs index 5cf680ff21..7611f94b56 100644 --- a/src/monitor.rs +++ b/src/monitor.rs @@ -1,14 +1,10 @@ //! 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_handle] -//! type. This is retrieved from one of the following methods, which return an iterator of -//! [`MonitorHandle`][monitor_handle]: -//! - [`EventLoopWindowTarget::available_monitors`][loop_get] -//! - [`Window::available_monitors`][window_get]. -//! -//! [monitor_handle]: crate::monitor::MonitorHandle -//! [loop_get]: crate::event_loop::EventLoopWindowTarget::available_monitors -//! [window_get]: crate::window::Window::available_monitors +//! If you want to get basic information about a monitor, you can use the +//! [`MonitorHandle`] type. This is retrieved from one of the following +//! methods, which return an iterator of [`MonitorHandle`]: +//! - [`EventLoopWindowTarget::available_monitors`](crate::event_loop::EventLoopWindowTarget::available_monitors). +//! - [`Window::available_monitors`](crate::window::Window::available_monitors). use crate::{ dpi::{PhysicalPosition, PhysicalSize}, platform_impl, @@ -16,10 +12,7 @@ use crate::{ /// Describes a fullscreen video mode of a monitor. /// -/// Can be acquired with: -/// - [`MonitorHandle::video_modes`][monitor_get]. -/// -/// [monitor_get]: crate::monitor::MonitorHandle::video_modes +/// Can be acquired with [`MonitorHandle::video_modes`]. #[derive(Clone, PartialEq, Eq, Hash)] pub struct VideoMode { pub(crate) video_mode: platform_impl::VideoMode, diff --git a/src/platform/android.rs b/src/platform/android.rs index b4e9917642..af15f3efd9 100644 --- a/src/platform/android.rs +++ b/src/platform/android.rs @@ -7,15 +7,15 @@ use crate::{ use ndk::configuration::Configuration; use ndk_glue::Rect; -/// Additional methods on `EventLoop` that are specific to Android. +/// Additional methods on [`EventLoop`] that are specific to Android. pub trait EventLoopExtAndroid {} impl EventLoopExtAndroid for EventLoop {} -/// Additional methods on `EventLoopWindowTarget` that are specific to Android. +/// Additional methods on [`EventLoopWindowTarget`] that are specific to Android. pub trait EventLoopWindowTargetExtAndroid {} -/// Additional methods on `Window` that are specific to Android. +/// Additional methods on [`Window`] that are specific to Android. pub trait WindowExtAndroid { fn content_rect(&self) -> Rect; @@ -34,7 +34,7 @@ impl WindowExtAndroid for Window { impl EventLoopWindowTargetExtAndroid for EventLoopWindowTarget {} -/// Additional methods on `WindowBuilder` that are specific to Android. +/// Additional methods on [`WindowBuilder`] that are specific to Android. pub trait WindowBuilderExtAndroid {} impl WindowBuilderExtAndroid for WindowBuilder {} diff --git a/src/platform/macos.rs b/src/platform/macos.rs index 1ddab448a9..10997e5247 100644 --- a/src/platform/macos.rs +++ b/src/platform/macos.rs @@ -9,16 +9,16 @@ use crate::{ window::{Window, WindowBuilder}, }; -/// Additional methods on `Window` that are specific to MacOS. +/// Additional methods on [`Window`] that are specific to MacOS. pub trait WindowExtMacOS { /// Returns a pointer to the cocoa `NSWindow` that is used by this window. /// - /// The pointer will become invalid when the `Window` is destroyed. + /// The pointer will become invalid when the [`Window`] is destroyed. fn ns_window(&self) -> *mut c_void; /// Returns a pointer to the cocoa `NSView` that is used by this window. /// - /// The pointer will become invalid when the `Window` is destroyed. + /// The pointer will become invalid when the [`Window`] is destroyed. fn ns_view(&self) -> *mut c_void; /// Returns whether or not the window is in simple fullscreen mode. @@ -89,16 +89,14 @@ impl Default for ActivationPolicy { } } -/// Additional methods on `WindowBuilder` that are specific to MacOS. +/// Additional methods on [`WindowBuilder`] that are specific to MacOS. /// -/// **Note:** Properties dealing with the titlebar will be overwritten by the `with_decorations` method -/// on the base `WindowBuilder`: -/// -/// - `with_titlebar_transparent` -/// - `with_title_hidden` -/// - `with_titlebar_hidden` -/// - `with_titlebar_buttons_hidden` -/// - `with_fullsize_content_view` +/// **Note:** Properties dealing with the titlebar will be overwritten by the [`WindowBuilder::with_decorations`] method: +/// - `with_titlebar_transparent` +/// - `with_title_hidden` +/// - `with_titlebar_hidden` +/// - `with_titlebar_buttons_hidden` +/// - `with_fullsize_content_view` pub trait WindowBuilderExtMacOS { /// Enables click-and-drag behavior for the entire window, not just the titlebar. fn with_movable_by_window_background(self, movable_by_window_background: bool) @@ -238,7 +236,7 @@ impl EventLoopBuilderExtMacOS for EventLoopBuilder { } } -/// Additional methods on `MonitorHandle` that are specific to MacOS. +/// Additional methods on [`MonitorHandle`] that are specific to MacOS. pub trait MonitorHandleExtMacOS { /// Returns the identifier of the monitor for Cocoa. fn native_id(&self) -> u32; @@ -257,7 +255,7 @@ impl MonitorHandleExtMacOS for MonitorHandle { } } -/// Additional methods on `EventLoopWindowTarget` that are specific to macOS. +/// Additional methods on [`EventLoopWindowTarget`] that are specific to macOS. pub trait EventLoopWindowTargetExtMacOS { /// Hide the entire application. In most applications this is typically triggered with Command-H. fn hide_application(&self); diff --git a/src/platform/mod.rs b/src/platform/mod.rs index 1ee5fce2bb..dde4c079cf 100644 --- a/src/platform/mod.rs +++ b/src/platform/mod.rs @@ -19,7 +19,7 @@ pub mod android; pub mod ios; pub mod macos; pub mod unix; +pub mod web; pub mod windows; pub mod run_return; -pub mod web; diff --git a/src/platform/run_return.rs b/src/platform/run_return.rs index 1c2fa62326..5c25461540 100644 --- a/src/platform/run_return.rs +++ b/src/platform/run_return.rs @@ -14,17 +14,18 @@ use crate::{ event_loop::{ControlFlow, EventLoop, EventLoopWindowTarget}, }; -/// Additional methods on `EventLoop` to return control flow to the caller. +/// Additional methods on [`EventLoop`] to return control flow to the caller. pub trait EventLoopExtRunReturn { - /// A type provided by the user that can be passed through `Event::UserEvent`. + /// A type provided by the user that can be passed through [`Event::UserEvent`]. type UserEvent; /// Initializes the `winit` event loop. /// - /// Unlike `run`, this function accepts non-`'static` (i.e. non-`move`) closures and returns - /// control flow to the caller when `control_flow` is set to `ControlFlow::Exit`. + /// Unlike [`EventLoop::run`], this function accepts non-`'static` (i.e. non-`move`) closures + /// and returns control flow to the caller when `control_flow` is set to [`ControlFlow::Exit`]. /// /// # Caveats + /// /// Despite its appearance at first glance, this is *not* a perfect replacement for /// `poll_events`. For example, this function will not return on Windows or macOS while a /// window is getting resized, resulting in all application logic outside of the diff --git a/src/platform/unix.rs b/src/platform/unix.rs index 81b06ad07b..ae0c994252 100644 --- a/src/platform/unix.rs +++ b/src/platform/unix.rs @@ -35,13 +35,13 @@ pub use crate::platform_impl::{x11::util::WindowType as XWindowType, XNotSupport #[cfg(feature = "wayland")] pub use crate::window::Theme; -/// Additional methods on `EventLoopWindowTarget` that are specific to Unix. +/// Additional methods on [`EventLoopWindowTarget`] that are specific to Unix. pub trait EventLoopWindowTargetExtUnix { - /// True if the `EventLoopWindowTarget` uses Wayland. + /// True if the [`EventLoopWindowTarget`] uses Wayland. #[cfg(feature = "wayland")] fn is_wayland(&self) -> bool; - /// True if the `EventLoopWindowTarget` uses X11. + /// True if the [`EventLoopWindowTarget`] uses X11. #[cfg(feature = "x11")] fn is_x11(&self) -> bool; @@ -50,11 +50,13 @@ pub trait EventLoopWindowTargetExtUnix { fn xlib_xconnection(&self) -> Option>; /// Returns a pointer to the `wl_display` object of wayland that is used by this - /// `EventLoopWindowTarget`. + /// [`EventLoopWindowTarget`]. /// - /// Returns `None` if the `EventLoop` doesn't use wayland (if it uses xlib for example). + /// Returns `None` if the [`EventLoop`] doesn't use wayland (if it uses xlib for example). /// - /// The pointer will become invalid when the winit `EventLoop` is destroyed. + /// The pointer will become invalid when the winit [`EventLoop`] is destroyed. + /// + /// [`EventLoop`]: crate::event_loop::EventLoop #[cfg(feature = "wayland")] fn wayland_display(&self) -> Option<*mut raw::c_void>; } @@ -134,9 +136,9 @@ impl EventLoopBuilderExtUnix for EventLoopBuilder { } } -/// Additional methods on `Window` that are specific to Unix. +/// Additional methods on [`Window`] that are specific to Unix. pub trait WindowExtUnix { - /// Returns the ID of the `Window` xlib object that is used by this window. + /// Returns the ID of the [`Window`] xlib object that is used by this window. /// /// Returns `None` if the window doesn't use xlib (if it uses wayland for example). #[cfg(feature = "x11")] @@ -146,7 +148,7 @@ pub trait WindowExtUnix { /// /// Returns `None` if the window doesn't use xlib (if it uses wayland for example). /// - /// The pointer will become invalid when the glutin `Window` is destroyed. + /// The pointer will become invalid when the [`Window`] is destroyed. #[cfg(feature = "x11")] fn xlib_display(&self) -> Option<*mut raw::c_void>; @@ -161,7 +163,7 @@ pub trait WindowExtUnix { /// /// Returns `None` if the window doesn't use xlib (if it uses wayland for example). /// - /// The pointer will become invalid when the glutin `Window` is destroyed. + /// The pointer will become invalid when the [`Window`] is destroyed. #[cfg(feature = "x11")] fn xcb_connection(&self) -> Option<*mut raw::c_void>; @@ -169,7 +171,7 @@ pub trait WindowExtUnix { /// /// Returns `None` if the window doesn't use wayland (if it uses xlib for example). /// - /// The pointer will become invalid when the glutin `Window` is destroyed. + /// The pointer will become invalid when the [`Window`] is destroyed. #[cfg(feature = "wayland")] fn wayland_surface(&self) -> Option<*mut raw::c_void>; @@ -177,7 +179,7 @@ pub trait WindowExtUnix { /// /// Returns `None` if the window doesn't use wayland (if it uses xlib for example). /// - /// The pointer will become invalid when the glutin `Window` is destroyed. + /// The pointer will become invalid when the [`Window`] is destroyed. #[cfg(feature = "wayland")] fn wayland_display(&self) -> Option<*mut raw::c_void>; @@ -193,7 +195,7 @@ pub trait WindowExtUnix { /// It is a remnant of a previous implementation detail for the /// wayland backend, and is no longer relevant. /// - /// Always return true. + /// Always return `true`. #[deprecated] fn is_ready(&self) -> bool; } @@ -285,7 +287,7 @@ impl WindowExtUnix for Window { } } -/// Additional methods on `WindowBuilder` that are specific to Unix. +/// Additional methods on [`WindowBuilder`] that are specific to Unix. pub trait WindowBuilderExtUnix { #[cfg(feature = "x11")] fn with_x11_visual(self, visual_infos: *const T) -> Self; diff --git a/src/platform/web.rs b/src/platform/web.rs index 210f87b9d2..36c2a53985 100644 --- a/src/platform/web.rs +++ b/src/platform/web.rs @@ -1,8 +1,8 @@ #![cfg(target_arch = "wasm32")] //! The web target does not automatically insert the canvas element object into the web page, to -//! allow end users to determine how the page should be laid out. Use the `WindowExtWebSys` trait -//! to retrieve the canvas from the Window. Alternatively, use the `WindowBuilderExtWebSys` trait +//! allow end users to determine how the page should be laid out. Use the [`WindowExtWebSys`] trait +//! to retrieve the canvas from the Window. Alternatively, use the [`WindowBuilderExtWebSys`] trait //! to provide your own canvas. use crate::window::WindowBuilder; diff --git a/src/window.rs b/src/window.rs index 2f8cbbf50c..1a1ded6fac 100644 --- a/src/window.rs +++ b/src/window.rs @@ -1,4 +1,4 @@ -//! The `Window` struct and associated types. +//! The [`Window`] struct and associated types. use std::fmt; use crate::{ @@ -61,7 +61,7 @@ impl Drop for Window { /// Identifier of a window. Unique for each window. /// -/// Can be obtained with `window.id()`. +/// Can be obtained with [`window.id()`](`Window::id`). /// /// Whenever you receive an event specific to a window, this event contains a `WindowId` which you /// can then compare to the ids of your windows. @@ -69,13 +69,13 @@ impl Drop for Window { pub struct WindowId(pub(crate) platform_impl::WindowId); impl WindowId { - /// Returns a dummy `WindowId`, useful for unit testing. + /// Returns a dummy id, useful for unit testing. /// /// # Safety /// /// The only guarantee made about the return value of this function is that /// it will always be equal to itself and to future values returned by this function. - /// No other guarantees are made. This may be equal to a real `WindowId`. + /// No other guarantees are made. This may be equal to a real [`WindowId`]. /// /// **Passing this into a winit function will result in undefined behavior.** pub const unsafe fn dummy() -> Self { @@ -142,7 +142,7 @@ impl Default for WindowAttributes { } impl WindowBuilder { - /// Initializes a new `WindowBuilder` with default values. + /// Initializes a new builder with default values. #[inline] pub fn new() -> Self { Default::default() @@ -153,8 +153,6 @@ impl WindowBuilder { /// If this is not set, some platform-specific dimensions will be used. /// /// See [`Window::set_inner_size`] for details. - /// - /// [`Window::set_inner_size`]: crate::window::Window::set_inner_size #[inline] pub fn with_inner_size>(mut self, size: S) -> Self { self.window.inner_size = Some(size.into()); @@ -167,8 +165,6 @@ impl WindowBuilder { /// from reserved). /// /// See [`Window::set_min_inner_size`] for details. - /// - /// [`Window::set_min_inner_size`]: crate::window::Window::set_min_inner_size #[inline] pub fn with_min_inner_size>(mut self, min_size: S) -> Self { self.window.min_inner_size = Some(min_size.into()); @@ -181,8 +177,6 @@ impl WindowBuilder { /// the primary monitor's dimensions by the platform. /// /// See [`Window::set_max_inner_size`] for details. - /// - /// [`Window::set_max_inner_size`]: crate::window::Window::set_max_inner_size #[inline] pub fn with_max_inner_size>(mut self, max_size: S) -> Self { self.window.max_inner_size = Some(max_size.into()); @@ -211,8 +205,6 @@ impl WindowBuilder { /// - **X11**: The top left corner of the window, the window's "outer" /// position. /// - **Others**: Ignored. - /// - /// [`Window::set_outer_position`]: crate::window::Window::set_outer_position #[inline] pub fn with_position>(mut self, position: P) -> Self { self.window.position = Some(position.into()); @@ -224,8 +216,6 @@ impl WindowBuilder { /// The default is `true`. /// /// See [`Window::set_resizable`] for details. - /// - /// [`Window::set_resizable`]: crate::window::Window::set_resizable #[inline] pub fn with_resizable(mut self, resizable: bool) -> Self { self.window.resizable = resizable; @@ -237,8 +227,6 @@ impl WindowBuilder { /// The default is `"winit window"`. /// /// See [`Window::set_title`] for details. - /// - /// [`Window::set_title`]: crate::window::Window::set_title #[inline] pub fn with_title>(mut self, title: T) -> Self { self.window.title = title.into(); @@ -250,8 +238,6 @@ impl WindowBuilder { /// The default is `None`. /// /// See [`Window::set_fullscreen`] for details. - /// - /// [`Window::set_fullscreen`]: crate::window::Window::set_fullscreen #[inline] pub fn with_fullscreen(mut self, fullscreen: Option) -> Self { self.window.fullscreen = fullscreen; @@ -263,8 +249,6 @@ impl WindowBuilder { /// The default is `false`. /// /// See [`Window::set_maximized`] for details. - /// - /// [`Window::set_maximized`]: crate::window::Window::set_maximized #[inline] pub fn with_maximized(mut self, maximized: bool) -> Self { self.window.maximized = maximized; @@ -276,8 +260,6 @@ impl WindowBuilder { /// The default is to show the window. /// /// See [`Window::set_visible`] for details. - /// - /// [`Window::set_visible`]: crate::window::Window::set_visible #[inline] pub fn with_visible(mut self, visible: bool) -> Self { self.window.visible = visible; @@ -301,8 +283,6 @@ impl WindowBuilder { /// The default is `true`. /// /// See [`Window::set_decorations`] for details. - /// - /// [`Window::set_decorations`]: crate::window::Window::set_decorations #[inline] pub fn with_decorations(mut self, decorations: bool) -> Self { self.window.decorations = decorations; @@ -314,8 +294,6 @@ impl WindowBuilder { /// The default is `false`. /// /// See [`Window::set_always_on_top`] for details. - /// - /// [`Window::set_always_on_top`]: crate::window::Window::set_always_on_top #[inline] pub fn with_always_on_top(mut self, always_on_top: bool) -> Self { self.window.always_on_top = always_on_top; @@ -327,8 +305,6 @@ impl WindowBuilder { /// The default is `None`. /// /// See [`Window::set_window_icon`] for details. - /// - /// [`Window::set_window_icon`]: crate::window::Window::set_window_icon #[inline] pub fn with_window_icon(mut self, window_icon: Option) -> Self { self.window.window_icon = window_icon; @@ -341,7 +317,7 @@ impl WindowBuilder { /// /// Platform-specific behavior: /// - **Web**: The window is created but not inserted into the web page automatically. Please - /// see the web platform module for more information. + /// see the web platform module for more information. #[inline] pub fn build( self, @@ -367,9 +343,9 @@ impl Window { /// /// Platform-specific behavior: /// - **Web**: The window is created but not inserted into the web page automatically. Please - /// see the web platform module for more information. + /// see the web platform module for more information. /// - /// [`WindowBuilder::new().build(event_loop)`]: crate::window::WindowBuilder::build + /// [`WindowBuilder::new().build(event_loop)`]: WindowBuilder::build #[inline] pub fn new(event_loop: &EventLoopWindowTarget) -> Result { let builder = WindowBuilder::new(); @@ -387,7 +363,7 @@ impl Window { /// See the [`dpi`](crate::dpi) module for more information. /// /// Note that this value can change depending on user action (for example if the window is - /// moved to another screen); as such, tracking `WindowEvent::ScaleFactorChanged` events is + /// moved to another screen); as such, tracking [`WindowEvent::ScaleFactorChanged`] events is /// the most robust way to track the DPI you need to use to draw. /// /// ## Platform-specific @@ -397,19 +373,20 @@ impl Window { /// - **iOS:** Can only be called on the main thread. Returns the underlying `UIView`'s /// [`contentScaleFactor`]. /// + /// [`WindowEvent::ScaleFactorChanged`]: crate::event::WindowEvent::ScaleFactorChanged /// [`contentScaleFactor`]: https://developer.apple.com/documentation/uikit/uiview/1622657-contentscalefactor?language=objc #[inline] pub fn scale_factor(&self) -> f64 { self.window.scale_factor() } - /// Emits a `WindowEvent::RedrawRequested` event in the associated event loop after all OS + /// Emits a [`Event::RedrawRequested`] event in the associated event loop after all OS /// events have been processed by the event loop. /// /// This is the **strongly encouraged** method of redrawing windows, as it can integrate with /// OS-requested redraws (e.g. when a window gets resized). /// - /// This function can cause `RedrawRequested` events to be emitted after `Event::MainEventsCleared` + /// This function can cause `RedrawRequested` events to be emitted after [`Event::MainEventsCleared`] /// but before `Event::NewEvents` if called in the following circumstances: /// * While processing `MainEventsCleared`. /// * While processing a `RedrawRequested` event that was sent during `MainEventsCleared` or any @@ -419,6 +396,9 @@ impl Window { /// /// - **iOS:** Can only be called on the main thread. /// - **Android:** Subsequent calls after `MainEventsCleared` are not handled. + /// + /// [`Event::RedrawRequested`]: crate::event::Event::RedrawRequested + /// [`Event::MainEventsCleared`]: crate::event::Event::MainEventsCleared #[inline] pub fn request_redraw(&self) { self.window.request_redraw() @@ -430,14 +410,14 @@ impl Window { /// Returns the position of the top-left hand corner of the window's client area relative to the /// top-left hand corner of the desktop. /// - /// The same conditions that apply to `outer_position` apply to this method. + /// The same conditions that apply to [`Window::outer_position`] apply to this method. /// /// ## Platform-specific /// /// - **iOS:** Can only be called on the main thread. Returns the top left coordinates of the /// window's [safe area] in the screen space coordinate system. /// - **Web:** Returns the top-left coordinates relative to the viewport. _Note: this returns the - /// same value as `outer_position`._ + /// same value as [`Window::outer_position`]._ /// - **Android / Wayland:** Always returns [`NotSupportedError`]. /// /// [safe area]: https://developer.apple.com/documentation/uikit/uiview/2891103-safeareainsets?language=objc @@ -447,14 +427,14 @@ impl Window { } /// Returns the position of the top-left hand corner of the window relative to the - /// top-left hand corner of the desktop. + /// top-left hand corner of the desktop. /// /// Note that the top-left hand corner of the desktop is not necessarily the same as - /// the screen. If the user uses a desktop with multiple monitors, the top-left hand corner - /// of the desktop is the top-left hand corner of the monitor at the top-left of the desktop. + /// the screen. If the user uses a desktop with multiple monitors, the top-left hand corner + /// of the desktop is the top-left hand corner of the monitor at the top-left of the desktop. /// /// The coordinates can be negative if the top-left hand corner of the window is outside - /// of the visible screen region. + /// of the visible screen region. /// /// ## Platform-specific /// @@ -469,8 +449,8 @@ impl Window { /// Modifies the position of the window. /// - /// See `outer_position` for more information about the coordinates. This automatically un-maximizes the - /// window if it's maximized. + /// See [`Window::outer_position`] for more information about the coordinates. + /// This automatically un-maximizes the window if it's maximized. /// /// ```no_run /// # use winit::dpi::{LogicalPosition, PhysicalPosition}; @@ -514,8 +494,8 @@ impl Window { /// Modifies the inner size of the window. /// - /// See `inner_size` for more information about the values. This automatically un-maximizes the - /// window if it's maximized. + /// See [`Window::inner_size`] for more information about the values. + /// This automatically un-maximizes the window if it's maximized. /// /// ```no_run /// # use winit::dpi::{LogicalSize, PhysicalSize}; @@ -542,14 +522,14 @@ impl Window { /// Returns the physical size of the entire window. /// /// These dimensions include the title bar and borders. If you don't want that (and you usually don't), - /// use `inner_size` instead. + /// use [`Window::inner_size`] instead. /// /// ## Platform-specific /// - /// - **iOS:** Can only be called on the main thread. Returns the `PhysicalSize` of the window in + /// - **iOS:** Can only be called on the main thread. Returns the [`PhysicalSize`] of the window in /// screen space coordinates. /// - **Web:** Returns the size of the canvas element. _Note: this returns the same value as - /// `inner_size`._ + /// [`Window::inner_size`]._ #[inline] pub fn outer_size(&self) -> PhysicalSize { self.window.outer_size() @@ -617,6 +597,7 @@ impl Window { /// Modifies the window's visibility. /// /// If `false`, this will hide the window. If `true`, this will show the window. + /// /// ## Platform-specific /// /// - **Android / Wayland / Web:** Unsupported. @@ -628,7 +609,7 @@ impl Window { /// Gets the window's current vibility state. /// - /// If `None` means it couldn't be determined so it is not recommended to use this to drive your rendering backend. + /// `None` means it couldn't be determined, so it is not recommended to use this to drive your rendering backend. /// /// ## Platform-specific /// @@ -641,9 +622,9 @@ impl Window { /// Sets whether the window is resizable or not. /// - /// Note that making the window unresizable doesn't exempt you from handling `Resized`, as that + /// Note that making the window unresizable doesn't exempt you from handling [`WindowEvent::Resized`], as that /// event can still be triggered by DPI scaling, entering fullscreen mode, etc. Also, the - /// window could still be resized by calling `[Window::set_inner_size]`. + /// window could still be resized by calling [`Window::set_inner_size`]. /// /// ## Platform-specific /// @@ -654,6 +635,8 @@ impl Window { /// ## Platform-specific /// /// - **iOS / Android / Web:** Unsupported. + /// + /// [`WindowEvent::Resized`]: crate::event::WindowEvent::Resized #[inline] pub fn set_resizable(&self, resizable: bool) { self.window.set_resizable(resizable) @@ -705,18 +688,18 @@ impl Window { /// /// ## Platform-specific /// - /// - **macOS:** `Fullscreen::Exclusive` provides true exclusive mode with a + /// - **macOS:** [`Fullscreen::Exclusive`] provides true exclusive mode with a /// video mode change. *Caveat!* macOS doesn't provide task switching (or /// spaces!) while in exclusive fullscreen mode. This mode should be used /// when a video mode change is desired, but for a better user experience, /// borderless fullscreen might be preferred. /// - /// `Fullscreen::Borderless` provides a borderless fullscreen window on a + /// [`Fullscreen::Borderless`] provides a borderless fullscreen window on a /// separate space. This is the idiomatic way for fullscreen games to work /// on macOS. See `WindowExtMacOs::set_simple_fullscreen` if /// separate spaces are not preferred. /// - /// The dock and the menu bar are always disabled in fullscreen mode. + /// The dock and the menu bar are disabled in exclusive fullscreen mode. /// - **iOS:** Can only be called on the main thread. /// - **Wayland:** Does not support exclusive fullscreen mode and will no-op a request. /// - **Windows:** Screen saver is disabled in fullscreen mode. @@ -743,8 +726,6 @@ impl Window { /// ## Platform-specific /// /// - **iOS / Android / Web:** Unsupported. - /// - /// [`setPrefersStatusBarHidden`]: https://developer.apple.com/documentation/uikit/uiviewcontroller/1621440-prefersstatusbarhidden?language=objc #[inline] pub fn set_decorations(&self, decorations: bool) { self.window.set_decorations(decorations) @@ -771,18 +752,20 @@ impl Window { self.window.set_always_on_top(always_on_top) } - /// Sets the window icon. On Windows and X11, this is typically the small icon in the top-left + /// Sets the window icon. + /// + /// On Windows and X11, this is typically the small icon in the top-left /// corner of the titlebar. /// /// ## Platform-specific /// /// - **iOS / Android / Web / Wayland / macOS:** Unsupported. /// - /// On Windows, this sets `ICON_SMALL`. The base size for a window icon is 16x16, but it's - /// recommended to account for screen scaling and pick a multiple of that, i.e. 32x32. + /// - **Windows:** Sets `ICON_SMALL`. The base size for a window icon is 16x16, but it's + /// recommended to account for screen scaling and pick a multiple of that, i.e. 32x32. /// - /// X11 has no universal guidelines for icon sizes, so you're at the whims of the WM. That - /// said, it's usually in the same ballpark as on Windows. + /// - **X11:** Has no universal guidelines for icon sizes, so you're at the whims of the WM. That + /// said, it's usually in the same ballpark as on Windows. #[inline] pub fn set_window_icon(&self, window_icon: Option) { self.window.set_window_icon(window_icon) @@ -865,7 +848,7 @@ impl Window { /// Requests user attention to the window, this has no effect if the application /// is already focused. How requesting for user attention manifests is platform dependent, - /// see `UserAttentionType` for details. + /// see [`UserAttentionType`] for details. /// /// Providing `None` will unset the request for user attention. Unsetting the request for /// user attention might not be done automatically by the WM when the window receives input. @@ -994,11 +977,13 @@ impl Window { /// Returns the list of all the monitors available on the system. /// - /// This is the same as `EventLoopWindowTarget::available_monitors`, and is provided for convenience. + /// This is the same as [`EventLoopWindowTarget::available_monitors`], and is provided for convenience. /// /// ## Platform-specific /// /// **iOS:** Can only be called on the main thread. + /// + /// [`EventLoopWindowTarget::available_monitors`]: crate::event_loop::EventLoopWindowTarget::available_monitors #[inline] pub fn available_monitors(&self) -> impl Iterator { self.window @@ -1011,12 +996,14 @@ impl Window { /// /// Returns `None` if it can't identify any monitor as a primary one. /// - /// This is the same as `EventLoopWindowTarget::primary_monitor`, and is provided for convenience. + /// This is the same as [`EventLoopWindowTarget::primary_monitor`], and is provided for convenience. /// /// ## Platform-specific /// /// **iOS:** Can only be called on the main thread. /// **Wayland:** Always returns `None`. + /// + /// [`EventLoopWindowTarget::primary_monitor`]: crate::event_loop::EventLoopWindowTarget::primary_monitor #[inline] pub fn primary_monitor(&self) -> Option { self.window.primary_monitor() @@ -1024,7 +1011,7 @@ impl Window { } unsafe impl raw_window_handle::HasRawWindowHandle for Window { - /// Returns a `raw_window_handle::RawWindowHandle` for the Window + /// Returns a [`raw_window_handle::RawWindowHandle`] for the Window /// /// ## Platform-specific /// @@ -1117,7 +1104,10 @@ pub enum Theme { /// ## Platform-specific /// -/// - **X11:** Sets the WM's `XUrgencyHint`. No distinction between `Critical` and `Informational`. +/// - **X11:** Sets the WM's `XUrgencyHint`. No distinction between [`Critical`] and [`Informational`]. +/// +/// [`Critical`]: Self::Critical +/// [`Informational`]: Self::Informational #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum UserAttentionType { /// ## Platform-specific From 8ef9fe44c7bb0352997898b793e361ea749e4883 Mon Sep 17 00:00:00 2001 From: Kirill Chibisov Date: Sun, 12 Jun 2022 09:53:28 +0300 Subject: [PATCH 16/60] Add `WindowBuilder::transparent` This is required to help hardware accelerated libraries like glutin that accept WindowBuilder instead of RawWindowHandle, since the api to access builder properties directly was removed. Follow up to 44288f6. --- CHANGELOG.md | 1 + src/window.rs | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5d103cdb8e..d98478e907 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ And please only add new entries to the top of this list, right below the `# Unre - Build docs on `docs.rs` for iOS and Android as well. - **Breaking:** Removed the `WindowAttributes` struct, since all its functionality is accessible from `WindowBuilder`. +- Added `WindowBuilder::transparent` getter to check if the user set `transparent` attribute. - On macOS, Fix emitting `Event::LoopDestroyed` on CMD+Q. - On macOS, fixed an issue where having multiple windows would prevent run_return from ever returning. - On Wayland, fix bug where the cursor wouldn't hide in GNOME. diff --git a/src/window.rs b/src/window.rs index 1a1ded6fac..50fcdbdc66 100644 --- a/src/window.rs +++ b/src/window.rs @@ -278,6 +278,12 @@ impl WindowBuilder { self } + /// Get whether the window will support transparency. + #[inline] + pub fn transparent(&self) -> bool { + self.window.transparent + } + /// Sets whether the window should have a border, a title bar, etc. /// /// The default is `true`. From 9e6f666616341ddd76fc61c6a0ed8dcbf3392a28 Mon Sep 17 00:00:00 2001 From: Kirill Chibisov Date: Mon, 13 Jun 2022 09:43:14 +0300 Subject: [PATCH 17/60] Refine `Window::set_cursor_grab` API This commit renames `Window::set_cursor_grab` to `Window::set_cursor_grab_mode`. The new API now accepts enumeration to control the way cursor grab is performed. The value could be: `lock`, `confine`, or `none`. This commit also implements `Window::set_cursor_position` for Wayland, since it's tied to locked cursor. Implements API from #1677. --- CHANGELOG.md | 2 + FEATURES.md | 8 +- examples/cursor_grab.rs | 24 +++- examples/multithreaded.rs | 18 ++- src/platform_impl/android/mod.rs | 5 +- src/platform_impl/ios/window.rs | 5 +- src/platform_impl/linux/mod.rs | 10 +- src/platform_impl/linux/wayland/env.rs | 10 +- .../linux/wayland/seat/pointer/data.rs | 6 +- .../linux/wayland/seat/pointer/handlers.rs | 2 + .../linux/wayland/seat/pointer/mod.rs | 77 ++++++++++++ src/platform_impl/linux/wayland/window/mod.rs | 41 +++++-- .../linux/wayland/window/shim.rs | 68 +++++++---- src/platform_impl/linux/x11/window.rs | 111 ++++++++++-------- src/platform_impl/macos/window.rs | 15 ++- src/platform_impl/web/web_sys/canvas.rs | 4 +- src/platform_impl/web/window.rs | 14 ++- src/platform_impl/windows/window.rs | 14 ++- src/window.rs | 54 +++++++-- 19 files changed, 358 insertions(+), 130 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d98478e907..fdc63ad11e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -58,6 +58,8 @@ And please only add new entries to the top of this list, right below the `# Unre - **Breaking:** On X11, device events are now ignored for unfocused windows by default, use `EventLoopWindowTarget::set_device_event_filter` to set the filter level. - Implemented `Default` on `EventLoop<()>`. - Implemented `Eq` for `Fullscreen`, `Theme`, and `UserAttentionType`. +- **Breaking:** `Window::set_cursor_grab` now accepts `CursorGrabMode` to control grabbing behavior. +- On Wayland, add support for `Window::set_cursor_position`. # 0.26.1 (2022-01-05) diff --git a/FEATURES.md b/FEATURES.md index 02625f064e..3610f4b793 100644 --- a/FEATURES.md +++ b/FEATURES.md @@ -100,7 +100,8 @@ If your PR makes notable changes to Winit's features, please update this section ### Input Handling - **Mouse events**: Generating mouse events associated with pointer motion, click, and scrolling events. - **Mouse set location**: Forcibly changing the location of the pointer. -- **Cursor grab**: Locking the cursor so it cannot exit the client area of a window. +- **Cursor locking**: Locking the cursor inside the window so it cannot move. +- **Cursor confining**: Confining the cursor to the window bounds so it cannot leave them. - **Cursor icon**: Changing the cursor icon, or hiding the cursor. - **Cursor hittest**: Handle or ignore mouse events for a window. - **Touch events**: Single-touch events. @@ -197,8 +198,9 @@ Legend: |Feature |Windows |MacOS |Linux x11|Linux Wayland|Android|iOS |WASM | |----------------------- | ----- | ---- | ------- | ----------- | ----- | ----- | -------- | |Mouse events |✔️ |▢[#63] |✔️ |✔️ |**N/A**|**N/A**|✔️ | -|Mouse set location |✔️ |✔️ |✔️ |❓ |**N/A**|**N/A**|**N/A**| -|Cursor grab |✔️ |▢[#165] |▢[#242] |✔️ |**N/A**|**N/A**|✔️ | +|Mouse set location |✔️ |✔️ |✔️ |✔️(when locked) |**N/A**|**N/A**|**N/A**| +|Cursor locking |❌ |✔️ |❌ |✔️ |**N/A**|**N/A**|✔️ | +|Cursor confining |✔️ |❌ |✔️ |✔️ |**N/A**|**N/A**|❌ | |Cursor icon |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|✔️ | |Cursor hittest |✔️ |✔️ |❌ |✔️ |**N/A**|**N/A**|❌ | |Touch events |✔️ |❌ |✔️ |✔️ |✔️ |✔️ |❌ | diff --git a/examples/cursor_grab.rs b/examples/cursor_grab.rs index 496b67704d..cff77885ea 100644 --- a/examples/cursor_grab.rs +++ b/examples/cursor_grab.rs @@ -4,7 +4,7 @@ use simple_logger::SimpleLogger; use winit::{ event::{DeviceEvent, ElementState, Event, KeyboardInput, ModifiersState, WindowEvent}, event_loop::EventLoop, - window::WindowBuilder, + window::{CursorGrabMode, WindowBuilder}, }; fn main() { @@ -34,11 +34,23 @@ fn main() { .. } => { use winit::event::VirtualKeyCode::*; - match key { - Escape => control_flow.set_exit(), - G => window.set_cursor_grab(!modifiers.shift()).unwrap(), - H => window.set_cursor_visible(modifiers.shift()), - _ => (), + let result = match key { + Escape => { + control_flow.set_exit(); + Ok(()) + } + G => window.set_cursor_grab(CursorGrabMode::Confined), + L => window.set_cursor_grab(CursorGrabMode::Locked), + A => window.set_cursor_grab(CursorGrabMode::None), + H => { + window.set_cursor_visible(modifiers.shift()); + Ok(()) + } + _ => Ok(()), + }; + + if let Err(err) = result { + println!("error: {}", err); } } WindowEvent::ModifiersChanged(m) => modifiers = m, diff --git a/examples/multithreaded.rs b/examples/multithreaded.rs index d4f7652950..9dc125079b 100644 --- a/examples/multithreaded.rs +++ b/examples/multithreaded.rs @@ -9,7 +9,7 @@ fn main() { dpi::{PhysicalPosition, PhysicalSize, Position, Size}, event::{ElementState, Event, KeyboardInput, VirtualKeyCode, WindowEvent}, event_loop::EventLoop, - window::{CursorIcon, Fullscreen, WindowBuilder}, + window::{CursorGrabMode, CursorIcon, Fullscreen, WindowBuilder}, }; const WINDOW_COUNT: usize = 3; @@ -88,7 +88,21 @@ fn main() { } (false, _) => None, }), - G => window.set_cursor_grab(state).unwrap(), + L if state => { + if let Err(err) = window.set_cursor_grab(CursorGrabMode::Locked) { + println!("error: {}", err); + } + } + G if state => { + if let Err(err) = window.set_cursor_grab(CursorGrabMode::Confined) { + println!("error: {}", err); + } + } + G | L if !state => { + if let Err(err) = window.set_cursor_grab(CursorGrabMode::None) { + println!("error: {}", err); + } + } H => window.set_cursor_visible(!state), I => { println!("Info:"); diff --git a/src/platform_impl/android/mod.rs b/src/platform_impl/android/mod.rs index c472e10723..d7b643b381 100644 --- a/src/platform_impl/android/mod.rs +++ b/src/platform_impl/android/mod.rs @@ -20,7 +20,8 @@ use crate::{ error, event::{self, VirtualKeyCode}, event_loop::{self, ControlFlow}, - monitor, window, + monitor, + window::{self, CursorGrabMode}, }; static CONFIG: Lazy> = Lazy::new(|| { @@ -765,7 +766,7 @@ impl Window { )) } - pub fn set_cursor_grab(&self, _: bool) -> Result<(), error::ExternalError> { + pub fn set_cursor_grab(&self, _: CursorGrabMode) -> Result<(), error::ExternalError> { Err(error::ExternalError::NotSupported( error::NotSupportedError::new(), )) diff --git a/src/platform_impl/ios/window.rs b/src/platform_impl/ios/window.rs index b597f93486..52356099e9 100644 --- a/src/platform_impl/ios/window.rs +++ b/src/platform_impl/ios/window.rs @@ -23,7 +23,8 @@ use crate::{ monitor, view, EventLoopWindowTarget, MonitorHandle, }, window::{ - CursorIcon, Fullscreen, UserAttentionType, WindowAttributes, WindowId as RootWindowId, + CursorGrabMode, CursorIcon, Fullscreen, UserAttentionType, WindowAttributes, + WindowId as RootWindowId, }, }; @@ -184,7 +185,7 @@ impl Inner { Err(ExternalError::NotSupported(NotSupportedError::new())) } - pub fn set_cursor_grab(&self, _grab: bool) -> Result<(), ExternalError> { + pub fn set_cursor_grab(&self, _: CursorGrabMode) -> Result<(), ExternalError> { Err(ExternalError::NotSupported(NotSupportedError::new())) } diff --git a/src/platform_impl/linux/mod.rs b/src/platform_impl/linux/mod.rs index 919d4d740b..b2e779479d 100644 --- a/src/platform_impl/linux/mod.rs +++ b/src/platform_impl/linux/mod.rs @@ -9,8 +9,6 @@ #[cfg(all(not(feature = "x11"), not(feature = "wayland")))] compile_error!("Please select a feature to build for unix: `x11`, `wayland`"); -#[cfg(feature = "wayland")] -use crate::window::Theme; #[cfg(feature = "wayland")] use std::error::Error; @@ -28,6 +26,8 @@ use raw_window_handle::RawWindowHandle; pub use self::x11::XNotSupported; #[cfg(feature = "x11")] use self::x11::{ffi::XVisualInfo, util::WindowType as XWindowType, XConnection, XError}; +#[cfg(feature = "wayland")] +use crate::window::Theme; use crate::{ dpi::{PhysicalPosition, PhysicalSize, Position, Size}, error::{ExternalError, NotSupportedError, OsError as RootOsError}, @@ -37,7 +37,7 @@ use crate::{ }, icon::Icon, monitor::{MonitorHandle as RootMonitorHandle, VideoMode as RootVideoMode}, - window::{CursorIcon, Fullscreen, UserAttentionType, WindowAttributes}, + window::{CursorGrabMode, CursorIcon, Fullscreen, UserAttentionType, WindowAttributes}, }; pub(crate) use crate::icon::RgbaIcon as PlatformIcon; @@ -388,8 +388,8 @@ impl Window { } #[inline] - pub fn set_cursor_grab(&self, grab: bool) -> Result<(), ExternalError> { - x11_or_wayland!(match self; Window(window) => window.set_cursor_grab(grab)) + pub fn set_cursor_grab(&self, mode: CursorGrabMode) -> Result<(), ExternalError> { + x11_or_wayland!(match self; Window(window) => window.set_cursor_grab(mode)) } #[inline] diff --git a/src/platform_impl/linux/wayland/env.rs b/src/platform_impl/linux/wayland/env.rs index ae213cb64a..f3e9b2401c 100644 --- a/src/platform_impl/linux/wayland/env.rs +++ b/src/platform_impl/linux/wayland/env.rs @@ -24,23 +24,23 @@ use sctk::shm::ShmHandler; /// Set of extra features that are supported by the compositor. #[derive(Debug, Clone, Copy)] pub struct WindowingFeatures { - cursor_grab: bool, + pointer_constraints: bool, xdg_activation: bool, } impl WindowingFeatures { /// Create `WindowingFeatures` based on the presented interfaces. pub fn new(env: &Environment) -> Self { - let cursor_grab = env.get_global::().is_some(); + let pointer_constraints = env.get_global::().is_some(); let xdg_activation = env.get_global::().is_some(); Self { - cursor_grab, + pointer_constraints, xdg_activation, } } - pub fn cursor_grab(&self) -> bool { - self.cursor_grab + pub fn pointer_constraints(&self) -> bool { + self.pointer_constraints } pub fn xdg_activation(&self) -> bool { diff --git a/src/platform_impl/linux/wayland/seat/pointer/data.rs b/src/platform_impl/linux/wayland/seat/pointer/data.rs index 310e692b43..17e7a57a07 100644 --- a/src/platform_impl/linux/wayland/seat/pointer/data.rs +++ b/src/platform_impl/linux/wayland/seat/pointer/data.rs @@ -5,8 +5,9 @@ use std::rc::Rc; use sctk::reexports::client::protocol::wl_surface::WlSurface; use sctk::reexports::client::Attached; -use sctk::reexports::protocols::unstable::pointer_constraints::v1::client::zwp_pointer_constraints_v1::{ZwpPointerConstraintsV1}; +use sctk::reexports::protocols::unstable::pointer_constraints::v1::client::zwp_pointer_constraints_v1::ZwpPointerConstraintsV1; use sctk::reexports::protocols::unstable::pointer_constraints::v1::client::zwp_confined_pointer_v1::ZwpConfinedPointerV1; +use sctk::reexports::protocols::unstable::pointer_constraints::v1::client::zwp_locked_pointer_v1::ZwpLockedPointerV1; use crate::event::{ModifiersState, TouchPhase}; @@ -25,6 +26,7 @@ pub(super) struct PointerData { pub pointer_constraints: Option>, pub confined_pointer: Rc>>, + pub locked_pointer: Rc>>, /// Latest observed serial in pointer events. pub latest_serial: Rc>, @@ -39,6 +41,7 @@ pub(super) struct PointerData { impl PointerData { pub fn new( confined_pointer: Rc>>, + locked_pointer: Rc>>, pointer_constraints: Option>, modifiers_state: Rc>, ) -> Self { @@ -47,6 +50,7 @@ impl PointerData { latest_serial: Rc::new(Cell::new(0)), latest_enter_serial: Rc::new(Cell::new(0)), confined_pointer, + locked_pointer, modifiers_state, pointer_constraints, axis_data: AxisData::new(), diff --git a/src/platform_impl/linux/wayland/seat/pointer/handlers.rs b/src/platform_impl/linux/wayland/seat/pointer/handlers.rs index 54ca19394f..7348cc66f1 100644 --- a/src/platform_impl/linux/wayland/seat/pointer/handlers.rs +++ b/src/platform_impl/linux/wayland/seat/pointer/handlers.rs @@ -60,6 +60,7 @@ pub(super) fn handle_pointer( let winit_pointer = WinitPointer { pointer, confined_pointer: Rc::downgrade(&pointer_data.confined_pointer), + locked_pointer: Rc::downgrade(&pointer_data.locked_pointer), pointer_constraints: pointer_data.pointer_constraints.clone(), latest_serial: pointer_data.latest_serial.clone(), latest_enter_serial: pointer_data.latest_enter_serial.clone(), @@ -104,6 +105,7 @@ pub(super) fn handle_pointer( let winit_pointer = WinitPointer { pointer, confined_pointer: Rc::downgrade(&pointer_data.confined_pointer), + locked_pointer: Rc::downgrade(&pointer_data.locked_pointer), pointer_constraints: pointer_data.pointer_constraints.clone(), latest_serial: pointer_data.latest_serial.clone(), latest_enter_serial: pointer_data.latest_enter_serial.clone(), diff --git a/src/platform_impl/linux/wayland/seat/pointer/mod.rs b/src/platform_impl/linux/wayland/seat/pointer/mod.rs index e1b34a5e80..b43deebb38 100644 --- a/src/platform_impl/linux/wayland/seat/pointer/mod.rs +++ b/src/platform_impl/linux/wayland/seat/pointer/mod.rs @@ -11,6 +11,7 @@ use sctk::reexports::protocols::unstable::relative_pointer::v1::client::zwp_rela use sctk::reexports::protocols::unstable::relative_pointer::v1::client::zwp_relative_pointer_v1::ZwpRelativePointerV1; use sctk::reexports::protocols::unstable::pointer_constraints::v1::client::zwp_pointer_constraints_v1::{ZwpPointerConstraintsV1, Lifetime}; use sctk::reexports::protocols::unstable::pointer_constraints::v1::client::zwp_confined_pointer_v1::ZwpConfinedPointerV1; +use sctk::reexports::protocols::unstable::pointer_constraints::v1::client::zwp_locked_pointer_v1::ZwpLockedPointerV1; use sctk::seat::pointer::{ThemeManager, ThemedPointer}; use sctk::window::Window; @@ -35,9 +36,13 @@ pub struct WinitPointer { /// Cursor to handle confine requests. confined_pointer: Weak>>, + /// Cursor to handle locked requests. + locked_pointer: Weak>>, + /// Latest observed serial in pointer events. /// used by Window::start_interactive_move() latest_serial: Rc>, + /// Latest observed serial in pointer enter events. /// used by Window::set_cursor() latest_enter_serial: Rc>, @@ -157,6 +162,52 @@ impl WinitPointer { } } + pub fn lock(&self, surface: &WlSurface) { + let pointer_constraints = match &self.pointer_constraints { + Some(pointer_constraints) => pointer_constraints, + None => return, + }; + + let locked_pointer = match self.locked_pointer.upgrade() { + Some(locked_pointer) => locked_pointer, + // A pointer is gone. + None => return, + }; + + *locked_pointer.borrow_mut() = Some(init_locked_pointer( + pointer_constraints, + surface, + &*self.pointer, + )); + } + + pub fn unlock(&self) { + let locked_pointer = match self.locked_pointer.upgrade() { + Some(locked_pointer) => locked_pointer, + // A pointer is gone. + None => return, + }; + + let mut locked_pointer = locked_pointer.borrow_mut(); + + if let Some(locked_pointer) = locked_pointer.take() { + locked_pointer.destroy(); + } + } + + pub fn set_cursor_position(&self, surface_x: u32, surface_y: u32) { + let locked_pointer = match self.locked_pointer.upgrade() { + Some(locked_pointer) => locked_pointer, + // A pointer is gone. + None => return, + }; + + let locked_pointer = locked_pointer.borrow_mut(); + if let Some(locked_pointer) = locked_pointer.as_ref() { + locked_pointer.set_cursor_position_hint(surface_x.into(), surface_y.into()); + } + } + pub fn drag_window(&self, window: &Window) { // WlPointer::setart_interactive_move() expects the last serial of *any* // pointer event (compare to set_cursor()). @@ -174,6 +225,9 @@ pub(super) struct Pointers { /// Confined pointer. confined_pointer: Rc>>, + + /// Locked pointer. + locked_pointer: Rc>>, } impl Pointers { @@ -185,11 +239,15 @@ impl Pointers { modifiers_state: Rc>, ) -> Self { let confined_pointer = Rc::new(RefCell::new(None)); + let locked_pointer = Rc::new(RefCell::new(None)); + let pointer_data = Rc::new(RefCell::new(PointerData::new( confined_pointer.clone(), + locked_pointer.clone(), pointer_constraints.clone(), modifiers_state, ))); + let pointer_seat = seat.detach(); let pointer = theme_manager.theme_pointer_with_impl( seat, @@ -216,6 +274,7 @@ impl Pointers { pointer, relative_pointer, confined_pointer, + locked_pointer, } } } @@ -232,6 +291,11 @@ impl Drop for Pointers { confined_pointer.destroy(); } + // Drop lock ponter. + if let Some(locked_pointer) = self.locked_pointer.borrow_mut().take() { + locked_pointer.destroy(); + } + // Drop the pointer itself in case it's possible. if self.pointer.as_ref().version() >= 3 { self.pointer.release(); @@ -264,3 +328,16 @@ pub(super) fn init_confined_pointer( confined_pointer.detach() } + +pub(super) fn init_locked_pointer( + pointer_constraints: &Attached, + surface: &WlSurface, + pointer: &WlPointer, +) -> ZwpLockedPointerV1 { + let locked_pointer = + pointer_constraints.lock_pointer(surface, pointer, None, Lifetime::Persistent); + + locked_pointer.quick_assign(move |_, _, _| {}); + + locked_pointer.detach() +} diff --git a/src/platform_impl/linux/wayland/window/mod.rs b/src/platform_impl/linux/wayland/window/mod.rs index f9c1079785..fe7fb42521 100644 --- a/src/platform_impl/linux/wayland/window/mod.rs +++ b/src/platform_impl/linux/wayland/window/mod.rs @@ -17,7 +17,9 @@ use crate::platform_impl::{ MonitorHandle as PlatformMonitorHandle, OsError, PlatformSpecificWindowBuilderAttributes as PlatformAttributes, }; -use crate::window::{CursorIcon, Fullscreen, Theme, UserAttentionType, WindowAttributes}; +use crate::window::{ + CursorGrabMode, CursorIcon, Fullscreen, Theme, UserAttentionType, WindowAttributes, +}; use super::env::WindowingFeatures; use super::event_loop::WinitState; @@ -72,6 +74,9 @@ pub struct Window { /// Whether the window is decorated. decorated: AtomicBool, + + /// Grabbing mode. + cursor_grab_mode: Mutex, } impl Window { @@ -285,6 +290,7 @@ impl Window { windowing_features, resizeable: AtomicBool::new(attributes.resizable), decorated: AtomicBool::new(attributes.decorations), + cursor_grab_mode: Mutex::new(CursorGrabMode::None), }; Ok(window) @@ -474,12 +480,17 @@ impl Window { } #[inline] - pub fn set_cursor_grab(&self, grab: bool) -> Result<(), ExternalError> { - if !self.windowing_features.cursor_grab() { + pub fn set_cursor_grab(&self, mode: CursorGrabMode) -> Result<(), ExternalError> { + if !self.windowing_features.pointer_constraints() { + if mode == CursorGrabMode::None { + return Ok(()); + } + return Err(ExternalError::NotSupported(NotSupportedError::new())); } - self.send_request(WindowRequest::GrabCursor(grab)); + *self.cursor_grab_mode.lock().unwrap() = mode; + self.send_request(WindowRequest::SetCursorGrabMode(mode)); Ok(()) } @@ -494,15 +505,19 @@ impl Window { } #[inline] - pub fn set_cursor_position(&self, _: Position) -> Result<(), ExternalError> { - // XXX This is possible if the locked pointer is being used. We don't have any - // API for that right now, but it could be added in - // https://github.com/rust-windowing/winit/issues/1677. - // - // This function is essential for the locked pointer API. - // - // See pointer-constraints-unstable-v1.xml. - Err(ExternalError::NotSupported(NotSupportedError::new())) + pub fn set_cursor_position(&self, position: Position) -> Result<(), ExternalError> { + // Positon can be set only for locked cursor. + if *self.cursor_grab_mode.lock().unwrap() != CursorGrabMode::Locked { + return Err(ExternalError::Os(os_error!(OsError::WaylandMisc( + "cursor position can be set only for locked cursor." + )))); + } + + let scale_factor = self.scale_factor() as f64; + let position = position.to_logical(scale_factor); + self.send_request(WindowRequest::SetLockedCursorPosition(position)); + + Ok(()) } #[inline] diff --git a/src/platform_impl/linux/wayland/window/shim.rs b/src/platform_impl/linux/wayland/window/shim.rs index 84d511f9fd..08e2a31655 100644 --- a/src/platform_impl/linux/wayland/window/shim.rs +++ b/src/platform_impl/linux/wayland/window/shim.rs @@ -19,7 +19,7 @@ use crate::platform_impl::wayland::event_loop::{EventSink, WinitState}; use crate::platform_impl::wayland::seat::pointer::WinitPointer; use crate::platform_impl::wayland::seat::text_input::TextInputHandler; use crate::platform_impl::wayland::WindowId; -use crate::window::{CursorIcon, Theme, UserAttentionType}; +use crate::window::{CursorGrabMode, CursorIcon, Theme, UserAttentionType}; use super::WinitFrame; @@ -40,8 +40,11 @@ pub enum WindowRequest { /// Change the cursor icon. NewCursorIcon(CursorIcon), - /// Grab cursor. - GrabCursor(bool), + /// Change cursor grabbing mode. + SetCursorGrabMode(CursorGrabMode), + + /// Set cursor position. + SetLockedCursorPosition(LogicalPosition), /// Drag window. DragWindow, @@ -172,7 +175,7 @@ pub struct WindowHandle { cursor_visible: Cell, /// Cursor confined to the surface. - confined: Cell, + cursor_grab_mode: Cell, /// Pointers over the current surface. pointers: Vec, @@ -208,7 +211,7 @@ impl WindowHandle { pending_window_requests, cursor_icon: Cell::new(CursorIcon::Default), is_resizable: Cell::new(true), - confined: Cell::new(false), + cursor_grab_mode: Cell::new(CursorGrabMode::None), cursor_visible: Cell::new(true), pointers: Vec::new(), text_inputs: Vec::new(), @@ -219,24 +222,37 @@ impl WindowHandle { } } - pub fn set_cursor_grab(&self, grab: bool) { + pub fn set_cursor_grab(&self, mode: CursorGrabMode) { // The new requested state matches the current confine status, return. - if self.confined.get() == grab { + let old_mode = self.cursor_grab_mode.replace(mode); + if old_mode == mode { return; } - self.confined.replace(grab); + // Clear old pointer data. + match old_mode { + CursorGrabMode::None => (), + CursorGrabMode::Confined => self.pointers.iter().for_each(|p| p.unconfine()), + CursorGrabMode::Locked => self.pointers.iter().for_each(|p| p.unlock()), + } - for pointer in self.pointers.iter() { - if self.confined.get() { - let surface = self.window.surface(); - pointer.confine(surface); - } else { - pointer.unconfine(); + let surface = self.window.surface(); + match mode { + CursorGrabMode::Locked => self.pointers.iter().for_each(|p| p.lock(surface)), + CursorGrabMode::Confined => self.pointers.iter().for_each(|p| p.confine(surface)), + CursorGrabMode::None => { + // Current lock/confine was already removed. } } } + pub fn set_locked_cursor_position(&self, position: LogicalPosition) { + // XXX the cursor locking is ensured inside `Window`. + self.pointers + .iter() + .for_each(|p| p.set_cursor_position(position.x, position.y)); + } + pub fn set_user_attention(&self, request_type: Option) { let xdg_activation = match self.xdg_activation.as_ref() { None => return, @@ -284,10 +300,13 @@ impl WindowHandle { let position = self.pointers.iter().position(|p| *p == pointer); if position.is_none() { - if self.confined.get() { - let surface = self.window.surface(); - pointer.confine(surface); + let surface = self.window.surface(); + match self.cursor_grab_mode.get() { + CursorGrabMode::None => (), + CursorGrabMode::Locked => pointer.lock(surface), + CursorGrabMode::Confined => pointer.confine(surface), } + self.pointers.push(pointer); } @@ -302,9 +321,11 @@ impl WindowHandle { if let Some(position) = position { let pointer = self.pointers.remove(position); - // Drop the confined pointer. - if self.confined.get() { - pointer.unconfine(); + // Drop the grabbing mode. + match self.cursor_grab_mode.get() { + CursorGrabMode::None => (), + CursorGrabMode::Locked => pointer.unlock(), + CursorGrabMode::Confined => pointer.unconfine(), } } } @@ -428,8 +449,11 @@ pub fn handle_window_requests(winit_state: &mut WinitState) { let event_sink = &mut winit_state.event_sink; window_handle.set_ime_allowed(allow, event_sink); } - WindowRequest::GrabCursor(grab) => { - window_handle.set_cursor_grab(grab); + WindowRequest::SetCursorGrabMode(mode) => { + window_handle.set_cursor_grab(mode); + } + WindowRequest::SetLockedCursorPosition(position) => { + window_handle.set_locked_cursor_position(position); } WindowRequest::DragWindow => { window_handle.drag_window(); diff --git a/src/platform_impl/linux/x11/window.rs b/src/platform_impl/linux/x11/window.rs index 89ac8e46c6..cb65755331 100644 --- a/src/platform_impl/linux/x11/window.rs +++ b/src/platform_impl/linux/x11/window.rs @@ -22,7 +22,7 @@ use crate::{ MonitorHandle as PlatformMonitorHandle, OsError, PlatformSpecificWindowBuilderAttributes, VideoMode as PlatformVideoMode, }, - window::{CursorIcon, Fullscreen, Icon, UserAttentionType, WindowAttributes}, + window::{CursorGrabMode, CursorIcon, Fullscreen, Icon, UserAttentionType, WindowAttributes}, }; use super::{ @@ -106,7 +106,7 @@ pub struct UnownedWindow { root: ffi::Window, // never changes screen_id: i32, // never changes cursor: Mutex, - cursor_grabbed: Mutex, + cursor_grabbed_mode: Mutex, cursor_visible: Mutex, ime_sender: Mutex, pub shared_state: Mutex, @@ -276,7 +276,7 @@ impl UnownedWindow { root, screen_id, cursor: Default::default(), - cursor_grabbed: Mutex::new(false), + cursor_grabbed_mode: Mutex::new(CursorGrabMode::None), cursor_visible: Mutex::new(true), ime_sender: Mutex::new(event_loop.ime_sender.clone()), shared_state: SharedState::new(guessed_monitor, &window_attrs), @@ -1272,64 +1272,75 @@ impl UnownedWindow { } #[inline] - pub fn set_cursor_grab(&self, grab: bool) -> Result<(), ExternalError> { - let mut grabbed_lock = self.cursor_grabbed.lock(); - if grab == *grabbed_lock { + pub fn set_cursor_grab(&self, mode: CursorGrabMode) -> Result<(), ExternalError> { + let mut grabbed_lock = self.cursor_grabbed_mode.lock(); + if mode == *grabbed_lock { return Ok(()); } + unsafe { // We ungrab before grabbing to prevent passive grabs from causing `AlreadyGrabbed`. // Therefore, this is common to both codepaths. (self.xconn.xlib.XUngrabPointer)(self.xconn.display, ffi::CurrentTime); } - let result = if grab { - let result = unsafe { - (self.xconn.xlib.XGrabPointer)( - self.xconn.display, - self.xwindow, - ffi::True, - (ffi::ButtonPressMask - | ffi::ButtonReleaseMask - | ffi::EnterWindowMask - | ffi::LeaveWindowMask - | ffi::PointerMotionMask - | ffi::PointerMotionHintMask - | ffi::Button1MotionMask - | ffi::Button2MotionMask - | ffi::Button3MotionMask - | ffi::Button4MotionMask - | ffi::Button5MotionMask - | ffi::ButtonMotionMask - | ffi::KeymapStateMask) as c_uint, - ffi::GrabModeAsync, - ffi::GrabModeAsync, - self.xwindow, - 0, - ffi::CurrentTime, - ) - }; - match result { - ffi::GrabSuccess => Ok(()), - ffi::AlreadyGrabbed => { - Err("Cursor could not be grabbed: already grabbed by another client") - } - ffi::GrabInvalidTime => Err("Cursor could not be grabbed: invalid time"), - ffi::GrabNotViewable => { - Err("Cursor could not be grabbed: grab location not viewable") + let result = match mode { + CursorGrabMode::None => self + .xconn + .flush_requests() + .map_err(|err| ExternalError::Os(os_error!(OsError::XError(err)))), + CursorGrabMode::Confined => { + let result = unsafe { + (self.xconn.xlib.XGrabPointer)( + self.xconn.display, + self.xwindow, + ffi::True, + (ffi::ButtonPressMask + | ffi::ButtonReleaseMask + | ffi::EnterWindowMask + | ffi::LeaveWindowMask + | ffi::PointerMotionMask + | ffi::PointerMotionHintMask + | ffi::Button1MotionMask + | ffi::Button2MotionMask + | ffi::Button3MotionMask + | ffi::Button4MotionMask + | ffi::Button5MotionMask + | ffi::ButtonMotionMask + | ffi::KeymapStateMask) as c_uint, + ffi::GrabModeAsync, + ffi::GrabModeAsync, + self.xwindow, + 0, + ffi::CurrentTime, + ) + }; + + match result { + ffi::GrabSuccess => Ok(()), + ffi::AlreadyGrabbed => { + Err("Cursor could not be confined: already confined by another client") + } + ffi::GrabInvalidTime => Err("Cursor could not be confined: invalid time"), + ffi::GrabNotViewable => { + Err("Cursor could not be confined: confine location not viewable") + } + ffi::GrabFrozen => { + Err("Cursor could not be confined: frozen by another client") + } + _ => unreachable!(), } - ffi::GrabFrozen => Err("Cursor could not be grabbed: frozen by another client"), - _ => unreachable!(), + .map_err(|err| ExternalError::Os(os_error!(OsError::XMisc(err)))) + } + CursorGrabMode::Locked => { + return Err(ExternalError::NotSupported(NotSupportedError::new())); } - .map_err(|err| ExternalError::Os(os_error!(OsError::XMisc(err)))) - } else { - self.xconn - .flush_requests() - .map_err(|err| ExternalError::Os(os_error!(OsError::XError(err)))) }; + if result.is_ok() { - *grabbed_lock = grab; + *grabbed_lock = mode; } + result } @@ -1386,14 +1397,14 @@ impl UnownedWindow { // we can't use `set_cursor_grab(false)` here because it doesn't run `XUngrabPointer` // if the cursor isn't currently grabbed - let mut grabbed_lock = self.cursor_grabbed.lock(); + let mut grabbed_lock = self.cursor_grabbed_mode.lock(); unsafe { (self.xconn.xlib.XUngrabPointer)(self.xconn.display, ffi::CurrentTime); } self.xconn .flush_requests() .map_err(|err| ExternalError::Os(os_error!(OsError::XError(err))))?; - *grabbed_lock = false; + *grabbed_lock = CursorGrabMode::None; // we keep the lock until we are done self.xconn diff --git a/src/platform_impl/macos/window.rs b/src/platform_impl/macos/window.rs index 4c1ec0bec0..35c944ce8b 100644 --- a/src/platform_impl/macos/window.rs +++ b/src/platform_impl/macos/window.rs @@ -29,7 +29,8 @@ use crate::{ OsError, }, window::{ - CursorIcon, Fullscreen, UserAttentionType, WindowAttributes, WindowId as RootWindowId, + CursorGrabMode, CursorIcon, Fullscreen, UserAttentionType, WindowAttributes, + WindowId as RootWindowId, }, }; use cocoa::{ @@ -621,9 +622,17 @@ impl UnownedWindow { } #[inline] - pub fn set_cursor_grab(&self, grab: bool) -> Result<(), ExternalError> { + pub fn set_cursor_grab(&self, mode: CursorGrabMode) -> Result<(), ExternalError> { + let associate_mouse_cursor = match mode { + CursorGrabMode::Locked => false, + CursorGrabMode::None => true, + CursorGrabMode::Confined => { + return Err(ExternalError::NotSupported(NotSupportedError::new())) + } + }; + // TODO: Do this for real https://stackoverflow.com/a/40922095/5435443 - CGDisplay::associate_mouse_and_mouse_cursor_position(!grab) + CGDisplay::associate_mouse_and_mouse_cursor_position(associate_mouse_cursor) .map_err(|status| ExternalError::Os(os_error!(OsError::CGError(status)))) } diff --git a/src/platform_impl/web/web_sys/canvas.rs b/src/platform_impl/web/web_sys/canvas.rs index 83669b2dcd..521e88a83f 100644 --- a/src/platform_impl/web/web_sys/canvas.rs +++ b/src/platform_impl/web/web_sys/canvas.rs @@ -89,8 +89,8 @@ impl Canvas { }) } - pub fn set_cursor_grab(&self, grab: bool) -> Result<(), RootOE> { - if grab { + pub fn set_cursor_lock(&self, lock: bool) -> Result<(), RootOE> { + if lock { self.raw().request_pointer_lock(); } else { let window = web_sys::window() diff --git a/src/platform_impl/web/window.rs b/src/platform_impl/web/window.rs index bd5791cbb5..51dc98ae5b 100644 --- a/src/platform_impl/web/window.rs +++ b/src/platform_impl/web/window.rs @@ -4,7 +4,7 @@ use crate::event; use crate::icon::Icon; use crate::monitor::MonitorHandle as RootMH; use crate::window::{ - CursorIcon, Fullscreen, UserAttentionType, WindowAttributes, WindowId as RootWI, + CursorGrabMode, CursorIcon, Fullscreen, UserAttentionType, WindowAttributes, WindowId as RootWI, }; use raw_window_handle::{RawWindowHandle, WebHandle}; @@ -216,10 +216,18 @@ impl Window { } #[inline] - pub fn set_cursor_grab(&self, grab: bool) -> Result<(), ExternalError> { + pub fn set_cursor_grab(&self, mode: CursorGrabMode) -> Result<(), ExternalError> { + let lock = match mode { + CursorGrabMode::None => false, + CursorGrabMode::Locked => true, + CursorGrabMode::Confined => { + return Err(ExternalError::NotSupported(NotSupportedError::new())) + } + }; + self.canvas .borrow() - .set_cursor_grab(grab) + .set_cursor_lock(lock) .map_err(ExternalError::Os) } diff --git a/src/platform_impl/windows/window.rs b/src/platform_impl/windows/window.rs index 3e0d98fd5a..bc55a2a4b1 100644 --- a/src/platform_impl/windows/window.rs +++ b/src/platform_impl/windows/window.rs @@ -69,7 +69,7 @@ use crate::{ window_state::{CursorFlags, SavedWindow, WindowFlags, WindowState}, Parent, PlatformSpecificWindowBuilderAttributes, WindowId, }, - window::{CursorIcon, Fullscreen, Theme, UserAttentionType, WindowAttributes}, + window::{CursorGrabMode, CursorIcon, Fullscreen, Theme, UserAttentionType, WindowAttributes}, }; /// The Win32 implementation of the main `Window` object. @@ -276,7 +276,15 @@ impl Window { } #[inline] - pub fn set_cursor_grab(&self, grab: bool) -> Result<(), ExternalError> { + pub fn set_cursor_grab(&self, mode: CursorGrabMode) -> Result<(), ExternalError> { + let confine = match mode { + CursorGrabMode::None => false, + CursorGrabMode::Confined => true, + CursorGrabMode::Locked => { + return Err(ExternalError::NotSupported(NotSupportedError::new())) + } + }; + let window = self.window.clone(); let window_state = Arc::clone(&self.window_state); let (tx, rx) = channel(); @@ -286,7 +294,7 @@ impl Window { let result = window_state .lock() .mouse - .set_cursor_flags(window.0, |f| f.set(CursorFlags::GRABBED, grab)) + .set_cursor_flags(window.0, |f| f.set(CursorFlags::GRABBED, confine)) .map_err(|e| ExternalError::Os(os_error!(e))); let _ = tx.send(result); }); diff --git a/src/window.rs b/src/window.rs index 50fcdbdc66..666ebe36cb 100644 --- a/src/window.rs +++ b/src/window.rs @@ -906,18 +906,24 @@ impl Window { self.window.set_cursor_position(position.into()) } - /// Grabs the cursor, preventing it from leaving the window. + /// Set grabbing [mode]([`CursorGrabMode`]) on the cursor preventing it from leaving the window. /// - /// There's no guarantee that the cursor will be hidden. You should - /// hide it by yourself if you want so. + /// # Example /// - /// ## Platform-specific + /// First try confining the cursor, and if that fails, try locking it instead. /// - /// - **macOS:** This locks the cursor in a fixed location, which looks visually awkward. - /// - **iOS / Android:** Always returns an [`ExternalError::NotSupported`]. + /// ```no-run + /// # use winit::event_loop::EventLoop; + /// # use winit::window::{CursorGrabMode, Window}; + /// # let mut event_loop = EventLoop::new(); + /// # let window = Window::new(&event_loop).unwrap(); + /// window.set_cursor_grab(CursorGrabMode::Confined) + /// .or_else(|_e| window.set_cursor_grab(CursorGrabMode::Locked)) + /// .unwrap(); + /// ``` #[inline] - pub fn set_cursor_grab(&self, grab: bool) -> Result<(), ExternalError> { - self.window.set_cursor_grab(grab) + pub fn set_cursor_grab(&self, mode: CursorGrabMode) -> Result<(), ExternalError> { + self.window.set_cursor_grab(mode) } /// Modifies the cursor's visibility. @@ -1028,6 +1034,38 @@ unsafe impl raw_window_handle::HasRawWindowHandle for Window { } } +/// The behavior of cursor grabbing. +/// +/// Use this enum with [`Window::set_cursor_grab`] to grab the cursor. +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub enum CursorGrabMode { + /// No grabbing of the cursor is performed. + None, + + /// The cursor is confined to the window area. + /// + /// There's no guarantee that the cursor will be hidden. You should hide it by yourself if you + /// want to do so. + /// + /// ## Platform-specific + /// + /// - **macOS:** Not implemented. Always returns [`ExternalError::NotSupported`] for now. + /// - ** iOS / Android / Web:** Always returns an [`ExternalError::NotSupported`]. + Confined, + + /// The cursor is locked inside the window area to the certain position. + /// + /// There's no guarantee that the cursor will be hidden. You should hide it by yourself if you + /// want to do so. + /// + /// ## Platform-specific + /// + /// - **X11 / Windows:** Not implemented. Always returns [`ExternalError::NotSupported`] for now. + /// - ** iOS / Android:** Always returns an [`ExternalError::NotSupported`]. + Locked, +} + /// Describes the appearance of the mouse cursor. #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] From 6b5b570b45c051271877332c4028bcddbb65ee7f Mon Sep 17 00:00:00 2001 From: Marijn Suijten Date: Mon, 13 Jun 2022 16:40:21 +0200 Subject: [PATCH 18/60] examples/window_run_return: Enable on Android (#2321) Android also supports `EventLoopExtRunReturn`. The user will still have to follow the README to turn this example into a `cdylib` and add the `ndk_glue::main()` initialization attribute, though. --- examples/window_run_return.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/examples/window_run_return.rs b/examples/window_run_return.rs index d9a201ac55..e6b3c66211 100644 --- a/examples/window_run_return.rs +++ b/examples/window_run_return.rs @@ -8,7 +8,8 @@ target_os = "dragonfly", target_os = "freebsd", target_os = "netbsd", - target_os = "openbsd" + target_os = "openbsd", + target_os = "android", ))] fn main() { use std::{thread::sleep, time::Duration}; @@ -59,7 +60,7 @@ fn main() { } } -#[cfg(any(target_os = "ios", target_os = "android", target_arch = "wasm32"))] +#[cfg(any(target_os = "ios", target_arch = "wasm32"))] fn main() { println!("This platform doesn't support run_return."); } From 401d20fa1fbdae66958ea4c965e761ae64ce8570 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20R=C3=B8yset?= Date: Mon, 13 Jun 2022 18:24:56 +0200 Subject: [PATCH 19/60] Fix doubled device events on X11 Fixes #2332 --- src/platform_impl/linux/x11/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/platform_impl/linux/x11/mod.rs b/src/platform_impl/linux/x11/mod.rs index 966d526188..9789f03625 100644 --- a/src/platform_impl/linux/x11/mod.rs +++ b/src/platform_impl/linux/x11/mod.rs @@ -555,7 +555,7 @@ impl EventLoopWindowTarget { } self.xconn - .select_xinput_events(self.root, ffi::XIAllDevices, mask) + .select_xinput_events(self.root, ffi::XIAllMasterDevices, mask) .queue(); } } From ac42447459b97a695092cbfcdf28b0591907d496 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nazar=C3=AD=20Gonz=C3=A1lez?= Date: Fri, 17 Jun 2022 00:12:05 +0100 Subject: [PATCH 20/60] macOS: disallow_highdpi will set explicity the value to avoid the SO value by default (#2339) Co-authored-by: Mads Marquart --- CHANGELOG.md | 1 + src/platform_impl/macos/window.rs | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index fdc63ad11e..53725d521e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -60,6 +60,7 @@ And please only add new entries to the top of this list, right below the `# Unre - Implemented `Eq` for `Fullscreen`, `Theme`, and `UserAttentionType`. - **Breaking:** `Window::set_cursor_grab` now accepts `CursorGrabMode` to control grabbing behavior. - On Wayland, add support for `Window::set_cursor_position`. +- Fix on macOS `WindowBuilder::with_disallow_hidpi`, setting true or false by the user no matter the SO default value. # 0.26.1 (2022-01-05) diff --git a/src/platform_impl/macos/window.rs b/src/platform_impl/macos/window.rs index 35c944ce8b..158feebebf 100644 --- a/src/platform_impl/macos/window.rs +++ b/src/platform_impl/macos/window.rs @@ -100,8 +100,13 @@ unsafe fn create_view( ) -> Option<(IdRef, Weak>)> { let (ns_view, cursor_state) = new_view(ns_window); ns_view.non_nil().map(|ns_view| { + // The default value of `setWantsBestResolutionOpenGLSurface:` was `false` until + // macos 10.14 and `true` after 10.15, we should set it to `YES` or `NO` to avoid + // always the default system value in favour of the user's code if !pl_attribs.disallow_hidpi { ns_view.setWantsBestResolutionOpenGLSurface_(YES); + } else { + ns_view.setWantsBestResolutionOpenGLSurface_(NO); } // On Mojave, views automatically become layer-backed shortly after being added to From 2b414cd8252865b5d7d639e8a9000ff577c27808 Mon Sep 17 00:00:00 2001 From: Marijn Suijten Date: Fri, 17 Jun 2022 14:19:09 +0200 Subject: [PATCH 21/60] ci: Disallow warnings in rustdoc and test private items (#2341) Make sure `cargo doc` runs cleanly without any warnings in the CI - some recently introduced but still allowing a PR to get merged. In case someone wishes to add docs on private items, make sure those adhere to the same standards. --- .github/workflows/ci.yml | 3 ++- src/platform_impl/ios/app_state.rs | 10 +++++----- src/platform_impl/linux/x11/event_processor.rs | 2 +- src/platform_impl/macos/observer.rs | 3 ++- src/platform_impl/windows/event_loop.rs | 2 +- src/window.rs | 2 +- 6 files changed, 12 insertions(+), 10 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 843632add7..b02af77fb9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -49,6 +49,7 @@ jobs: OPTIONS: ${{ matrix.platform.options }} FEATURES: ${{ format(',{0}', matrix.platform.features ) }} CMD: ${{ matrix.platform.cmd }} + RUSTDOCFLAGS: -Dwarnings runs-on: ${{ matrix.platform.os }} steps: @@ -78,7 +79,7 @@ jobs: - name: Check documentation shell: bash - run: cargo $CMD doc --no-deps --target ${{ matrix.platform.target }} $OPTIONS --features $FEATURES + run: cargo $CMD doc --no-deps --target ${{ matrix.platform.target }} $OPTIONS --features $FEATURES --document-private-items - name: Build shell: bash diff --git a/src/platform_impl/ios/app_state.rs b/src/platform_impl/ios/app_state.rs index ed7592bdf3..55ed3e9855 100644 --- a/src/platform_impl/ios/app_state.rs +++ b/src/platform_impl/ios/app_state.rs @@ -990,20 +990,20 @@ macro_rules! os_capabilities { } os_capabilities! { - /// https://developer.apple.com/documentation/uikit/uiview/2891103-safeareainsets?language=objc + /// #[allow(unused)] // error message unused safe_area_err_msg: "-[UIView safeAreaInsets]", safe_area: 11-0, - /// https://developer.apple.com/documentation/uikit/uiviewcontroller/2887509-setneedsupdateofhomeindicatoraut?language=objc + /// home_indicator_hidden_err_msg: "-[UIViewController setNeedsUpdateOfHomeIndicatorAutoHidden]", home_indicator_hidden: 11-0, - /// https://developer.apple.com/documentation/uikit/uiviewcontroller/2887507-setneedsupdateofscreenedgesdefer?language=objc + /// defer_system_gestures_err_msg: "-[UIViewController setNeedsUpdateOfScreenEdgesDeferringSystem]", defer_system_gestures: 11-0, - /// https://developer.apple.com/documentation/uikit/uiscreen/2806814-maximumframespersecond?language=objc + /// maximum_frames_per_second_err_msg: "-[UIScreen maximumFramesPerSecond]", maximum_frames_per_second: 10-3, - /// https://developer.apple.com/documentation/uikit/uitouch/1618110-force?language=objc + /// #[allow(unused)] // error message unused force_touch_err_msg: "-[UITouch force]", force_touch: 9-0, diff --git a/src/platform_impl/linux/x11/event_processor.rs b/src/platform_impl/linux/x11/event_processor.rs index 1a024ac4e4..dea0ec85b9 100644 --- a/src/platform_impl/linux/x11/event_processor.rs +++ b/src/platform_impl/linux/x11/event_processor.rs @@ -22,7 +22,7 @@ use crate::{ event_loop::EventLoopWindowTarget as RootELW, }; -/// The X11 documentation states: "Keycodes lie in the inclusive range [8,255]". +/// The X11 documentation states: "Keycodes lie in the inclusive range `[8, 255]`". const KEYCODE_OFFSET: u8 = 8; pub(super) struct EventProcessor { diff --git a/src/platform_impl/macos/observer.rs b/src/platform_impl/macos/observer.rs index 308637cb81..2ced84ef5f 100644 --- a/src/platform_impl/macos/observer.rs +++ b/src/platform_impl/macos/observer.rs @@ -99,7 +99,8 @@ pub type CFRunLoopTimerCallBack = extern "C" fn(timer: CFRunLoopTimerRef, info: pub enum CFRunLoopTimerContext {} /// This mirrors the struct with the same name from Core Foundation. -/// https://developer.apple.com/documentation/corefoundation/cfrunloopobservercontext?language=objc +/// +/// #[allow(non_snake_case)] #[repr(C)] pub struct CFRunLoopObserverContext { diff --git a/src/platform_impl/windows/event_loop.rs b/src/platform_impl/windows/event_loop.rs index ff6ad5f76c..7e4b7fbdb8 100644 --- a/src/platform_impl/windows/event_loop.rs +++ b/src/platform_impl/windows/event_loop.rs @@ -333,7 +333,7 @@ impl EventLoopWindowTarget { /// entrypoint. /// /// Full details of CRT initialization can be found here: -/// https://docs.microsoft.com/en-us/cpp/c-runtime-library/crt-initialization?view=msvc-160 +/// fn main_thread_id() -> u32 { static mut MAIN_THREAD_ID: u32 = 0; diff --git a/src/window.rs b/src/window.rs index 666ebe36cb..ea997d6b09 100644 --- a/src/window.rs +++ b/src/window.rs @@ -912,7 +912,7 @@ impl Window { /// /// First try confining the cursor, and if that fails, try locking it instead. /// - /// ```no-run + /// ```no_run /// # use winit::event_loop::EventLoop; /// # use winit::window::{CursorGrabMode, Window}; /// # let mut event_loop = EventLoop::new(); From c93ef47b9b05a561d8bfe85905cdfd38663b7c52 Mon Sep 17 00:00:00 2001 From: Kirill Chibisov Date: Mon, 20 Jun 2022 10:19:49 +0300 Subject: [PATCH 22/60] Bump smithay-client-toolkit to v0.16.0 --- Cargo.toml | 4 +-- .../linux/wayland/event_loop/mod.rs | 4 ++- .../linux/wayland/seat/keyboard/mod.rs | 25 ++++--------------- 3 files changed, 10 insertions(+), 23 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 8709e748b6..ce8b35d109 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -102,8 +102,8 @@ features = [ [target.'cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd", target_os = "netbsd"))'.dependencies] wayland-client = { version = "0.29.4", default_features = false, features = ["use_system_lib"], optional = true } wayland-protocols = { version = "0.29.4", features = [ "staging_protocols"], optional = true } -sctk = { package = "smithay-client-toolkit", version = "0.15.4", default_features = false, features = ["calloop"], optional = true } -sctk-adwaita = { version = "0.3.5", optional = true } +sctk = { package = "smithay-client-toolkit", version = "0.16.0", default_features = false, features = ["calloop"], optional = true } +sctk-adwaita = { version = "0.4.0", optional = true } mio = { version = "0.8", features = ["os-ext"], optional = true } x11-dl = { version = "2.18.5", optional = true } percent-encoding = { version = "2.0", optional = true } diff --git a/src/platform_impl/linux/wayland/event_loop/mod.rs b/src/platform_impl/linux/wayland/event_loop/mod.rs index 0f879ea2d9..b2948a9b15 100644 --- a/src/platform_impl/linux/wayland/event_loop/mod.rs +++ b/src/platform_impl/linux/wayland/event_loop/mod.rs @@ -538,6 +538,8 @@ impl EventLoop { _ => unreachable!(), }; - self.event_loop.dispatch(timeout, state) + self.event_loop + .dispatch(timeout, state) + .map_err(|error| error.into()) } } diff --git a/src/platform_impl/linux/wayland/seat/keyboard/mod.rs b/src/platform_impl/linux/wayland/seat/keyboard/mod.rs index c6e0ad456e..262a014bac 100644 --- a/src/platform_impl/linux/wayland/seat/keyboard/mod.rs +++ b/src/platform_impl/linux/wayland/seat/keyboard/mod.rs @@ -7,7 +7,7 @@ use sctk::reexports::client::protocol::wl_keyboard::WlKeyboard; use sctk::reexports::client::protocol::wl_seat::WlSeat; use sctk::reexports::client::Attached; -use sctk::reexports::calloop::{LoopHandle, RegistrationToken}; +use sctk::reexports::calloop::LoopHandle; use sctk::seat::keyboard; @@ -20,12 +20,6 @@ mod keymap; pub(crate) struct Keyboard { pub keyboard: WlKeyboard, - - /// The source for repeat keys. - pub repeat_token: Option, - - /// LoopHandle to drop `RepeatSource`, when dropping the keyboard. - pub loop_handle: LoopHandle<'static, WinitState>, } impl Keyboard { @@ -35,7 +29,7 @@ impl Keyboard { modifiers_state: Rc>, ) -> Option { let mut inner = KeyboardInner::new(modifiers_state); - let keyboard_data = keyboard::map_keyboard_repeat( + let keyboard = keyboard::map_keyboard_repeat( loop_handle.clone(), seat, None, @@ -44,15 +38,10 @@ impl Keyboard { let winit_state = dispatch_data.get::().unwrap(); handlers::handle_keyboard(event, &mut inner, winit_state); }, - ); - - let (keyboard, repeat_token) = keyboard_data.ok()?; + ) + .ok()?; - Some(Self { - keyboard, - loop_handle, - repeat_token: Some(repeat_token), - }) + Some(Self { keyboard }) } } @@ -61,10 +50,6 @@ impl Drop for Keyboard { if self.keyboard.as_ref().version() >= 3 { self.keyboard.release(); } - - if let Some(repeat_token) = self.repeat_token.take() { - self.loop_handle.remove(repeat_token); - } } } From 76b949c1960bdf70459469109283868b1027c789 Mon Sep 17 00:00:00 2001 From: MarcusGrass <34198073+MarcusGrass@users.noreply.github.com> Date: Wed, 22 Jun 2022 19:43:25 +0200 Subject: [PATCH 23/60] Disallow multiple EventLoop creation --- CHANGELOG.md | 1 + src/event_loop.rs | 15 ++++++++++++--- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 53725d521e..17f0f522d2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -61,6 +61,7 @@ And please only add new entries to the top of this list, right below the `# Unre - **Breaking:** `Window::set_cursor_grab` now accepts `CursorGrabMode` to control grabbing behavior. - On Wayland, add support for `Window::set_cursor_position`. - Fix on macOS `WindowBuilder::with_disallow_hidpi`, setting true or false by the user no matter the SO default value. +- `EventLoopBuilder::build` will now panic when the `EventLoop` is being created more than once. # 0.26.1 (2022-01-05) diff --git a/src/event_loop.rs b/src/event_loop.rs index 92b807fdcf..890e4e560e 100644 --- a/src/event_loop.rs +++ b/src/event_loop.rs @@ -7,11 +7,13 @@ //! //! See the root-level documentation for information on how to create and use an event loop to //! handle events. -use instant::Instant; use std::marker::PhantomData; use std::ops::Deref; use std::{error, fmt}; +use instant::Instant; +use once_cell::sync::OnceCell; + 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 @@ -76,8 +78,11 @@ impl EventLoopBuilder { /// Builds a new event loop. /// - /// ***For cross-platform compatibility, the [`EventLoop`] must be created on the main thread.*** - /// Attempting to create the event loop on a different thread will panic. This restriction isn't + /// ***For cross-platform compatibility, the [`EventLoop`] must be created on the main thread, + /// and only once per application.*** + /// + /// Attempting to create the event loop on a different thread, or multiple event loops in + /// the same application, will panic. This restriction isn't /// strictly necessary on all platforms, but is imposed to eliminate any nasty surprises when /// porting to platforms that require it. `EventLoopBuilderExt::any_thread` functions are exposed /// in the relevant [`platform`] module if the target platform supports creating an event loop on @@ -95,6 +100,10 @@ impl EventLoopBuilder { /// [`platform`]: crate::platform #[inline] pub fn build(&mut self) -> EventLoop { + static EVENT_LOOP_CREATED: OnceCell<()> = OnceCell::new(); + if EVENT_LOOP_CREATED.set(()).is_err() { + panic!("Creating EventLoop multiple times is not supported."); + } // Certain platforms accept a mutable reference in their API. #[allow(clippy::unnecessary_mut_passed)] EventLoop { From 64c1d4c5bbcde5e6d30d9c8182bde7a0cfbb9244 Mon Sep 17 00:00:00 2001 From: Amr Bashir Date: Wed, 22 Jun 2022 19:44:00 +0200 Subject: [PATCH 24/60] Fix conflict in `WindowFlags` on Windows --- src/platform_impl/windows/window_state.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/platform_impl/windows/window_state.rs b/src/platform_impl/windows/window_state.rs index 77f8dcda2f..744dca0a18 100644 --- a/src/platform_impl/windows/window_state.rs +++ b/src/platform_impl/windows/window_state.rs @@ -96,7 +96,7 @@ bitflags! { const MINIMIZED = 1 << 12; - const IGNORE_CURSOR_EVENT = 1 << 14; + const IGNORE_CURSOR_EVENT = 1 << 15; const EXCLUSIVE_FULLSCREEN_OR_MASK = WindowFlags::ALWAYS_ON_TOP.bits; const NO_DECORATIONS_AND_MASK = !WindowFlags::RESIZABLE.bits; From 8646cbc9f5898e0d9970ca13b315c6a1beca16cb Mon Sep 17 00:00:00 2001 From: Aaron Hill Date: Thu, 23 Jun 2022 10:59:04 -0500 Subject: [PATCH 25/60] Map XK_Caps_Lock to VirtualKeyCode::Capital (#1864) This allows applications to handle events for the caps lock key under X11 --- CHANGELOG.md | 1 + src/platform_impl/linux/x11/events.rs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 17f0f522d2..a7218e15c0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ And please only add new entries to the top of this list, right below the `# Unre # Unreleased +- On X11, fix events for caps lock key not being sent - Build docs on `docs.rs` for iOS and Android as well. - **Breaking:** Removed the `WindowAttributes` struct, since all its functionality is accessible from `WindowBuilder`. - Added `WindowBuilder::transparent` getter to check if the user set `transparent` attribute. diff --git a/src/platform_impl/linux/x11/events.rs b/src/platform_impl/linux/x11/events.rs index 1c34b0d0cb..9063916025 100644 --- a/src/platform_impl/linux/x11/events.rs +++ b/src/platform_impl/linux/x11/events.rs @@ -161,7 +161,7 @@ pub fn keysym_to_element(keysym: libc::c_uint) -> Option { ffi::XK_Shift_R => VirtualKeyCode::RShift, ffi::XK_Control_L => VirtualKeyCode::LControl, ffi::XK_Control_R => VirtualKeyCode::RControl, - //ffi::XK_Caps_Lock => VirtualKeyCode::Caps_lock, + ffi::XK_Caps_Lock => VirtualKeyCode::Capital, //ffi::XK_Shift_Lock => VirtualKeyCode::Shift_lock, //ffi::XK_Meta_L => VirtualKeyCode::Meta_l, //ffi::XK_Meta_R => VirtualKeyCode::Meta_r, From c55d97183d890004650faa4daf1920c3a36b4035 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Fri, 1 Jul 2022 12:07:10 +0100 Subject: [PATCH 26/60] Less redundancy and improve fullscreen in examples Remove examples/minimize which is redundant --- examples/fullscreen.rs | 141 +++++++++++++++++++-------------------- examples/min_max_size.rs | 32 --------- examples/minimize.rs | 41 ------------ examples/resizable.rs | 4 +- 4 files changed, 70 insertions(+), 148 deletions(-) delete mode 100644 examples/min_max_size.rs delete mode 100644 examples/minimize.rs diff --git a/examples/fullscreen.rs b/examples/fullscreen.rs index 4b19efdc56..212bd75505 100644 --- a/examples/fullscreen.rs +++ b/examples/fullscreen.rs @@ -1,39 +1,44 @@ #![allow(clippy::single_match)] -use std::io::{stdin, stdout, Write}; - use simple_logger::SimpleLogger; use winit::event::{ElementState, Event, KeyboardInput, VirtualKeyCode, WindowEvent}; use winit::event_loop::EventLoop; -use winit::monitor::{MonitorHandle, VideoMode}; use winit::window::{Fullscreen, WindowBuilder}; fn main() { SimpleLogger::new().init().unwrap(); let event_loop = EventLoop::new(); - print!("Please choose the fullscreen mode: (1) exclusive, (2) borderless: "); - stdout().flush().unwrap(); - - let mut num = String::new(); - stdin().read_line(&mut num).unwrap(); - let num = num.trim().parse().expect("Please enter a number"); - - let fullscreen = Some(match num { - 1 => Fullscreen::Exclusive(prompt_for_video_mode(&prompt_for_monitor(&event_loop))), - 2 => Fullscreen::Borderless(Some(prompt_for_monitor(&event_loop))), - _ => panic!("Please enter a valid number"), - }); - let mut decorations = true; + let mut minimized = false; let window = WindowBuilder::new() .with_title("Hello world!") - .with_fullscreen(fullscreen.clone()) .build(&event_loop) .unwrap(); - event_loop.run(move |event, _, control_flow| { + let mut monitor_index = 0; + let mut monitor = event_loop + .available_monitors() + .next() + .expect("no monitor found!"); + println!("Monitor: {:?}", monitor.name()); + + let mut mode_index = 0; + let mut mode = monitor.video_modes().next().expect("no mode found"); + println!("Mode: {}", mode); + + println!("Keys:"); + println!("- Esc\tExit"); + println!("- F\tToggle exclusive fullscreen mode"); + println!("- B\tToggle borderless mode"); + println!("- S\tNext screen"); + println!("- M\tNext mode for this screen"); + println!("- D\tToggle window decorations"); + println!("- X\tMaximize window"); + println!("- Z\tMinimize window"); + + event_loop.run(move |event, elwt, control_flow| { control_flow.set_wait(); match event { @@ -43,29 +48,60 @@ fn main() { input: KeyboardInput { virtual_keycode: Some(virtual_code), - state, + state: ElementState::Pressed, .. }, .. - } => match (virtual_code, state) { - (VirtualKeyCode::Escape, _) => control_flow.set_exit(), - (VirtualKeyCode::F, ElementState::Pressed) => { - if window.fullscreen().is_some() { - window.set_fullscreen(None); + } => match virtual_code { + VirtualKeyCode::Escape => control_flow.set_exit(), + VirtualKeyCode::F | VirtualKeyCode::B if window.fullscreen().is_some() => { + window.set_fullscreen(None); + } + VirtualKeyCode::F => { + let fullscreen = Some(Fullscreen::Exclusive(mode.clone())); + println!("Setting mode: {fullscreen:?}"); + window.set_fullscreen(fullscreen); + } + VirtualKeyCode::B => { + let fullscreen = Some(Fullscreen::Borderless(Some(monitor.clone()))); + println!("Setting mode: {fullscreen:?}"); + window.set_fullscreen(fullscreen); + } + VirtualKeyCode::S => { + monitor_index += 1; + if let Some(mon) = elwt.available_monitors().nth(monitor_index) { + monitor = mon; } else { - window.set_fullscreen(fullscreen.clone()); + monitor_index = 0; + monitor = elwt.available_monitors().next().expect("no monitor found!"); } + println!("Monitor: {:?}", monitor.name()); + + mode_index = 0; + mode = monitor.video_modes().next().expect("no mode found"); + println!("Mode: {}", mode); } - (VirtualKeyCode::S, ElementState::Pressed) => { - println!("window.fullscreen {:?}", window.fullscreen()); + VirtualKeyCode::M => { + mode_index += 1; + if let Some(m) = monitor.video_modes().nth(mode_index) { + mode = m; + } else { + mode_index = 0; + mode = monitor.video_modes().next().expect("no mode found"); + } + println!("Mode: {}", mode); + } + VirtualKeyCode::D => { + decorations = !decorations; + window.set_decorations(decorations); } - (VirtualKeyCode::M, ElementState::Pressed) => { + VirtualKeyCode::X => { let is_maximized = window.is_maximized(); window.set_maximized(!is_maximized); } - (VirtualKeyCode::D, ElementState::Pressed) => { - decorations = !decorations; - window.set_decorations(decorations); + VirtualKeyCode::Z => { + minimized = !minimized; + window.set_minimized(minimized); } _ => (), }, @@ -75,46 +111,3 @@ fn main() { } }); } - -// Enumerate monitors and prompt user to choose one -fn prompt_for_monitor(event_loop: &EventLoop<()>) -> MonitorHandle { - for (num, monitor) in event_loop.available_monitors().enumerate() { - println!("Monitor #{}: {:?}", num, monitor.name()); - } - - print!("Please write the number of the monitor to use: "); - stdout().flush().unwrap(); - - let mut num = String::new(); - stdin().read_line(&mut num).unwrap(); - let num = num.trim().parse().expect("Please enter a number"); - let monitor = event_loop - .available_monitors() - .nth(num) - .expect("Please enter a valid ID"); - - println!("Using {:?}", monitor.name()); - - monitor -} - -fn prompt_for_video_mode(monitor: &MonitorHandle) -> VideoMode { - for (i, video_mode) in monitor.video_modes().enumerate() { - println!("Video mode #{}: {}", i, video_mode); - } - - print!("Please write the number of the video mode to use: "); - stdout().flush().unwrap(); - - let mut num = String::new(); - stdin().read_line(&mut num).unwrap(); - let num = num.trim().parse().expect("Please enter a number"); - let video_mode = monitor - .video_modes() - .nth(num) - .expect("Please enter a valid ID"); - - println!("Using {}", video_mode); - - video_mode -} diff --git a/examples/min_max_size.rs b/examples/min_max_size.rs deleted file mode 100644 index 84852b3e71..0000000000 --- a/examples/min_max_size.rs +++ /dev/null @@ -1,32 +0,0 @@ -#![allow(clippy::single_match)] - -use simple_logger::SimpleLogger; -use winit::{ - dpi::LogicalSize, - event::{Event, WindowEvent}, - event_loop::EventLoop, - window::WindowBuilder, -}; - -fn main() { - SimpleLogger::new().init().unwrap(); - let event_loop = EventLoop::new(); - - let window = WindowBuilder::new().build(&event_loop).unwrap(); - - window.set_min_inner_size(Some(LogicalSize::new(400.0, 200.0))); - window.set_max_inner_size(Some(LogicalSize::new(800.0, 400.0))); - - event_loop.run(move |event, _, control_flow| { - control_flow.set_wait(); - println!("{:?}", event); - - match event { - Event::WindowEvent { - event: WindowEvent::CloseRequested, - .. - } => control_flow.set_exit(), - _ => (), - } - }); -} diff --git a/examples/minimize.rs b/examples/minimize.rs deleted file mode 100644 index 55a62d1542..0000000000 --- a/examples/minimize.rs +++ /dev/null @@ -1,41 +0,0 @@ -#![allow(clippy::single_match)] - -use simple_logger::SimpleLogger; -use winit::event::{Event, VirtualKeyCode, WindowEvent}; -use winit::event_loop::EventLoop; -use winit::window::WindowBuilder; - -fn main() { - SimpleLogger::new().init().unwrap(); - let event_loop = EventLoop::new(); - - let window = WindowBuilder::new() - .with_title("A fantastic window!") - .build(&event_loop) - .unwrap(); - - event_loop.run(move |event, _, control_flow| { - control_flow.set_wait(); - - match event { - Event::WindowEvent { - event: WindowEvent::CloseRequested, - .. - } => control_flow.set_exit(), - - // Keyboard input event to handle minimize via a hotkey - Event::WindowEvent { - event: WindowEvent::KeyboardInput { input, .. }, - window_id, - } => { - if window_id == window.id() { - // Pressing the 'M' key will minimize the window - if input.virtual_keycode == Some(VirtualKeyCode::M) { - window.set_minimized(true); - } - } - } - _ => (), - } - }); -} diff --git a/examples/resizable.rs b/examples/resizable.rs index 1d10d4d83b..9e438a51d9 100644 --- a/examples/resizable.rs +++ b/examples/resizable.rs @@ -16,7 +16,9 @@ fn main() { let window = WindowBuilder::new() .with_title("Hit space to toggle resizability.") - .with_inner_size(LogicalSize::new(400.0, 200.0)) + .with_inner_size(LogicalSize::new(600.0, 300.0)) + .with_min_inner_size(LogicalSize::new(400.0, 200.0)) + .with_max_inner_size(LogicalSize::new(800.0, 400.0)) .with_resizable(resizable) .build(&event_loop) .unwrap(); From cb41c58f2146774dbeeed450a0b40a64e3ce3417 Mon Sep 17 00:00:00 2001 From: Kirill Chibisov Date: Sat, 2 Jul 2022 14:27:19 +0300 Subject: [PATCH 27/60] Implement From for WindowId and vise-versa This should help downstream applications to expose WindowId to the end users via e.g. IPC to control particular windows in multi window systems. --- CHANGELOG.md | 1 + src/platform_impl/android/mod.rs | 12 ++++++++ src/platform_impl/ios/window.rs | 14 +++++++++ src/platform_impl/linux/mod.rs | 29 ++++++++++++------- .../linux/wayland/event_loop/mod.rs | 16 +++------- .../linux/wayland/event_loop/sink.rs | 4 +-- src/platform_impl/linux/wayland/mod.rs | 12 ++------ .../linux/x11/event_processor.rs | 10 ++----- src/platform_impl/linux/x11/mod.rs | 25 +++++----------- src/platform_impl/linux/x11/window.rs | 4 +-- src/platform_impl/macos/window.rs | 12 ++++++++ src/platform_impl/web/window.rs | 12 ++++++++ src/platform_impl/windows/mod.rs | 12 ++++++++ src/window.rs | 12 ++++++++ 14 files changed, 115 insertions(+), 60 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a7218e15c0..722ea7aa5f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -63,6 +63,7 @@ And please only add new entries to the top of this list, right below the `# Unre - On Wayland, add support for `Window::set_cursor_position`. - Fix on macOS `WindowBuilder::with_disallow_hidpi`, setting true or false by the user no matter the SO default value. - `EventLoopBuilder::build` will now panic when the `EventLoop` is being created more than once. +- Added `From` for `WindowId` and `From` for `u64`. # 0.26.1 (2022-01-05) diff --git a/src/platform_impl/android/mod.rs b/src/platform_impl/android/mod.rs index d7b643b381..e37bd1f5fe 100644 --- a/src/platform_impl/android/mod.rs +++ b/src/platform_impl/android/mod.rs @@ -627,6 +627,18 @@ impl WindowId { } } +impl From for u64 { + fn from(_: WindowId) -> Self { + 0 + } +} + +impl From for WindowId { + fn from(_: u64) -> Self { + Self + } +} + #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] pub struct DeviceId; diff --git a/src/platform_impl/ios/window.rs b/src/platform_impl/ios/window.rs index 52356099e9..2fc9f3ed6f 100644 --- a/src/platform_impl/ios/window.rs +++ b/src/platform_impl/ios/window.rs @@ -641,6 +641,20 @@ impl WindowId { } } +impl From for u64 { + fn from(window_id: WindowId) -> Self { + window_id.window as u64 + } +} + +impl From for WindowId { + fn from(raw_id: u64) -> Self { + Self { + window: raw_id as _, + } + } +} + unsafe impl Send for WindowId {} unsafe impl Sync for WindowId {} diff --git a/src/platform_impl/linux/mod.rs b/src/platform_impl/linux/mod.rs index b2e779479d..37b1d4533a 100644 --- a/src/platform_impl/linux/mod.rs +++ b/src/platform_impl/linux/mod.rs @@ -162,19 +162,23 @@ pub enum Window { } #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub enum WindowId { - #[cfg(feature = "x11")] - X(x11::WindowId), - #[cfg(feature = "wayland")] - Wayland(wayland::WindowId), +pub struct WindowId(u64); + +impl From for u64 { + fn from(window_id: WindowId) -> Self { + window_id.0 + } +} + +impl From for WindowId { + fn from(raw_id: u64) -> Self { + Self(raw_id) + } } impl WindowId { pub const unsafe fn dummy() -> Self { - #[cfg(feature = "wayland")] - return WindowId::Wayland(wayland::WindowId::dummy()); - #[cfg(all(not(feature = "wayland"), feature = "x11"))] - return WindowId::X(x11::WindowId::dummy()); + Self(0) } } @@ -314,7 +318,12 @@ impl Window { #[inline] pub fn id(&self) -> WindowId { - x11_or_wayland!(match self; Window(w) => w.id(); as WindowId) + match self { + #[cfg(feature = "wayland")] + Self::Wayland(window) => window.id(), + #[cfg(feature = "x11")] + Self::X(window) => window.id(), + } } #[inline] diff --git a/src/platform_impl/linux/wayland/event_loop/mod.rs b/src/platform_impl/linux/wayland/event_loop/mod.rs index b2948a9b15..491883ab58 100644 --- a/src/platform_impl/linux/wayland/event_loop/mod.rs +++ b/src/platform_impl/linux/wayland/event_loop/mod.rs @@ -369,9 +369,7 @@ impl EventLoop { sticky_exit_callback( Event::WindowEvent { - window_id: crate::window::WindowId( - crate::platform_impl::WindowId::Wayland(*window_id), - ), + window_id: crate::window::WindowId(*window_id), event: WindowEvent::ScaleFactorChanged { scale_factor, new_inner_size: &mut physical_size, @@ -421,9 +419,7 @@ impl EventLoop { if let Some(physical_size) = physical_size { sticky_exit_callback( Event::WindowEvent { - window_id: crate::window::WindowId( - crate::platform_impl::WindowId::Wayland(*window_id), - ), + window_id: crate::window::WindowId(*window_id), event: WindowEvent::Resized(physical_size), }, &self.window_target, @@ -436,9 +432,7 @@ impl EventLoop { if window_update.close_window { sticky_exit_callback( Event::WindowEvent { - window_id: crate::window::WindowId( - crate::platform_impl::WindowId::Wayland(*window_id), - ), + window_id: crate::window::WindowId(*window_id), event: WindowEvent::CloseRequested, }, &self.window_target, @@ -488,9 +482,7 @@ impl EventLoop { // Handle redraw request. if window_update.redraw_requested { sticky_exit_callback( - Event::RedrawRequested(crate::window::WindowId( - crate::platform_impl::WindowId::Wayland(*window_id), - )), + Event::RedrawRequested(crate::window::WindowId(*window_id)), &self.window_target, &mut control_flow, &mut callback, diff --git a/src/platform_impl/linux/wayland/event_loop/sink.rs b/src/platform_impl/linux/wayland/event_loop/sink.rs index 303ab826e4..26a895e569 100644 --- a/src/platform_impl/linux/wayland/event_loop/sink.rs +++ b/src/platform_impl/linux/wayland/event_loop/sink.rs @@ -1,7 +1,7 @@ //! An event loop's sink to deliver events from the Wayland event callbacks. use crate::event::{DeviceEvent, DeviceId as RootDeviceId, Event, WindowEvent}; -use crate::platform_impl::platform::{DeviceId as PlatformDeviceId, WindowId as PlatformWindowId}; +use crate::platform_impl::platform::DeviceId as PlatformDeviceId; use crate::window::WindowId as RootWindowId; use super::{DeviceId, WindowId}; @@ -30,7 +30,7 @@ impl EventSink { pub fn push_window_event(&mut self, event: WindowEvent<'static>, window_id: WindowId) { self.window_events.push(Event::WindowEvent { event, - window_id: RootWindowId(PlatformWindowId::Wayland(window_id)), + window_id: RootWindowId(window_id), }); } } diff --git a/src/platform_impl/linux/wayland/mod.rs b/src/platform_impl/linux/wayland/mod.rs index 4ed564aec0..9871b2f23e 100644 --- a/src/platform_impl/linux/wayland/mod.rs +++ b/src/platform_impl/linux/wayland/mod.rs @@ -8,6 +8,7 @@ use sctk::reexports::client::protocol::wl_surface::WlSurface; +pub use crate::platform_impl::platform::WindowId; pub use event_loop::{EventLoop, EventLoopProxy, EventLoopWindowTarget}; pub use output::{MonitorHandle, VideoMode}; pub use window::Window; @@ -27,16 +28,7 @@ impl DeviceId { } } -#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct WindowId(usize); - -impl WindowId { - pub const unsafe fn dummy() -> Self { - WindowId(0) - } -} - #[inline] fn make_wid(surface: &WlSurface) -> WindowId { - WindowId(surface.as_ref().c_ptr() as usize) + WindowId(surface.as_ref().c_ptr() as u64) } diff --git a/src/platform_impl/linux/x11/event_processor.rs b/src/platform_impl/linux/x11/event_processor.rs index dea0ec85b9..bbd07ff110 100644 --- a/src/platform_impl/linux/x11/event_processor.rs +++ b/src/platform_impl/linux/x11/event_processor.rs @@ -59,7 +59,7 @@ impl EventProcessor { F: Fn(&Arc) -> Ret, { let mut deleted = false; - let window_id = WindowId(window_id); + let window_id = WindowId(window_id as u64); let wt = get_xtarget(&self.target); let result = wt .windows @@ -513,7 +513,7 @@ impl EventProcessor { // In the event that the window's been destroyed without being dropped first, we // cleanup again here. - wt.windows.borrow_mut().remove(&WindowId(window)); + wt.windows.borrow_mut().remove(&WindowId(window as u64)); // Since all XIM stuff needs to happen from the same thread, we destroy the input // context here instead of when dropping the window. @@ -1213,11 +1213,7 @@ impl EventProcessor { &*window.shared_state.lock(), ); - let window_id = crate::window::WindowId( - crate::platform_impl::platform::WindowId::X( - *window_id, - ), - ); + let window_id = crate::window::WindowId(*window_id); let old_inner_size = PhysicalSize::new(width, height); let mut new_inner_size = PhysicalSize::new(new_width, new_height); diff --git a/src/platform_impl/linux/x11/mod.rs b/src/platform_impl/linux/x11/mod.rs index 9789f03625..6ee2fb827d 100644 --- a/src/platform_impl/linux/x11/mod.rs +++ b/src/platform_impl/linux/x11/mod.rs @@ -53,7 +53,10 @@ use crate::{ event_loop::{ ControlFlow, DeviceEventFilter, EventLoopClosed, EventLoopWindowTarget as RootELW, }, - platform_impl::{platform::sticky_exit_callback, PlatformSpecificWindowBuilderAttributes}, + platform_impl::{ + platform::{sticky_exit_callback, WindowId}, + PlatformSpecificWindowBuilderAttributes, + }, window::WindowAttributes, }; @@ -361,7 +364,7 @@ impl EventLoop { } for window_id in windows { - let window_id = crate::window::WindowId(super::WindowId::X(window_id)); + let window_id = crate::window::WindowId(window_id); sticky_exit_callback( Event::RedrawRequested(window_id), &this.target, @@ -505,10 +508,7 @@ impl EventLoop { target, control_flow, &mut |event, window_target, control_flow| { - if let Event::RedrawRequested(crate::window::WindowId( - super::WindowId::X(wid), - )) = event - { + if let Event::RedrawRequested(crate::window::WindowId(wid)) = event { wt.redraw_sender.sender.send(wid).unwrap(); wt.redraw_sender.waker.wake().unwrap(); } else { @@ -609,15 +609,6 @@ impl<'a> Deref for DeviceInfo<'a> { } } -#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct WindowId(ffi::Window); - -impl WindowId { - pub const unsafe fn dummy() -> Self { - WindowId(0) - } -} - #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct DeviceId(c_int); @@ -657,7 +648,7 @@ impl Drop for Window { let window = self.deref(); let xconn = &window.xconn; unsafe { - (xconn.xlib.XDestroyWindow)(xconn.display, window.id().0); + (xconn.xlib.XDestroyWindow)(xconn.display, window.id().0 as ffi::Window); // If the window was somehow already destroyed, we'll get a `BadWindow` error, which we don't care about. let _ = xconn.check_errors(); } @@ -700,7 +691,7 @@ struct XExtension { } fn mkwid(w: ffi::Window) -> crate::window::WindowId { - crate::window::WindowId(crate::platform_impl::WindowId::X(WindowId(w))) + crate::window::WindowId(crate::platform_impl::platform::WindowId(w as u64)) } fn mkdid(w: c_int) -> crate::event::DeviceId { crate::event::DeviceId(crate::platform_impl::DeviceId::X(DeviceId(w))) diff --git a/src/platform_impl/linux/x11/window.rs b/src/platform_impl/linux/x11/window.rs index cb65755331..fa4bd1440b 100644 --- a/src/platform_impl/linux/x11/window.rs +++ b/src/platform_impl/linux/x11/window.rs @@ -1496,14 +1496,14 @@ impl UnownedWindow { #[inline] pub fn id(&self) -> WindowId { - WindowId(self.xwindow) + WindowId(self.xwindow as u64) } #[inline] pub fn request_redraw(&self) { self.redraw_sender .sender - .send(WindowId(self.xwindow)) + .send(WindowId(self.xwindow as u64)) .unwrap(); self.redraw_sender.waker.wake().unwrap(); } diff --git a/src/platform_impl/macos/window.rs b/src/platform_impl/macos/window.rs index 158feebebf..11d43f5a87 100644 --- a/src/platform_impl/macos/window.rs +++ b/src/platform_impl/macos/window.rs @@ -58,6 +58,18 @@ impl WindowId { } } +impl From for u64 { + fn from(window_id: WindowId) -> Self { + window_id.0 as u64 + } +} + +impl From for WindowId { + fn from(raw_id: u64) -> Self { + Self(raw_id as usize) + } +} + // Convert the `cocoa::base::id` associated with a window to a usize to use as a unique identifier // for the window. pub fn get_window_id(window_cocoa_id: id) -> WindowId { diff --git a/src/platform_impl/web/window.rs b/src/platform_impl/web/window.rs index 51dc98ae5b..78299c8ce0 100644 --- a/src/platform_impl/web/window.rs +++ b/src/platform_impl/web/window.rs @@ -380,6 +380,18 @@ impl WindowId { } } +impl From for u64 { + fn from(window_id: WindowId) -> Self { + window_id.0 as u64 + } +} + +impl From for WindowId { + fn from(raw_id: u64) -> Self { + Self(raw_id as u32) + } +} + #[derive(Default, Clone)] pub struct PlatformSpecificWindowBuilderAttributes { pub(crate) canvas: Option, diff --git a/src/platform_impl/windows/mod.rs b/src/platform_impl/windows/mod.rs index 85830bc586..84bf917dc0 100644 --- a/src/platform_impl/windows/mod.rs +++ b/src/platform_impl/windows/mod.rs @@ -100,6 +100,18 @@ impl WindowId { } } +impl From for u64 { + fn from(window_id: WindowId) -> Self { + window_id.0 as u64 + } +} + +impl From for WindowId { + fn from(raw_id: u64) -> Self { + Self(raw_id as HWND) + } +} + #[inline(always)] const fn get_xbutton_wparam(x: u32) -> u16 { loword(x) diff --git a/src/window.rs b/src/window.rs index ea997d6b09..dbe54e0d49 100644 --- a/src/window.rs +++ b/src/window.rs @@ -83,6 +83,18 @@ impl WindowId { } } +impl From for u64 { + fn from(window_id: WindowId) -> Self { + window_id.into() + } +} + +impl From for WindowId { + fn from(raw_id: u64) -> Self { + raw_id.into() + } +} + /// Object that allows building windows. #[derive(Clone, Default)] #[must_use] From d78a870e662e17118142fa0778d42456a3333a19 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Sun, 3 Jul 2022 20:25:08 +0100 Subject: [PATCH 28/60] examples/multiwindow.rs: ignore synthetic key press events --- examples/multiwindow.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/examples/multiwindow.rs b/examples/multiwindow.rs index 10d3fad3f5..de87962d04 100644 --- a/examples/multiwindow.rs +++ b/examples/multiwindow.rs @@ -4,7 +4,7 @@ use std::collections::HashMap; use simple_logger::SimpleLogger; use winit::{ - event::{ElementState, Event, KeyboardInput, WindowEvent}, + event::{ElementState, Event, KeyboardInput, VirtualKeyCode, WindowEvent}, event_loop::EventLoop, window::Window, }; @@ -16,9 +16,12 @@ fn main() { let mut windows = HashMap::new(); for _ in 0..3 { let window = Window::new(&event_loop).unwrap(); + println!("Opened a new window: {:?}", window.id()); windows.insert(window.id(), window); } + println!("Press N to open a new window."); + event_loop.run(move |event, event_loop, control_flow| { control_flow.set_wait(); @@ -39,11 +42,14 @@ fn main() { input: KeyboardInput { state: ElementState::Pressed, + virtual_keycode: Some(VirtualKeyCode::N), .. }, + is_synthetic: false, .. } => { let window = Window::new(event_loop).unwrap(); + println!("Opened a new window: {:?}", window.id()); windows.insert(window.id(), window); } _ => (), From 4b10993970a3a6a17ef6688527eb9a924d70e1a1 Mon Sep 17 00:00:00 2001 From: Shinichi Tanaka Date: Wed, 6 Jul 2022 02:09:40 +0900 Subject: [PATCH 29/60] Fix infinite recursion in `WindowId` conversion methods --- src/window.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/window.rs b/src/window.rs index dbe54e0d49..2d732e8a49 100644 --- a/src/window.rs +++ b/src/window.rs @@ -85,13 +85,13 @@ impl WindowId { impl From for u64 { fn from(window_id: WindowId) -> Self { - window_id.into() + window_id.0.into() } } impl From for WindowId { fn from(raw_id: u64) -> Self { - raw_id.into() + Self(raw_id.into()) } } From e289f30e5ddf599e348606fb9a384ec4e1a299a3 Mon Sep 17 00:00:00 2001 From: trimental Date: Wed, 6 Jul 2022 18:46:25 +0000 Subject: [PATCH 30/60] Add 'WindowEvent::Occluded(bool)' This commits and an event to track window occlusion state, which could help optimize rendering downstream. --- CHANGELOG.md | 1 + src/event.rs | 11 ++++++++++ .../linux/x11/event_processor.rs | 9 ++++++-- src/platform_impl/macos/window_delegate.rs | 21 ++++++++++++++++++- 4 files changed, 39 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 722ea7aa5f..d49e5d0be5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ And please only add new entries to the top of this list, right below the `# Unre # Unreleased +- Added `WindowEvent::Occluded(bool)`, currently implemented on macOS and X11. - On X11, fix events for caps lock key not being sent - Build docs on `docs.rs` for iOS and Android as well. - **Breaking:** Removed the `WindowAttributes` struct, since all its functionality is accessible from `WindowBuilder`. diff --git a/src/event.rs b/src/event.rs index 29e860e699..e6710d4454 100644 --- a/src/event.rs +++ b/src/event.rs @@ -372,6 +372,15 @@ pub enum WindowEvent<'a> { /// /// At the moment this is only supported on Windows. ThemeChanged(Theme), + + /// The window has been occluded (completely hidden from view). + /// + /// This is different to window visibility as it depends on whether the window is closed, + /// minimised, set invisible, or fully occluded by another window. + /// + /// Platform-specific behavior: + /// - **iOS / Android / Web / Wayland / Windows :** Unsupported. + Occluded(bool), } impl Clone for WindowEvent<'static> { @@ -461,6 +470,7 @@ impl Clone for WindowEvent<'static> { ScaleFactorChanged { .. } => { unreachable!("Static event can't be about scale factor changing") } + Occluded(occluded) => Occluded(*occluded), }; } } @@ -546,6 +556,7 @@ impl<'a> WindowEvent<'a> { Touch(touch) => Some(Touch(touch)), ThemeChanged(theme) => Some(ThemeChanged(theme)), ScaleFactorChanged { .. } => None, + Occluded(occluded) => Some(Occluded(occluded)), } } } diff --git a/src/platform_impl/linux/x11/event_processor.rs b/src/platform_impl/linux/x11/event_processor.rs index bbd07ff110..54cdc59b03 100644 --- a/src/platform_impl/linux/x11/event_processor.rs +++ b/src/platform_impl/linux/x11/event_processor.rs @@ -531,8 +531,13 @@ impl EventProcessor { ffi::VisibilityNotify => { let xev: &ffi::XVisibilityEvent = xev.as_ref(); let xwindow = xev.window; - - self.with_window(xwindow, |window| window.visibility_notify()); + callback(Event::WindowEvent { + window_id: mkwid(xwindow), + event: WindowEvent::Occluded(xev.state == ffi::VisibilityFullyObscured), + }); + self.with_window(xwindow, |window| { + window.visibility_notify(); + }); } ffi::Expose => { diff --git a/src/platform_impl/macos/window_delegate.rs b/src/platform_impl/macos/window_delegate.rs index 3a8d713f5b..bb855cd5ef 100644 --- a/src/platform_impl/macos/window_delegate.rs +++ b/src/platform_impl/macos/window_delegate.rs @@ -5,7 +5,7 @@ use std::{ }; use cocoa::{ - appkit::{self, NSApplicationPresentationOptions, NSView, NSWindow}, + appkit::{self, NSApplicationPresentationOptions, NSView, NSWindow, NSWindowOcclusionState}, base::{id, nil}, foundation::NSUInteger, }; @@ -220,6 +220,10 @@ static WINDOW_DELEGATE_CLASS: Lazy = Lazy::new(|| unsafe { sel!(windowDidFailToEnterFullScreen:), window_did_fail_to_enter_fullscreen as extern "C" fn(&Object, Sel, id), ); + decl.add_method( + sel!(windowDidChangeOcclusionState:), + window_did_change_occlusion_state as extern "C" fn(&Object, Sel, id), + ); decl.add_ivar::<*mut c_void>("winitState"); WindowDelegateClass(decl.register()) @@ -554,3 +558,18 @@ extern "C" fn window_did_fail_to_enter_fullscreen(this: &Object, _: Sel, _: id) } }); } + +// Invoked when the occlusion state of the window changes +extern "C" fn window_did_change_occlusion_state(this: &Object, _: Sel, _: id) { + trace_scope!("windowDidChangeOcclusionState:"); + unsafe { + with_state(this, |state| { + state.emit_event(WindowEvent::Occluded( + !state + .ns_window + .occlusionState() + .contains(NSWindowOcclusionState::NSWindowOcclusionStateVisible), + )) + }); + } +} From a06bb3f992101e2668562aa6720821cc004a985d Mon Sep 17 00:00:00 2001 From: Kirill Chibisov Date: Fri, 8 Jul 2022 13:25:56 +0300 Subject: [PATCH 31/60] Add `refresh_rate_millihertz` for `MonitorHandle` This also alters `VideoMode::refresh_rate` to `VideoMode::refresh_rate_millihertz` which now returns monitor refresh rate in mHz. --- CHANGELOG.md | 2 + src/monitor.rs | 25 ++++++---- src/platform_impl/android/mod.rs | 13 ++++-- src/platform_impl/ios/monitor.rs | 56 ++++++++++++++--------- src/platform_impl/linux/mod.rs | 9 +++- src/platform_impl/linux/wayland/output.rs | 18 ++++++-- src/platform_impl/linux/x11/monitor.rs | 37 +++++++++++++-- src/platform_impl/linux/x11/util/randr.rs | 16 +++---- src/platform_impl/macos/monitor.rs | 34 ++++++++------ src/platform_impl/web/monitor.rs | 8 +++- src/platform_impl/windows/monitor.rs | 35 ++++++++++---- 11 files changed, 172 insertions(+), 81 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d49e5d0be5..40b9cd1f20 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -65,6 +65,8 @@ And please only add new entries to the top of this list, right below the `# Unre - Fix on macOS `WindowBuilder::with_disallow_hidpi`, setting true or false by the user no matter the SO default value. - `EventLoopBuilder::build` will now panic when the `EventLoop` is being created more than once. - Added `From` for `WindowId` and `From` for `u64`. +- Added `MonitorHandle::refresh_rate_millihertz` to get monitor's refresh rate. +- **Breaking**, Replaced `VideoMode::refresh_rate` with `VideoMode::refresh_rate_millihertz` providing better precision. # 0.26.1 (2022-01-05) diff --git a/src/monitor.rs b/src/monitor.rs index 7611f94b56..0ee4817737 100644 --- a/src/monitor.rs +++ b/src/monitor.rs @@ -39,8 +39,8 @@ impl Ord for VideoMode { self.monitor().cmp(&other.monitor()).then( size.cmp(&other_size) .then( - self.refresh_rate() - .cmp(&other.refresh_rate()) + self.refresh_rate_millihertz() + .cmp(&other.refresh_rate_millihertz()) .then(self.bit_depth().cmp(&other.bit_depth())), ) .reverse(), @@ -68,12 +68,10 @@ impl VideoMode { self.video_mode.bit_depth() } - /// Returns the refresh rate of this video mode. **Note**: the returned - /// refresh rate is an integer approximation, and you shouldn't rely on this - /// value to be exact. + /// Returns the refresh rate of this video mode in mHz. #[inline] - pub fn refresh_rate(&self) -> u16 { - self.video_mode.refresh_rate() + pub fn refresh_rate_millihertz(&self) -> u32 { + self.video_mode.refresh_rate_millihertz() } /// Returns the monitor that this video mode is valid for. Each monitor has @@ -88,10 +86,10 @@ impl std::fmt::Display for VideoMode { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!( f, - "{}x{} @ {} Hz ({} bpp)", + "{}x{} @ {} mHz ({} bpp)", self.size().width, self.size().height, - self.refresh_rate(), + self.refresh_rate_millihertz(), self.bit_depth() ) } @@ -141,6 +139,15 @@ impl MonitorHandle { self.inner.position() } + /// The monitor refresh rate used by the system. + /// + /// When using exclusive fullscreen, the refresh rate of the [`VideoMode`] that was used to + /// enter fullscreen should be used instead. + #[inline] + pub fn refresh_rate_millihertz(&self) -> Option { + self.inner.refresh_rate_millihertz() + } + /// Returns the scale factor that can be used to map logical pixels to physical pixels, and vice versa. /// /// See the [`dpi`](crate::dpi) module for more information. diff --git a/src/platform_impl/android/mod.rs b/src/platform_impl/android/mod.rs index e37bd1f5fe..1d39c86233 100644 --- a/src/platform_impl/android/mod.rs +++ b/src/platform_impl/android/mod.rs @@ -857,6 +857,11 @@ impl MonitorHandle { .unwrap_or(1.0) } + pub fn refresh_rate_millihertz(&self) -> Option { + // FIXME no way to get real refrsh rate for now. + None + } + pub fn video_modes(&self) -> impl Iterator { let size = self.size().into(); // FIXME this is not the real refresh rate @@ -865,7 +870,7 @@ impl MonitorHandle { video_mode: VideoMode { size, bit_depth: 32, - refresh_rate: 60, + refresh_rate_millihertz: 60000, monitor: self.clone(), }, }) @@ -876,7 +881,7 @@ impl MonitorHandle { pub struct VideoMode { size: (u32, u32), bit_depth: u16, - refresh_rate: u16, + refresh_rate_millihertz: u32, monitor: MonitorHandle, } @@ -889,8 +894,8 @@ impl VideoMode { self.bit_depth } - pub fn refresh_rate(&self) -> u16 { - self.refresh_rate + pub fn refresh_rate_millihertz(&self) -> u32 { + self.refresh_rate_millihertz } pub fn monitor(&self) -> monitor::MonitorHandle { diff --git a/src/platform_impl/ios/monitor.rs b/src/platform_impl/ios/monitor.rs index 26f4af06b3..42516cb0c4 100644 --- a/src/platform_impl/ios/monitor.rs +++ b/src/platform_impl/ios/monitor.rs @@ -17,7 +17,7 @@ use crate::{ pub struct VideoMode { pub(crate) size: (u32, u32), pub(crate) bit_depth: u16, - pub(crate) refresh_rate: u16, + pub(crate) refresh_rate_millihertz: u32, pub(crate) screen_mode: NativeDisplayMode, pub(crate) monitor: MonitorHandle, } @@ -49,7 +49,7 @@ impl Clone for VideoMode { VideoMode { size: self.size, bit_depth: self.bit_depth, - refresh_rate: self.refresh_rate, + refresh_rate_millihertz: self.refresh_rate_millihertz, screen_mode: self.screen_mode.clone(), monitor: self.monitor.clone(), } @@ -59,30 +59,14 @@ impl Clone for VideoMode { impl VideoMode { unsafe fn retained_new(uiscreen: id, screen_mode: id) -> VideoMode { assert_main_thread!("`VideoMode` can only be created on the main thread on iOS"); - let os_capabilities = app_state::os_capabilities(); - let refresh_rate: NSInteger = if os_capabilities.maximum_frames_per_second { - msg_send![uiscreen, maximumFramesPerSecond] - } else { - // https://developer.apple.com/library/archive/technotes/tn2460/_index.html - // https://en.wikipedia.org/wiki/IPad_Pro#Model_comparison - // - // All iOS devices support 60 fps, and on devices where `maximumFramesPerSecond` is not - // supported, they are all guaranteed to have 60hz refresh rates. This does not - // correctly handle external displays. ProMotion displays support 120fps, but they were - // introduced at the same time as the `maximumFramesPerSecond` API. - // - // FIXME: earlier OSs could calculate the refresh rate using - // `-[CADisplayLink duration]`. - os_capabilities.maximum_frames_per_second_err_msg("defaulting to 60 fps"); - 60 - }; + let refresh_rate_millihertz = refresh_rate_millihertz(uiscreen); let size: CGSize = msg_send![screen_mode, size]; let screen_mode: id = msg_send![screen_mode, retain]; let screen_mode = NativeDisplayMode(screen_mode); VideoMode { size: (size.width as u32, size.height as u32), bit_depth: 32, - refresh_rate: refresh_rate as u16, + refresh_rate_millihertz, screen_mode, monitor: MonitorHandle::retained_new(uiscreen), } @@ -96,8 +80,8 @@ impl VideoMode { self.bit_depth } - pub fn refresh_rate(&self) -> u16 { - self.refresh_rate + pub fn refresh_rate_millihertz(&self) -> u32 { + self.refresh_rate_millihertz } pub fn monitor(&self) -> RootMonitorHandle { @@ -239,6 +223,10 @@ impl Inner { } } + pub fn refresh_rate_millihertz(&self) -> Option { + Some(refresh_rate_millihertz(self.uiscreen)) + } + pub fn video_modes(&self) -> impl Iterator { let mut modes = BTreeSet::new(); unsafe { @@ -257,6 +245,30 @@ impl Inner { } } +fn refresh_rate_millihertz(uiscreen: id) -> u32 { + let refresh_rate_millihertz: NSInteger = unsafe { + let os_capabilities = app_state::os_capabilities(); + if os_capabilities.maximum_frames_per_second { + msg_send![uiscreen, maximumFramesPerSecond] + } else { + // https://developer.apple.com/library/archive/technotes/tn2460/_index.html + // https://en.wikipedia.org/wiki/IPad_Pro#Model_comparison + // + // All iOS devices support 60 fps, and on devices where `maximumFramesPerSecond` is not + // supported, they are all guaranteed to have 60hz refresh rates. This does not + // correctly handle external displays. ProMotion displays support 120fps, but they were + // introduced at the same time as the `maximumFramesPerSecond` API. + // + // FIXME: earlier OSs could calculate the refresh rate using + // `-[CADisplayLink duration]`. + os_capabilities.maximum_frames_per_second_err_msg("defaulting to 60 fps"); + 60 + } + }; + + refresh_rate_millihertz as u32 * 1000 +} + // MonitorHandleExtIOS impl Inner { pub fn ui_screen(&self) -> id { diff --git a/src/platform_impl/linux/mod.rs b/src/platform_impl/linux/mod.rs index 37b1d4533a..6876c48b8a 100644 --- a/src/platform_impl/linux/mod.rs +++ b/src/platform_impl/linux/mod.rs @@ -256,6 +256,11 @@ impl MonitorHandle { x11_or_wayland!(match self; MonitorHandle(m) => m.position()) } + #[inline] + pub fn refresh_rate_millihertz(&self) -> Option { + x11_or_wayland!(match self; MonitorHandle(m) => m.refresh_rate_millihertz()) + } + #[inline] pub fn scale_factor(&self) -> f64 { x11_or_wayland!(match self; MonitorHandle(m) => m.scale_factor() as f64) @@ -287,8 +292,8 @@ impl VideoMode { } #[inline] - pub fn refresh_rate(&self) -> u16 { - x11_or_wayland!(match self; VideoMode(m) => m.refresh_rate()) + pub fn refresh_rate_millihertz(&self) -> u32 { + x11_or_wayland!(match self; VideoMode(m) => m.refresh_rate_millihertz()) } #[inline] diff --git a/src/platform_impl/linux/wayland/output.rs b/src/platform_impl/linux/wayland/output.rs index a01b0d83dc..e3a434781e 100644 --- a/src/platform_impl/linux/wayland/output.rs +++ b/src/platform_impl/linux/wayland/output.rs @@ -167,6 +167,16 @@ impl MonitorHandle { .into() } + #[inline] + pub fn refresh_rate_millihertz(&self) -> Option { + sctk::output::with_output_info(&self.proxy, |info| { + info.modes + .iter() + .find_map(|mode| mode.is_current.then(|| mode.refresh_rate as u32)) + }) + .flatten() + } + #[inline] pub fn scale_factor(&self) -> i32 { sctk::output::with_output_info(&self.proxy, |info| info.scale_factor).unwrap_or(1) @@ -182,7 +192,7 @@ impl MonitorHandle { modes.into_iter().map(move |mode| RootVideoMode { video_mode: PlatformVideoMode::Wayland(VideoMode { size: (mode.dimensions.0 as u32, mode.dimensions.1 as u32).into(), - refresh_rate: (mode.refresh_rate as f32 / 1000.0).round() as u16, + refresh_rate_millihertz: mode.refresh_rate as u32, bit_depth: 32, monitor: monitor.clone(), }), @@ -194,7 +204,7 @@ impl MonitorHandle { pub struct VideoMode { pub(crate) size: PhysicalSize, pub(crate) bit_depth: u16, - pub(crate) refresh_rate: u16, + pub(crate) refresh_rate_millihertz: u32, pub(crate) monitor: MonitorHandle, } @@ -210,8 +220,8 @@ impl VideoMode { } #[inline] - pub fn refresh_rate(&self) -> u16 { - self.refresh_rate + pub fn refresh_rate_millihertz(&self) -> u32 { + self.refresh_rate_millihertz } pub fn monitor(&self) -> RootMonitorHandle { diff --git a/src/platform_impl/linux/x11/monitor.rs b/src/platform_impl/linux/x11/monitor.rs index 9d3d3c8d73..8a696e8a05 100644 --- a/src/platform_impl/linux/x11/monitor.rs +++ b/src/platform_impl/linux/x11/monitor.rs @@ -1,4 +1,5 @@ use std::os::raw::*; +use std::slice; use once_cell::sync::Lazy; use parking_lot::Mutex; @@ -6,7 +7,7 @@ use parking_lot::Mutex; use super::{ ffi::{ RRCrtc, RRCrtcChangeNotifyMask, RRMode, RROutputPropertyNotifyMask, - RRScreenChangeNotifyMask, True, Window, XRRCrtcInfo, XRRScreenResources, + RRScreenChangeNotifyMask, True, Window, XRRCrtcInfo, XRRModeInfo, XRRScreenResources, }, util, XConnection, XError, }; @@ -30,7 +31,7 @@ pub fn invalidate_cached_monitor_list() -> Option> { pub struct VideoMode { pub(crate) size: (u32, u32), pub(crate) bit_depth: u16, - pub(crate) refresh_rate: u16, + pub(crate) refresh_rate_millihertz: u32, pub(crate) native_mode: RRMode, pub(crate) monitor: Option, } @@ -47,8 +48,8 @@ impl VideoMode { } #[inline] - pub fn refresh_rate(&self) -> u16 { - self.refresh_rate + pub fn refresh_rate_millihertz(&self) -> u32 { + self.refresh_rate_millihertz } #[inline] @@ -71,6 +72,8 @@ pub struct MonitorHandle { position: (i32, i32), /// If the monitor is the primary one primary: bool, + /// The refresh rate used by monitor. + refresh_rate_millihertz: Option, /// The DPI scale factor pub(crate) scale_factor: f64, /// Used to determine which windows are on this monitor @@ -105,6 +108,15 @@ impl std::hash::Hash for MonitorHandle { } } +#[inline] +pub fn mode_refresh_rate_millihertz(mode: &XRRModeInfo) -> Option { + if mode.dotClock > 0 && mode.hTotal > 0 && mode.vTotal > 0 { + Some((mode.dotClock as u64 * 1000 / (mode.hTotal as u64 * mode.vTotal as u64)) as u32) + } else { + None + } +} + impl MonitorHandle { fn new( xconn: &XConnection, @@ -116,10 +128,22 @@ impl MonitorHandle { let (name, scale_factor, video_modes) = unsafe { xconn.get_output_info(resources, crtc)? }; let dimensions = unsafe { ((*crtc).width as u32, (*crtc).height as u32) }; let position = unsafe { ((*crtc).x as i32, (*crtc).y as i32) }; + + // Get the refresh rate of the current video mode. + let current_mode = unsafe { (*crtc).mode }; + let screen_modes = + unsafe { slice::from_raw_parts((*resources).modes, (*resources).nmode as usize) }; + let refresh_rate_millihertz = screen_modes + .iter() + .find(|mode| mode.id == current_mode) + .and_then(mode_refresh_rate_millihertz); + let rect = util::AaRect::new(position, dimensions); + Some(MonitorHandle { id, name, + refresh_rate_millihertz, scale_factor, dimensions, position, @@ -136,6 +160,7 @@ impl MonitorHandle { scale_factor: 1.0, dimensions: (1, 1), position: (0, 0), + refresh_rate_millihertz: None, primary: true, rect: util::AaRect::new((0, 0), (1, 1)), video_modes: Vec::new(), @@ -164,6 +189,10 @@ impl MonitorHandle { self.position.into() } + pub fn refresh_rate_millihertz(&self) -> Option { + self.refresh_rate_millihertz + } + #[inline] pub fn scale_factor(&self) -> f64 { self.scale_factor diff --git a/src/platform_impl/linux/x11/util/randr.rs b/src/platform_impl/linux/x11/util/randr.rs index 3500152439..2ce46e70e5 100644 --- a/src/platform_impl/linux/x11/util/randr.rs +++ b/src/platform_impl/linux/x11/util/randr.rs @@ -4,6 +4,7 @@ use super::{ ffi::{CurrentTime, RRCrtc, RRMode, Success, XRRCrtcInfo, XRRScreenResources}, *, }; +use crate::platform_impl::platform::x11::monitor; use crate::{dpi::validate_scale_factor, platform_impl::platform::x11::VideoMode}; /// Represents values of `WINIT_HIDPI_FACTOR`. @@ -80,18 +81,13 @@ impl XConnection { // XRROutputInfo contains an array of mode ids that correspond to // modes in the array in XRRScreenResources .filter(|x| output_modes.iter().any(|id| x.id == *id)) - .map(|x| { - let refresh_rate = if x.dotClock > 0 && x.hTotal > 0 && x.vTotal > 0 { - x.dotClock as u64 * 1000 / (x.hTotal as u64 * x.vTotal as u64) - } else { - 0 - }; - + .map(|mode| { VideoMode { - size: (x.width, x.height), - refresh_rate: (refresh_rate as f32 / 1000.0).round() as u16, + size: (mode.width, mode.height), + refresh_rate_millihertz: monitor::mode_refresh_rate_millihertz(mode) + .unwrap_or(0), bit_depth: bit_depth as u16, - native_mode: x.id, + native_mode: mode.id, // This is populated in `MonitorHandle::video_modes` as the // video mode is returned to the user monitor: None, diff --git a/src/platform_impl/macos/monitor.rs b/src/platform_impl/macos/monitor.rs index d421951e1d..2b93be6a5a 100644 --- a/src/platform_impl/macos/monitor.rs +++ b/src/platform_impl/macos/monitor.rs @@ -21,7 +21,7 @@ use core_graphics::display::{CGDirectDisplayID, CGDisplay, CGDisplayBounds}; pub struct VideoMode { pub(crate) size: (u32, u32), pub(crate) bit_depth: u16, - pub(crate) refresh_rate: u16, + pub(crate) refresh_rate_millihertz: u32, pub(crate) monitor: MonitorHandle, pub(crate) native_mode: NativeDisplayMode, } @@ -30,7 +30,7 @@ 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.refresh_rate_millihertz == other.refresh_rate_millihertz && self.monitor == other.monitor } } @@ -41,7 +41,7 @@ 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.refresh_rate_millihertz.hash(state); self.monitor.hash(state); } } @@ -51,7 +51,7 @@ impl std::fmt::Debug for VideoMode { f.debug_struct("VideoMode") .field("size", &self.size) .field("bit_depth", &self.bit_depth) - .field("refresh_rate", &self.refresh_rate) + .field("refresh_rate_millihertz", &self.refresh_rate_millihertz) .field("monitor", &self.monitor) .finish() } @@ -87,8 +87,8 @@ impl VideoMode { self.bit_depth } - pub fn refresh_rate(&self) -> u16 { - self.refresh_rate + pub fn refresh_rate_millihertz(&self) -> u32 { + self.refresh_rate_millihertz } pub fn monitor(&self) -> RootMonitorHandle { @@ -220,8 +220,8 @@ impl MonitorHandle { unsafe { NSScreen::backingScaleFactor(screen) as f64 } } - pub fn video_modes(&self) -> impl Iterator { - let cv_refresh_rate = unsafe { + pub fn refresh_rate_millihertz(&self) -> Option { + unsafe { let mut display_link = std::ptr::null_mut(); assert_eq!( ffi::CVDisplayLinkCreateWithCGDisplay(self.0, &mut display_link), @@ -233,9 +233,12 @@ impl MonitorHandle { // This value is indefinite if an invalid display link was specified assert!(time.flags & ffi::kCVTimeIsIndefinite == 0); - time.time_scale as i64 / time.time_value - }; + Some((time.time_scale as i64 / time.time_value * 1000) as u32) + } + } + pub fn video_modes(&self) -> impl Iterator { + let refresh_rate_millihertz = self.refresh_rate_millihertz().unwrap_or(0); let monitor = self.clone(); unsafe { @@ -255,14 +258,15 @@ impl MonitorHandle { }; modes.into_iter().map(move |mode| { - let cg_refresh_rate = ffi::CGDisplayModeGetRefreshRate(mode).round() as i64; + let cg_refresh_rate_millihertz = + ffi::CGDisplayModeGetRefreshRate(mode).round() as i64; // CGDisplayModeGetRefreshRate returns 0.0 for any display that // isn't a CRT - let refresh_rate = if cg_refresh_rate > 0 { - cg_refresh_rate + let refresh_rate_millihertz = if cg_refresh_rate_millihertz > 0 { + (cg_refresh_rate_millihertz * 1000) as u32 } else { - cv_refresh_rate + refresh_rate_millihertz }; let pixel_encoding = @@ -283,7 +287,7 @@ impl MonitorHandle { ffi::CGDisplayModeGetPixelWidth(mode) as u32, ffi::CGDisplayModeGetPixelHeight(mode) as u32, ), - refresh_rate: refresh_rate as u16, + refresh_rate_millihertz, bit_depth, monitor: monitor.clone(), native_mode: NativeDisplayMode(mode), diff --git a/src/platform_impl/web/monitor.rs b/src/platform_impl/web/monitor.rs index 780ffccd0a..5bbcc44319 100644 --- a/src/platform_impl/web/monitor.rs +++ b/src/platform_impl/web/monitor.rs @@ -17,6 +17,10 @@ impl MonitorHandle { None } + pub fn refresh_rate_millihertz(&self) -> Option { + None + } + pub fn size(&self) -> PhysicalSize { PhysicalSize { width: 0, @@ -41,8 +45,8 @@ impl VideoMode { unimplemented!(); } - pub fn refresh_rate(&self) -> u16 { - 32 + pub fn refresh_rate_millihertz(&self) -> u32 { + 32000 } pub fn monitor(&self) -> RootMonitorHandle { diff --git a/src/platform_impl/windows/monitor.rs b/src/platform_impl/windows/monitor.rs index 595c75efe3..469059560c 100644 --- a/src/platform_impl/windows/monitor.rs +++ b/src/platform_impl/windows/monitor.rs @@ -9,8 +9,8 @@ use windows_sys::Win32::{ Graphics::Gdi::{ EnumDisplayMonitors, EnumDisplaySettingsExW, GetMonitorInfoW, MonitorFromPoint, MonitorFromWindow, DEVMODEW, DM_BITSPERPEL, DM_DISPLAYFREQUENCY, DM_PELSHEIGHT, - DM_PELSWIDTH, HDC, HMONITOR, MONITORINFO, MONITORINFOEXW, MONITOR_DEFAULTTONEAREST, - MONITOR_DEFAULTTOPRIMARY, + DM_PELSWIDTH, ENUM_CURRENT_SETTINGS, HDC, HMONITOR, MONITORINFO, MONITORINFOEXW, + MONITOR_DEFAULTTONEAREST, MONITOR_DEFAULTTOPRIMARY, }, }; @@ -29,7 +29,7 @@ use crate::{ pub struct VideoMode { pub(crate) size: (u32, u32), pub(crate) bit_depth: u16, - pub(crate) refresh_rate: u16, + pub(crate) refresh_rate_millihertz: u32, pub(crate) monitor: MonitorHandle, // DEVMODEW is huge so we box it to avoid blowing up the size of winit::window::Fullscreen pub(crate) native_video_mode: Box, @@ -39,7 +39,7 @@ 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.refresh_rate_millihertz == other.refresh_rate_millihertz && self.monitor == other.monitor } } @@ -50,7 +50,7 @@ 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.refresh_rate_millihertz.hash(state); self.monitor.hash(state); } } @@ -60,7 +60,7 @@ impl std::fmt::Debug for VideoMode { f.debug_struct("VideoMode") .field("size", &self.size) .field("bit_depth", &self.bit_depth) - .field("refresh_rate", &self.refresh_rate) + .field("refresh_rate_millihertz", &self.refresh_rate_millihertz) .field("monitor", &self.monitor) .finish() } @@ -75,8 +75,8 @@ impl VideoMode { self.bit_depth } - pub fn refresh_rate(&self) -> u16 { - self.refresh_rate + pub fn refresh_rate_millihertz(&self) -> u32 { + self.refresh_rate_millihertz } pub fn monitor(&self) -> RootMonitorHandle { @@ -192,6 +192,23 @@ impl MonitorHandle { } } + #[inline] + pub fn refresh_rate_millihertz(&self) -> Option { + let monitor_info = get_monitor_info(self.0).unwrap(); + let device_name = monitor_info.szDevice.as_ptr(); + unsafe { + let mut mode: DEVMODEW = mem::zeroed(); + mode.dmSize = mem::size_of_val(&mode) as u16; + if EnumDisplaySettingsExW(device_name, ENUM_CURRENT_SETTINGS, &mut mode, 0) + == false.into() + { + None + } else { + Some(mode.dmDisplayFrequency * 1000) + } + } + } + #[inline] pub fn position(&self) -> PhysicalPosition { let rc_monitor = get_monitor_info(self.0).unwrap().monitorInfo.rcMonitor; @@ -233,7 +250,7 @@ impl MonitorHandle { video_mode: VideoMode { size: (mode.dmPelsWidth, mode.dmPelsHeight), bit_depth: mode.dmBitsPerPel as u16, - refresh_rate: mode.dmDisplayFrequency as u16, + refresh_rate_millihertz: mode.dmDisplayFrequency as u32 * 1000, monitor: self.clone(), native_video_mode: Box::new(mode), }, From 78f1d1df38fb9c2f13abb9d8e96e7add7eead15f Mon Sep 17 00:00:00 2001 From: Kirill Chibisov Date: Sat, 9 Jul 2022 18:17:41 +0300 Subject: [PATCH 32/60] On Wayland send Focused(false) for new window On Wayland winit will always get an explicit focused event from the system and will transfer it downstream. So send focused false to enforce it. --- src/platform_impl/linux/wayland/window/mod.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/platform_impl/linux/wayland/window/mod.rs b/src/platform_impl/linux/wayland/window/mod.rs index fe7fb42521..18bbee541b 100644 --- a/src/platform_impl/linux/wayland/window/mod.rs +++ b/src/platform_impl/linux/wayland/window/mod.rs @@ -254,6 +254,12 @@ impl Window { winit_state.window_map.insert(window_id, window_handle); + // On Wayland window doesn't have Focus by default and it'll get it later on. So be + // explicit here. + winit_state + .event_sink + .push_window_event(crate::event::WindowEvent::Focused(false), window_id); + winit_state .window_updates .insert(window_id, WindowUpdate::new()); From 2d2ce70edc9005b87b7c98d1bed4e0b89ecfbb83 Mon Sep 17 00:00:00 2001 From: Kirill Chibisov Date: Sat, 9 Jul 2022 21:41:18 +0300 Subject: [PATCH 33/60] On Wayland, drop wl_surface on window close --- src/platform_impl/linux/wayland/window/shim.rs | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/platform_impl/linux/wayland/window/shim.rs b/src/platform_impl/linux/wayland/window/shim.rs index 08e2a31655..13ff2dcdb0 100644 --- a/src/platform_impl/linux/wayland/window/shim.rs +++ b/src/platform_impl/linux/wayland/window/shim.rs @@ -1,4 +1,5 @@ use std::cell::Cell; +use std::mem::ManuallyDrop; use std::sync::{Arc, Mutex}; use sctk::reexports::client::protocol::wl_compositor::WlCompositor; @@ -154,7 +155,7 @@ impl WindowUpdate { /// and react to events. pub struct WindowHandle { /// An actual window. - pub window: Window, + pub window: ManuallyDrop>, /// The current size of the window. pub size: Arc>>, @@ -206,7 +207,7 @@ impl WindowHandle { let compositor = env.get_global::().unwrap(); Self { - window, + window: ManuallyDrop::new(window), size, pending_window_requests, cursor_icon: Cell::new(CursorIcon::Default), @@ -563,3 +564,14 @@ pub fn handle_window_requests(winit_state: &mut WinitState) { let _ = window_updates.remove(&window); } } + +impl Drop for WindowHandle { + fn drop(&mut self) { + unsafe { + let surface = self.window.surface().clone(); + // The window must be destroyed before wl_surface. + ManuallyDrop::drop(&mut self.window); + surface.destroy(); + } + } +} From cdd9b1e1eb1c1c16fd3077917abceccf5784d6af Mon Sep 17 00:00:00 2001 From: Lucas Kent Date: Wed, 13 Jul 2022 08:46:15 +1000 Subject: [PATCH 34/60] web: Manually emit focused event on mouse click (#2202) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Manually emit focused event on mouse click * Update CHANGELOG.md Co-authored-by: Markus Røyset Co-authored-by: Markus Røyset --- CHANGELOG.md | 1 + src/platform_impl/web/event_loop/window_target.rs | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 40b9cd1f20..fbd1c237ef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,7 @@ And please only add new entries to the top of this list, right below the `# Unre - On X11, fix for repeated event loop iteration when `ControlFlow` was `Wait` - On X11, fix scale factor calculation when the only monitor is reconnected - On Wayland, report unaccelerated mouse deltas in `DeviceEvent::MouseMotion`. +- On Web, a focused event is manually generated when a click occurs to emulate behaviour of other backends. - **Breaking:** Bump `ndk` version to 0.6, ndk-sys to `v0.3`, `ndk-glue` to `0.6`. - Remove no longer needed `WINIT_LINK_COLORSYNC` environment variable. - **Breaking:** Rename the `Exit` variant of `ControlFlow` to `ExitWithCode`, which holds a value to control the exit code after running. Add an `Exit` constant which aliases to `ExitWithCode(0)` instead to avoid major breakage. This shouldn't affect most existing programs. diff --git a/src/platform_impl/web/event_loop/window_target.rs b/src/platform_impl/web/event_loop/window_target.rs index 0ed7c37ace..43d3034152 100644 --- a/src/platform_impl/web/event_loop/window_target.rs +++ b/src/platform_impl/web/event_loop/window_target.rs @@ -160,13 +160,17 @@ impl EventLoopWindowTarget { // user code has the correct cursor position. runner.send_events( std::iter::once(Event::WindowEvent { + window_id: RootWindowId(id), + event: WindowEvent::Focused(true), + }) + .chain(std::iter::once(Event::WindowEvent { window_id: RootWindowId(id), event: WindowEvent::CursorMoved { device_id: RootDeviceId(DeviceId(pointer_id)), position, modifiers, }, - }) + })) .chain(std::iter::once(Event::WindowEvent { window_id: RootWindowId(id), event: WindowEvent::MouseInput { From aa8f8db305fc519f3bb744a4916ce7570dd70bcb Mon Sep 17 00:00:00 2001 From: Liam Murphy Date: Thu, 14 Jul 2022 01:17:18 +1000 Subject: [PATCH 35/60] web: Add `EventLoop::spawn` (#2208) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * web: Add `EventLoop::spawn` This is the same as `EventLoop::run`, but doesn't throw an exception in order to return `!`. I decided to name it `spawn` rather than `run_web` because I think that's more descriptive, but I'm happy to change it to `run_web`. Resolves #1714 * Update src/platform/web.rs Co-authored-by: Markus Røyset * Fix outdated names Co-authored-by: Markus Røyset --- CHANGELOG.md | 1 + src/platform/web.rs | 39 +++++++++++++++++++++++++ src/platform_impl/web/event_loop/mod.rs | 25 ++++++++++------ 3 files changed, 56 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fbd1c237ef..9ba9563278 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ And please only add new entries to the top of this list, right below the `# Unre # Unreleased +- Added `EventLoopExtWebSys` with a `spawn` method to start the event loop without throwing an exception. - Added `WindowEvent::Occluded(bool)`, currently implemented on macOS and X11. - On X11, fix events for caps lock key not being sent - Build docs on `docs.rs` for iOS and Android as well. diff --git a/src/platform/web.rs b/src/platform/web.rs index 36c2a53985..56017c8ae1 100644 --- a/src/platform/web.rs +++ b/src/platform/web.rs @@ -5,6 +5,10 @@ //! to retrieve the canvas from the Window. Alternatively, use the [`WindowBuilderExtWebSys`] trait //! to provide your own canvas. +use crate::event::Event; +use crate::event_loop::ControlFlow; +use crate::event_loop::EventLoop; +use crate::event_loop::EventLoopWindowTarget; use crate::window::WindowBuilder; use web_sys::HtmlCanvasElement; @@ -27,3 +31,38 @@ impl WindowBuilderExtWebSys for WindowBuilder { self } } + +/// Additional methods on `EventLoop` that are specific to the web. +pub trait EventLoopExtWebSys { + /// A type provided by the user that can be passed through `Event::UserEvent`. + type UserEvent; + + /// Initializes the winit event loop. + /// + /// Unlike `run`, this returns immediately, and doesn't throw an exception in order to + /// satisfy its `!` return type. + fn spawn(self, event_handler: F) + where + F: 'static + + FnMut( + Event<'_, Self::UserEvent>, + &EventLoopWindowTarget, + &mut ControlFlow, + ); +} + +impl EventLoopExtWebSys for EventLoop { + type UserEvent = T; + + fn spawn(self, event_handler: F) + where + F: 'static + + FnMut( + Event<'_, Self::UserEvent>, + &EventLoopWindowTarget, + &mut ControlFlow, + ), + { + self.event_loop.spawn(event_handler) + } +} diff --git a/src/platform_impl/web/event_loop/mod.rs b/src/platform_impl/web/event_loop/mod.rs index a8d234cf29..6bec70c972 100644 --- a/src/platform_impl/web/event_loop/mod.rs +++ b/src/platform_impl/web/event_loop/mod.rs @@ -29,7 +29,22 @@ impl EventLoop { } } - pub fn run(self, mut event_handler: F) -> ! + pub fn run(self, event_handler: F) -> ! + where + F: 'static + FnMut(Event<'_, T>, &RootEventLoopWindowTarget, &mut ControlFlow), + { + self.spawn(event_handler); + + // Throw an exception to break out of Rust execution and use unreachable to tell the + // compiler this function won't return, giving it a return type of '!' + backend::throw( + "Using exceptions for control flow, don't mind me. This isn't actually an error!", + ); + + unreachable!(); + } + + pub fn spawn(self, mut event_handler: F) where F: 'static + FnMut(Event<'_, T>, &RootEventLoopWindowTarget, &mut ControlFlow), { @@ -41,14 +56,6 @@ impl EventLoop { self.elw.p.run(Box::new(move |event, flow| { event_handler(event, &target, flow) })); - - // Throw an exception to break out of Rust exceution and use unreachable to tell the - // compiler this function won't return, giving it a return type of '!' - backend::throw( - "Using exceptions for control flow, don't mind me. This isn't actually an error!", - ); - - unreachable!(); } pub fn create_proxy(&self) -> EventLoopProxy { From 50dd7881b15ee3d67c14cabc00da23ce3b9a59af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20R=C3=B8yset?= Date: Wed, 13 Jul 2022 17:54:06 +0200 Subject: [PATCH 36/60] Fix changelog entry for `EventLoopExtWebSys` (#2372) --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9ba9563278..a650ac24be 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,7 @@ And please only add new entries to the top of this list, right below the `# Unre # Unreleased -- Added `EventLoopExtWebSys` with a `spawn` method to start the event loop without throwing an exception. +- On Web, added `EventLoopExtWebSys` with a `spawn` method to start the event loop without throwing an exception. - Added `WindowEvent::Occluded(bool)`, currently implemented on macOS and X11. - On X11, fix events for caps lock key not being sent - Build docs on `docs.rs` for iOS and Android as well. From 472d7b9376c33cd703377b379cb774e53738b6d7 Mon Sep 17 00:00:00 2001 From: Marijn Suijten Date: Thu, 14 Jul 2022 12:35:49 +0200 Subject: [PATCH 37/60] android: Hold `NativeWindow` lock until after notifying the user with `Event::Suspended` (#2307) This applies https://github.com/rust-windowing/android-ndk-rs/issues/117 on the `winit` side: Android destroys its window/surface as soon as the user returns from [`onNativeWindowDestroyed`], and we "fixed" this on the `ndk-glue` side by sending the `WindowDestroyed` event before locking the window and removing it: this lock has to wait for any user of `ndk-glue` - ie. `winit` - to give up its readlock on the window, which is what we utilize here to give users of `winit` "time" to destroy any resource created on top of a `RawWindowHandle`. since we can't pass the user a `RawWindowHandle` through the `HasRawWindowHandle` trait we have to document this case explicitly and keep the lock alive on the `winit` side instead. [`onNativeWindowDestroyed`]: https://developer.android.com/ndk/reference/struct/a-native-activity-callbacks#onnativewindowdestroyed --- CHANGELOG.md | 3 +- Cargo.toml | 4 +-- src/platform_impl/android/mod.rs | 53 +++++++++++++++++++++++--------- src/window.rs | 13 ++++++-- 4 files changed, 53 insertions(+), 20 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a650ac24be..1cb080d3ff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ And please only add new entries to the top of this list, right below the `# Unre # Unreleased +- On Android, `ndk-glue`'s `NativeWindow` lock is now held between `Event::Resumed` and `Event::Suspended`. - On Web, added `EventLoopExtWebSys` with a `spawn` method to start the event loop without throwing an exception. - Added `WindowEvent::Occluded(bool)`, currently implemented on macOS and X11. - On X11, fix events for caps lock key not being sent @@ -64,7 +65,7 @@ And please only add new entries to the top of this list, right below the `# Unre - Implemented `Eq` for `Fullscreen`, `Theme`, and `UserAttentionType`. - **Breaking:** `Window::set_cursor_grab` now accepts `CursorGrabMode` to control grabbing behavior. - On Wayland, add support for `Window::set_cursor_position`. -- Fix on macOS `WindowBuilder::with_disallow_hidpi`, setting true or false by the user no matter the SO default value. +- Fix on macOS `WindowBuilder::with_disallow_hidpi`, setting true or false by the user no matter the SO default value. - `EventLoopBuilder::build` will now panic when the `EventLoop` is being created more than once. - Added `From` for `WindowId` and `From` for `u64`. - Added `MonitorHandle::refresh_rate_millihertz` to get monitor's refresh rate. diff --git a/Cargo.toml b/Cargo.toml index ce8b35d109..403701127d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -55,8 +55,8 @@ simple_logger = "2.1.0" [target.'cfg(target_os = "android")'.dependencies] # Coordinate the next winit release with android-ndk-rs: https://github.com/rust-windowing/winit/issues/1995 -ndk = { git = "https://github.com/rust-windowing/android-ndk-rs", rev = "7e33384" } -ndk-glue = { git = "https://github.com/rust-windowing/android-ndk-rs", rev = "7e33384" } +ndk = { git = "https://github.com/rust-windowing/android-ndk-rs", rev = "a0c5e99" } +ndk-glue = { git = "https://github.com/rust-windowing/android-ndk-rs", rev = "a0c5e99" } [target.'cfg(any(target_os = "ios", target_os = "macos"))'.dependencies] objc = "0.2.7" diff --git a/src/platform_impl/android/mod.rs b/src/platform_impl/android/mod.rs index 1d39c86233..31972b81c3 100644 --- a/src/platform_impl/android/mod.rs +++ b/src/platform_impl/android/mod.rs @@ -10,8 +10,9 @@ use ndk::{ configuration::Configuration, event::{InputEvent, KeyAction, Keycode, MotionAction}, looper::{ForeignLooper, Poll, ThreadLooper}, + native_window::NativeWindow, }; -use ndk_glue::{Event, Rect}; +use ndk_glue::{Event, LockReadGuard, Rect}; use once_cell::sync::Lazy; use raw_window_handle::{HasRawWindowHandle, RawWindowHandle}; @@ -239,6 +240,7 @@ pub struct EventLoop { start_cause: event::StartCause, looper: ThreadLooper, running: bool, + window_lock: Option>, } #[derive(Default, Debug, Copy, Clone, PartialEq, Eq, Hash)] @@ -268,6 +270,7 @@ impl EventLoop { start_cause: event::StartCause::Init, looper: ThreadLooper::for_thread().unwrap(), running: false, + window_lock: None, } } @@ -300,22 +303,42 @@ impl EventLoop { match self.first_event.take() { Some(EventSource::Callback) => match ndk_glue::poll_events().unwrap() { Event::WindowCreated => { - call_event_handler!( - event_handler, - self.window_target(), - control_flow, - event::Event::Resumed - ); + // Acquire a lock on the window to prevent Android from destroying + // it until we've notified and waited for the user in Event::Suspended. + // WARNING: ndk-glue is inherently racy (https://github.com/rust-windowing/winit/issues/2293) + // and may have already received onNativeWindowDestroyed while this thread hasn't yet processed + // the event, and would see a `None` lock+window in that case. + if let Some(next_window_lock) = ndk_glue::native_window() { + assert!( + self.window_lock.replace(next_window_lock).is_none(), + "Received `Event::WindowCreated` while we were already holding a lock" + ); + call_event_handler!( + event_handler, + self.window_target(), + control_flow, + event::Event::Resumed + ); + } else { + warn!("Received `Event::WindowCreated` while `ndk_glue::native_window()` provides no window"); + } } Event::WindowResized => resized = true, Event::WindowRedrawNeeded => redraw = true, Event::WindowDestroyed => { - call_event_handler!( - event_handler, - self.window_target(), - control_flow, - event::Event::Suspended - ); + // Release the lock, allowing Android to clean up this surface + // WARNING: See above - if ndk-glue is racy, this event may be called + // without having a `self.window_lock` in place. + if self.window_lock.take().is_some() { + call_event_handler!( + event_handler, + self.window_target(), + control_flow, + event::Event::Suspended + ); + } else { + warn!("Received `Event::WindowDestroyed` while we were not holding a window lock"); + } } Event::Pause => self.running = false, Event::Resume => self.running = true, @@ -369,7 +392,7 @@ impl EventLoop { }, Some(EventSource::InputQueue) => { if let Some(input_queue) = ndk_glue::input_queue().as_ref() { - while let Some(event) = input_queue.get_event() { + while let Some(event) = input_queue.get_event().expect("get_event") { if let Some(event) = input_queue.pre_dispatch(event) { let mut handled = true; let window_id = window::WindowId(WindowId); @@ -799,7 +822,7 @@ impl Window { } pub fn raw_window_handle(&self) -> RawWindowHandle { - if let Some(native_window) = ndk_glue::native_window().as_ref() { + if let Some(native_window) = ndk_glue::native_window() { native_window.raw_window_handle() } else { panic!("Cannot get the native window, it's null and will always be null before Event::Resumed and after Event::Suspended. Make sure you only call this function between those events."); diff --git a/src/window.rs b/src/window.rs index 2d732e8a49..50bc252618 100644 --- a/src/window.rs +++ b/src/window.rs @@ -1039,8 +1039,17 @@ unsafe impl raw_window_handle::HasRawWindowHandle for Window { /// /// ## Platform-specific /// - /// - **Android:** Only available after receiving the Resumed event and before Suspended. *If you* - /// *try to get the handle outside of that period, this function will panic*! + /// ### Android + /// + /// Only available after receiving [`Event::Resumed`] and before [`Event::Suspended`]. *If you + /// try to get the handle outside of that period, this function will panic*! + /// + /// Make sure to release or destroy any resources created from this `RawWindowHandle` (ie. Vulkan + /// or OpenGL surfaces) before returning from [`Event::Suspended`], at which point Android will + /// release the underlying window/surface: any subsequent interaction is undefined behavior. + /// + /// [`Event::Resumed`]: crate::event::Event::Resumed + /// [`Event::Suspended`]: crate::event::Event::Suspended fn raw_window_handle(&self) -> raw_window_handle::RawWindowHandle { self.window.raw_window_handle() } From 990e34a129858bb81b16d0921cdee4c1dc5c9604 Mon Sep 17 00:00:00 2001 From: Josh Groves Date: Thu, 14 Jul 2022 13:22:31 -0230 Subject: [PATCH 38/60] web: add `with_prevent_default`, `with_focusable` (#2365) * web: add `with_prevent_default`, `with_focusable` `with_prevent_default` controls whether `event.preventDefault` is called `with_focusable` controls whether `tabindex` is added Fixes #1768 * Remove extra space from CHANGELOG --- CHANGELOG.md | 1 + src/platform/web.rs | 23 ++++ .../web/event_loop/window_target.rs | 113 ++++++++++-------- src/platform_impl/web/web_sys/canvas.rs | 50 +++++--- src/platform_impl/web/window.rs | 18 ++- 5 files changed, 137 insertions(+), 68 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1cb080d3ff..dbc289efee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -70,6 +70,7 @@ And please only add new entries to the top of this list, right below the `# Unre - Added `From` for `WindowId` and `From` for `u64`. - Added `MonitorHandle::refresh_rate_millihertz` to get monitor's refresh rate. - **Breaking**, Replaced `VideoMode::refresh_rate` with `VideoMode::refresh_rate_millihertz` providing better precision. +- On Web, add `with_prevent_default` and `with_focusable` to `WindowBuilderExtWebSys` to control whether events should be propagated. # 0.26.1 (2022-01-05) diff --git a/src/platform/web.rs b/src/platform/web.rs index 56017c8ae1..82e42db336 100644 --- a/src/platform/web.rs +++ b/src/platform/web.rs @@ -22,6 +22,17 @@ pub trait WindowExtWebSys { pub trait WindowBuilderExtWebSys { fn with_canvas(self, canvas: Option) -> Self; + + /// Whether `event.preventDefault` should be automatically called to prevent event propagation + /// when appropriate. + /// + /// For example, mouse wheel events are only handled by the canvas by default. This avoids + /// the default behavior of scrolling the page. + fn with_prevent_default(self, prevent_default: bool) -> Self; + + /// Whether the canvas should be focusable using the tab key. This is necessary to capture + /// canvas keyboard events. + fn with_focusable(self, focusable: bool) -> Self; } impl WindowBuilderExtWebSys for WindowBuilder { @@ -30,6 +41,18 @@ impl WindowBuilderExtWebSys for WindowBuilder { self } + + fn with_prevent_default(mut self, prevent_default: bool) -> Self { + self.platform_specific.prevent_default = prevent_default; + + self + } + + fn with_focusable(mut self, focusable: bool) -> Self { + self.platform_specific.focusable = focusable; + + self + } } /// Additional methods on `EventLoop` that are specific to the web. diff --git a/src/platform_impl/web/event_loop/window_target.rs b/src/platform_impl/web/event_loop/window_target.rs index 43d3034152..b936762945 100644 --- a/src/platform_impl/web/event_loop/window_target.rs +++ b/src/platform_impl/web/event_loop/window_target.rs @@ -50,7 +50,12 @@ impl EventLoopWindowTarget { WindowId(self.runner.generate_id()) } - pub fn register(&self, canvas: &Rc>, id: WindowId) { + pub fn register( + &self, + canvas: &Rc>, + id: WindowId, + prevent_default: bool, + ) { self.runner.add_canvas(RootWindowId(id), canvas); let mut canvas = canvas.borrow_mut(); canvas.set_attribute("data-raw-handle", &id.0.to_string()); @@ -72,48 +77,57 @@ impl EventLoopWindowTarget { }); let runner = self.runner.clone(); - canvas.on_keyboard_press(move |scancode, virtual_keycode, modifiers| { - #[allow(deprecated)] - runner.send_event(Event::WindowEvent { - window_id: RootWindowId(id), - event: WindowEvent::KeyboardInput { - device_id: RootDeviceId(unsafe { DeviceId::dummy() }), - input: KeyboardInput { - scancode, - state: ElementState::Pressed, - virtual_keycode, - modifiers, + canvas.on_keyboard_press( + move |scancode, virtual_keycode, modifiers| { + #[allow(deprecated)] + runner.send_event(Event::WindowEvent { + window_id: RootWindowId(id), + event: WindowEvent::KeyboardInput { + device_id: RootDeviceId(unsafe { DeviceId::dummy() }), + input: KeyboardInput { + scancode, + state: ElementState::Pressed, + virtual_keycode, + modifiers, + }, + is_synthetic: false, }, - is_synthetic: false, - }, - }); - }); + }); + }, + prevent_default, + ); let runner = self.runner.clone(); - canvas.on_keyboard_release(move |scancode, virtual_keycode, modifiers| { - #[allow(deprecated)] - runner.send_event(Event::WindowEvent { - window_id: RootWindowId(id), - event: WindowEvent::KeyboardInput { - device_id: RootDeviceId(unsafe { DeviceId::dummy() }), - input: KeyboardInput { - scancode, - state: ElementState::Released, - virtual_keycode, - modifiers, + canvas.on_keyboard_release( + move |scancode, virtual_keycode, modifiers| { + #[allow(deprecated)] + runner.send_event(Event::WindowEvent { + window_id: RootWindowId(id), + event: WindowEvent::KeyboardInput { + device_id: RootDeviceId(unsafe { DeviceId::dummy() }), + input: KeyboardInput { + scancode, + state: ElementState::Released, + virtual_keycode, + modifiers, + }, + is_synthetic: false, }, - is_synthetic: false, - }, - }); - }); + }); + }, + prevent_default, + ); let runner = self.runner.clone(); - canvas.on_received_character(move |char_code| { - runner.send_event(Event::WindowEvent { - window_id: RootWindowId(id), - event: WindowEvent::ReceivedCharacter(char_code), - }); - }); + canvas.on_received_character( + move |char_code| { + runner.send_event(Event::WindowEvent { + window_id: RootWindowId(id), + event: WindowEvent::ReceivedCharacter(char_code), + }); + }, + prevent_default, + ); let runner = self.runner.clone(); canvas.on_cursor_leave(move |pointer_id| { @@ -197,17 +211,20 @@ impl EventLoopWindowTarget { }); let runner = self.runner.clone(); - canvas.on_mouse_wheel(move |pointer_id, delta, modifiers| { - runner.send_event(Event::WindowEvent { - window_id: RootWindowId(id), - event: WindowEvent::MouseWheel { - device_id: RootDeviceId(DeviceId(pointer_id)), - delta, - phase: TouchPhase::Moved, - modifiers, - }, - }); - }); + canvas.on_mouse_wheel( + move |pointer_id, delta, modifiers| { + runner.send_event(Event::WindowEvent { + window_id: RootWindowId(id), + event: WindowEvent::MouseWheel { + device_id: RootDeviceId(DeviceId(pointer_id)), + delta, + phase: TouchPhase::Moved, + modifiers, + }, + }); + }, + prevent_default, + ); let runner = self.runner.clone(); let raw = canvas.raw().clone(); diff --git a/src/platform_impl/web/web_sys/canvas.rs b/src/platform_impl/web/web_sys/canvas.rs index 521e88a83f..ebae672227 100644 --- a/src/platform_impl/web/web_sys/canvas.rs +++ b/src/platform_impl/web/web_sys/canvas.rs @@ -62,9 +62,11 @@ impl Canvas { // sequential keyboard navigation, but its order is defined by the // document's source order. // https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/tabindex - canvas - .set_attribute("tabindex", "0") - .map_err(|_| os_error!(OsError("Failed to set a tabindex".to_owned())))?; + if attr.focusable { + canvas + .set_attribute("tabindex", "0") + .map_err(|_| os_error!(OsError("Failed to set a tabindex".to_owned())))?; + } let mouse_state = if has_pointer_event() { MouseState::HasPointerEvent(pointer_handler::PointerHandler::new()) @@ -148,14 +150,17 @@ impl Canvas { })); } - pub fn on_keyboard_release(&mut self, mut handler: F) + pub fn on_keyboard_release(&mut self, mut handler: F, prevent_default: bool) where F: 'static + FnMut(ScanCode, Option, ModifiersState), { self.on_keyboard_release = Some(self.common.add_user_event( "keyup", move |event: KeyboardEvent| { - event.prevent_default(); + if prevent_default { + event.prevent_default(); + } + handler( event::scan_code(&event), event::virtual_key_code(&event), @@ -165,7 +170,7 @@ impl Canvas { )); } - pub fn on_keyboard_press(&mut self, mut handler: F) + pub fn on_keyboard_press(&mut self, mut handler: F, prevent_default: bool) where F: 'static + FnMut(ScanCode, Option, ModifiersState), { @@ -173,16 +178,19 @@ impl Canvas { "keydown", move |event: KeyboardEvent| { // event.prevent_default() would suppress subsequent on_received_character() calls. That - // supression is correct for key sequences like Tab/Shift-Tab, Ctrl+R, PgUp/Down to + // suppression is correct for key sequences like Tab/Shift-Tab, Ctrl+R, PgUp/Down to // scroll, etc. We should not do it for key sequences that result in meaningful character // input though. - let event_key = &event.key(); - let is_key_string = event_key.len() == 1 || !event_key.is_ascii(); - let is_shortcut_modifiers = - (event.ctrl_key() || event.alt_key()) && !event.get_modifier_state("AltGr"); - if !is_key_string || is_shortcut_modifiers { - event.prevent_default(); + if prevent_default { + let event_key = &event.key(); + let is_key_string = event_key.len() == 1 || !event_key.is_ascii(); + let is_shortcut_modifiers = + (event.ctrl_key() || event.alt_key()) && !event.get_modifier_state("AltGr"); + if !is_key_string || is_shortcut_modifiers { + event.prevent_default(); + } } + handler( event::scan_code(&event), event::virtual_key_code(&event), @@ -192,7 +200,7 @@ impl Canvas { )); } - pub fn on_received_character(&mut self, mut handler: F) + pub fn on_received_character(&mut self, mut handler: F, prevent_default: bool) where F: 'static + FnMut(char), { @@ -204,8 +212,11 @@ impl Canvas { self.on_received_character = Some(self.common.add_user_event( "keypress", move |event: KeyboardEvent| { - // Supress further handling to stop keys like the space key from scrolling the page. - event.prevent_default(); + // Suppress further handling to stop keys like the space key from scrolling the page. + if prevent_default { + event.prevent_default(); + } + handler(event::codepoint(&event)); }, )); @@ -261,12 +272,15 @@ impl Canvas { } } - pub fn on_mouse_wheel(&mut self, mut handler: F) + pub fn on_mouse_wheel(&mut self, mut handler: F, prevent_default: bool) where F: 'static + FnMut(i32, MouseScrollDelta, ModifiersState), { self.on_mouse_wheel = Some(self.common.add_event("wheel", move |event: WheelEvent| { - event.prevent_default(); + if prevent_default { + event.prevent_default(); + } + if let Some(delta) = event::mouse_scroll_delta(&event) { handler(0, delta, event::mouse_modifiers(&event)); } diff --git a/src/platform_impl/web/window.rs b/src/platform_impl/web/window.rs index 78299c8ce0..fe1d4b229a 100644 --- a/src/platform_impl/web/window.rs +++ b/src/platform_impl/web/window.rs @@ -35,12 +35,14 @@ impl Window { let id = target.generate_id(); + let prevent_default = platform_attr.prevent_default; + let canvas = backend::Canvas::create(platform_attr)?; let canvas = Rc::new(RefCell::new(canvas)); let register_redraw_request = Box::new(move || runner.request_redraw(RootWI(id))); - target.register(&canvas, id); + target.register(&canvas, id, prevent_default); let runner = target.runner.clone(); let resize_notify_fn = Box::new(move |new_size| { @@ -392,7 +394,19 @@ impl From for WindowId { } } -#[derive(Default, Clone)] +#[derive(Clone)] pub struct PlatformSpecificWindowBuilderAttributes { pub(crate) canvas: Option, + pub(crate) prevent_default: bool, + pub(crate) focusable: bool, +} + +impl Default for PlatformSpecificWindowBuilderAttributes { + fn default() -> Self { + Self { + canvas: None, + prevent_default: true, + focusable: true, + } + } } From 9116b6c8cd92f0b38e310ed03088e87db50bb465 Mon Sep 17 00:00:00 2001 From: Josh Groves Date: Thu, 14 Jul 2022 17:30:22 -0230 Subject: [PATCH 39/60] windows: Use correct value for mouse wheel delta (#2374) --- src/platform_impl/windows/event_loop.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/platform_impl/windows/event_loop.rs b/src/platform_impl/windows/event_loop.rs index 7e4b7fbdb8..d0a857436b 100644 --- a/src/platform_impl/windows/event_loop.rs +++ b/src/platform_impl/windows/event_loop.rs @@ -2272,7 +2272,8 @@ unsafe extern "system" fn thread_event_target_callback( let mouse_button_flags = mouse.Anonymous.Anonymous.usButtonFlags; if util::has_flag(mouse_button_flags as u32, RI_MOUSE_WHEEL) { - let delta = mouse_button_flags as i16 as f32 / WHEEL_DELTA as f32; + let delta = mouse.Anonymous.Anonymous.usButtonData as i16 as f32 + / WHEEL_DELTA as f32; userdata.send_event(Event::DeviceEvent { device_id, event: MouseWheel { From 1091a8ba1adb6533cb3747d49cf4c08303cdb92a Mon Sep 17 00:00:00 2001 From: Steve Wooster Date: Fri, 15 Jul 2022 01:27:27 -0700 Subject: [PATCH 40/60] Make winit focus take activity into account on Windows (#2159) winit's notion of "focus" is very simple; you're either focused or not. However, Windows has both notions of focused window and active window and paying attention only to WM_SETFOCUS/WM_KILLFOCUS can cause a window to believe the user is interacting with it when they're not. (this manifests when a user switches to another application between when a winit application starts and it creates its first window) --- CHANGELOG.md | 1 + src/platform_impl/windows/event_loop.rs | 150 +++++++++++++--------- src/platform_impl/windows/window_state.rs | 25 ++++ 3 files changed, 114 insertions(+), 62 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dbc289efee..5e412377c2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -71,6 +71,7 @@ And please only add new entries to the top of this list, right below the `# Unre - Added `MonitorHandle::refresh_rate_millihertz` to get monitor's refresh rate. - **Breaking**, Replaced `VideoMode::refresh_rate` with `VideoMode::refresh_rate_millihertz` providing better precision. - On Web, add `with_prevent_default` and `with_focusable` to `WindowBuilderExtWebSys` to control whether events should be propagated. +- On Windows, fix focus events being sent to inactive windows. # 0.26.1 (2022-01-05) diff --git a/src/platform_impl/windows/event_loop.rs b/src/platform_impl/windows/event_loop.rs index d0a857436b..870f323d96 100644 --- a/src/platform_impl/windows/event_loop.rs +++ b/src/platform_impl/windows/event_loop.rs @@ -61,7 +61,7 @@ use windows_sys::Win32::{ WM_GETMINMAXINFO, WM_IME_COMPOSITION, WM_IME_ENDCOMPOSITION, WM_IME_SETCONTEXT, WM_IME_STARTCOMPOSITION, WM_INPUT, WM_INPUT_DEVICE_CHANGE, WM_KEYDOWN, WM_KEYUP, WM_KILLFOCUS, WM_LBUTTONDOWN, WM_LBUTTONUP, WM_MBUTTONDOWN, WM_MBUTTONUP, - WM_MOUSEHWHEEL, WM_MOUSEMOVE, WM_MOUSEWHEEL, WM_NCCREATE, WM_NCDESTROY, + WM_MOUSEHWHEEL, WM_MOUSEMOVE, WM_MOUSEWHEEL, WM_NCACTIVATE, WM_NCCREATE, WM_NCDESTROY, WM_NCLBUTTONDOWN, WM_PAINT, WM_POINTERDOWN, WM_POINTERUP, WM_POINTERUPDATE, WM_RBUTTONDOWN, WM_RBUTTONUP, WM_SETCURSOR, WM_SETFOCUS, WM_SETTINGCHANGE, WM_SIZE, WM_SYSCHAR, WM_SYSCOMMAND, WM_SYSKEYDOWN, WM_SYSKEYUP, WM_TOUCH, WM_WINDOWPOSCHANGED, @@ -795,6 +795,74 @@ fn update_modifiers(window: HWND, userdata: &WindowData) { } } +unsafe fn gain_active_focus(window: HWND, userdata: &WindowData) { + use crate::event::{ElementState::Released, WindowEvent::Focused}; + for windows_keycode in event::get_pressed_keys() { + let scancode = MapVirtualKeyA(windows_keycode as u32, MAPVK_VK_TO_VSC); + let virtual_keycode = event::vkey_to_winit_vkey(windows_keycode); + + update_modifiers(window, userdata); + + #[allow(deprecated)] + userdata.send_event(Event::WindowEvent { + window_id: RootWindowId(WindowId(window)), + event: WindowEvent::KeyboardInput { + device_id: DEVICE_ID, + input: KeyboardInput { + scancode, + virtual_keycode, + state: Released, + modifiers: event::get_key_mods(), + }, + is_synthetic: true, + }, + }) + } + + userdata.send_event(Event::WindowEvent { + window_id: RootWindowId(WindowId(window)), + event: Focused(true), + }); +} + +unsafe fn lose_active_focus(window: HWND, userdata: &WindowData) { + use crate::event::{ + ElementState::Released, + ModifiersState, + WindowEvent::{Focused, ModifiersChanged}, + }; + for windows_keycode in event::get_pressed_keys() { + let scancode = MapVirtualKeyA(windows_keycode as u32, MAPVK_VK_TO_VSC); + let virtual_keycode = event::vkey_to_winit_vkey(windows_keycode); + + #[allow(deprecated)] + userdata.send_event(Event::WindowEvent { + window_id: RootWindowId(WindowId(window)), + event: WindowEvent::KeyboardInput { + device_id: DEVICE_ID, + input: KeyboardInput { + scancode, + virtual_keycode, + state: Released, + modifiers: event::get_key_mods(), + }, + is_synthetic: true, + }, + }) + } + + userdata.window_state.lock().modifiers_state = ModifiersState::empty(); + userdata.send_event(Event::WindowEvent { + window_id: RootWindowId(WindowId(window)), + event: ModifiersChanged(ModifiersState::empty()), + }); + + userdata.send_event(Event::WindowEvent { + window_id: RootWindowId(WindowId(window)), + event: Focused(false), + }); +} + /// Any window whose callback is configured to this function will have its events propagated /// through the events loop of the thread the window was created in. // @@ -1770,74 +1838,32 @@ unsafe fn public_window_callback_inner( 0 } - WM_SETFOCUS => { - use crate::event::{ElementState::Released, WindowEvent::Focused}; - for windows_keycode in event::get_pressed_keys() { - let scancode = MapVirtualKeyA(windows_keycode as u32, MAPVK_VK_TO_VSC); - let virtual_keycode = event::vkey_to_winit_vkey(windows_keycode); - - update_modifiers(window, userdata); - - #[allow(deprecated)] - userdata.send_event(Event::WindowEvent { - window_id: RootWindowId(WindowId(window)), - event: WindowEvent::KeyboardInput { - device_id: DEVICE_ID, - input: KeyboardInput { - scancode, - virtual_keycode, - state: Released, - modifiers: event::get_key_mods(), - }, - is_synthetic: true, - }, - }) + WM_NCACTIVATE => { + let is_active = wparam == 1; + let active_focus_changed = userdata.window_state.lock().set_active(is_active); + if active_focus_changed { + if is_active { + gain_active_focus(window, userdata); + } else { + lose_active_focus(window, userdata); + } } + DefWindowProcW(window, msg, wparam, lparam) + } - userdata.send_event(Event::WindowEvent { - window_id: RootWindowId(WindowId(window)), - event: Focused(true), - }); - + WM_SETFOCUS => { + let active_focus_changed = userdata.window_state.lock().set_focused(true); + if active_focus_changed { + gain_active_focus(window, userdata); + } 0 } WM_KILLFOCUS => { - use crate::event::{ - ElementState::Released, - ModifiersState, - WindowEvent::{Focused, ModifiersChanged}, - }; - for windows_keycode in event::get_pressed_keys() { - let scancode = MapVirtualKeyA(windows_keycode as u32, MAPVK_VK_TO_VSC); - let virtual_keycode = event::vkey_to_winit_vkey(windows_keycode); - - #[allow(deprecated)] - userdata.send_event(Event::WindowEvent { - window_id: RootWindowId(WindowId(window)), - event: WindowEvent::KeyboardInput { - device_id: DEVICE_ID, - input: KeyboardInput { - scancode, - virtual_keycode, - state: Released, - modifiers: event::get_key_mods(), - }, - is_synthetic: true, - }, - }) + let active_focus_changed = userdata.window_state.lock().set_focused(false); + if active_focus_changed { + lose_active_focus(window, userdata); } - - userdata.window_state.lock().modifiers_state = ModifiersState::empty(); - userdata.send_event(Event::WindowEvent { - window_id: RootWindowId(WindowId(window)), - event: ModifiersChanged(ModifiersState::empty()), - }); - - userdata.send_event(Event::WindowEvent { - window_id: RootWindowId(WindowId(window)), - event: Focused(false), - }); 0 } diff --git a/src/platform_impl/windows/window_state.rs b/src/platform_impl/windows/window_state.rs index 744dca0a18..35a8f0d51e 100644 --- a/src/platform_impl/windows/window_state.rs +++ b/src/platform_impl/windows/window_state.rs @@ -45,6 +45,10 @@ pub struct WindowState { pub ime_state: ImeState, pub ime_allowed: bool, + + // Used by WM_NCACTIVATE, WM_SETFOCUS and WM_KILLFOCUS + pub is_active: bool, + pub is_focused: bool, } #[derive(Clone)] @@ -145,6 +149,9 @@ impl WindowState { ime_state: ImeState::Disabled, ime_allowed: false, + + is_active: false, + is_focused: false, } } @@ -170,6 +177,24 @@ impl WindowState { { f(&mut self.window_flags); } + + pub fn has_active_focus(&self) -> bool { + self.is_active && self.is_focused + } + + // Updates is_active and returns whether active-focus state has changed + pub fn set_active(&mut self, is_active: bool) -> bool { + let old = self.has_active_focus(); + self.is_active = is_active; + old != self.has_active_focus() + } + + // Updates is_focused and returns whether active-focus state has changed + pub fn set_focused(&mut self, is_focused: bool) -> bool { + let old = self.has_active_focus(); + self.is_focused = is_focused; + old != self.has_active_focus() + } } impl MouseProperties { From 430a49ebc2e1dbe7a8e06518e52bd5de7f33f2e6 Mon Sep 17 00:00:00 2001 From: Josh Groves Date: Fri, 15 Jul 2022 14:02:12 -0230 Subject: [PATCH 41/60] Fix typos (#2375) --- src/dpi.rs | 2 +- src/event_loop.rs | 2 +- src/platform_impl/android/mod.rs | 2 +- src/platform_impl/linux/wayland/event_loop/mod.rs | 2 +- src/platform_impl/linux/wayland/window/mod.rs | 2 +- src/platform_impl/linux/x11/ime/input_method.rs | 6 +++--- src/platform_impl/linux/x11/mod.rs | 2 +- src/platform_impl/linux/x11/util/window_property.rs | 2 +- src/platform_impl/web/event_loop/runner.rs | 2 +- src/window.rs | 2 +- 10 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/dpi.rs b/src/dpi.rs index bf234007b9..3c3829359f 100644 --- a/src/dpi.rs +++ b/src/dpi.rs @@ -77,7 +77,7 @@ //! currently uses a three-pronged approach: //! + Use the value in the `WINIT_X11_SCALE_FACTOR` environment variable, if present. //! + If not present, use the value set in `Xft.dpi` in Xresources. -//! + Otherwise, calcuate the scale factor based on the millimeter monitor dimensions provided by XRandR. +//! + Otherwise, calculate the scale factor based on the millimeter monitor dimensions provided by XRandR. //! //! If `WINIT_X11_SCALE_FACTOR` is set to `randr`, it'll ignore the `Xft.dpi` field and use the //! XRandR scaling method. Generally speaking, you should try to configure the standard system diff --git a/src/event_loop.rs b/src/event_loop.rs index 890e4e560e..dc95934f5a 100644 --- a/src/event_loop.rs +++ b/src/event_loop.rs @@ -383,7 +383,7 @@ impl fmt::Display for EventLoopClosed { impl error::Error for EventLoopClosed {} -/// Fiter controlling the propagation of device events. +/// Filter controlling the propagation of device events. #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] pub enum DeviceEventFilter { /// Always filter out device events. diff --git a/src/platform_impl/android/mod.rs b/src/platform_impl/android/mod.rs index 31972b81c3..405130fcd9 100644 --- a/src/platform_impl/android/mod.rs +++ b/src/platform_impl/android/mod.rs @@ -881,7 +881,7 @@ impl MonitorHandle { } pub fn refresh_rate_millihertz(&self) -> Option { - // FIXME no way to get real refrsh rate for now. + // FIXME no way to get real refresh rate for now. None } diff --git a/src/platform_impl/linux/wayland/event_loop/mod.rs b/src/platform_impl/linux/wayland/event_loop/mod.rs index 491883ab58..8fe13bdee0 100644 --- a/src/platform_impl/linux/wayland/event_loop/mod.rs +++ b/src/platform_impl/linux/wayland/event_loop/mod.rs @@ -64,7 +64,7 @@ pub struct EventLoopWindowTarget { /// Theme manager to manage cursors. /// - /// It's being shared amoung all windows to avoid loading + /// It's being shared between all windows to avoid loading /// multiple similar themes. pub theme_manager: ThemeManager, diff --git a/src/platform_impl/linux/wayland/window/mod.rs b/src/platform_impl/linux/wayland/window/mod.rs index 18bbee541b..c061352872 100644 --- a/src/platform_impl/linux/wayland/window/mod.rs +++ b/src/platform_impl/linux/wayland/window/mod.rs @@ -90,7 +90,7 @@ impl Window { .create_surface_with_scale_callback(move |scale, surface, mut dispatch_data| { let winit_state = dispatch_data.get::().unwrap(); - // Get the window that receiced the event. + // Get the window that received the event. let window_id = super::make_wid(&surface); let mut window_update = winit_state.window_updates.get_mut(&window_id).unwrap(); diff --git a/src/platform_impl/linux/x11/ime/input_method.rs b/src/platform_impl/linux/x11/ime/input_method.rs index 5a1695cb0e..a747f972a9 100644 --- a/src/platform_impl/linux/x11/ime/input_method.rs +++ b/src/platform_impl/linux/x11/ime/input_method.rs @@ -180,10 +180,10 @@ impl PotentialInputMethod { } // By logging this struct, you get a sequential listing of every locale modifier tried, where it -// came from, and if it succceeded. +// came from, and if it succeeded. #[derive(Debug, Clone)] pub struct PotentialInputMethods { - // On correctly configured systems, the XMODIFIERS environemnt variable tells us everything we + // On correctly configured systems, the XMODIFIERS environment variable tells us everything we // need to know. xmodifiers: Option, // We have some standard options at our disposal that should ostensibly always work. For users @@ -213,7 +213,7 @@ impl PotentialInputMethods { // that case, we get `None` and end up skipping ahead to the next method. xmodifiers, fallbacks: [ - // This is a standard input method that supports compose equences, which should + // This is a standard input method that supports compose sequences, which should // always be available. `@im=none` appears to mean the same thing. PotentialInputMethod::from_str("@im=local"), // This explicitly specifies to use the implementation-dependent default, though diff --git a/src/platform_impl/linux/x11/mod.rs b/src/platform_impl/linux/x11/mod.rs index 6ee2fb827d..ff6f012338 100644 --- a/src/platform_impl/linux/x11/mod.rs +++ b/src/platform_impl/linux/x11/mod.rs @@ -457,7 +457,7 @@ impl EventLoop { // must do this because during the execution of the iteration we sometimes wake // the mio waker, and if the waker is already awaken before we call poll(), // then poll doesn't block, but it returns immediately. This caused the event - // loop to run continously even if the control_flow was `Wait` + // loop to run continuously even if the control_flow was `Wait` continue; } } diff --git a/src/platform_impl/linux/x11/util/window_property.rs b/src/platform_impl/linux/x11/util/window_property.rs index 8bf718ea8e..e93d5bb072 100644 --- a/src/platform_impl/linux/x11/util/window_property.rs +++ b/src/platform_impl/linux/x11/util/window_property.rs @@ -58,7 +58,7 @@ impl XConnection { property, // This offset is in terms of 32-bit chunks. offset, - // This is the quanity of 32-bit chunks to receive at once. + // This is the quantity of 32-bit chunks to receive at once. PROPERTY_BUFFER_SIZE, ffi::False, property_type, diff --git a/src/platform_impl/web/event_loop/runner.rs b/src/platform_impl/web/event_loop/runner.rs index 840ba558e6..fd9a2e850e 100644 --- a/src/platform_impl/web/event_loop/runner.rs +++ b/src/platform_impl/web/event_loop/runner.rs @@ -65,7 +65,7 @@ impl Runner { } } - /// Returns the cooresponding `StartCause` for the current `state`, or `None` + /// Returns the corresponding `StartCause` for the current `state`, or `None` /// when in `Exit` state. fn maybe_start_cause(&self) -> Option { Some(match self.state { diff --git a/src/window.rs b/src/window.rs index 50bc252618..a3598ff54c 100644 --- a/src/window.rs +++ b/src/window.rs @@ -625,7 +625,7 @@ impl Window { self.window.set_visible(visible) } - /// Gets the window's current vibility state. + /// Gets the window's current visibility state. /// /// `None` means it couldn't be determined, so it is not recommended to use this to drive your rendering backend. /// From f09259f6deb6f9dd99b8e116bc6edbb65cfb51e3 Mon Sep 17 00:00:00 2001 From: Kirill Chibisov Date: Wed, 20 Jul 2022 11:50:49 +0300 Subject: [PATCH 42/60] Bump sctk-adwaita to 0.4.1 This should force the use of system libraries for Fontconfig and freetype instead of building them with cmake if missing. This also fixes compilation failures on nightly. Fixes #2373. --- Cargo.toml | 2 +- src/platform_impl/macos/app_delegate.rs | 2 +- src/platform_impl/macos/event_loop.rs | 2 +- src/platform_impl/macos/view.rs | 2 +- src/platform_impl/macos/window_delegate.rs | 2 +- src/platform_impl/windows/drop_handler.rs | 2 +- src/platform_impl/windows/event_loop.rs | 6 +++--- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 403701127d..7171992486 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -103,7 +103,7 @@ features = [ wayland-client = { version = "0.29.4", default_features = false, features = ["use_system_lib"], optional = true } wayland-protocols = { version = "0.29.4", features = [ "staging_protocols"], optional = true } sctk = { package = "smithay-client-toolkit", version = "0.16.0", default_features = false, features = ["calloop"], optional = true } -sctk-adwaita = { version = "0.4.0", optional = true } +sctk-adwaita = { version = "0.4.1", optional = true } mio = { version = "0.8", features = ["os-ext"], optional = true } x11-dl = { version = "2.18.5", optional = true } percent-encoding = { version = "2.0", optional = true } diff --git a/src/platform_impl/macos/app_delegate.rs b/src/platform_impl/macos/app_delegate.rs index 0ba31ecd40..fc5ae7e407 100644 --- a/src/platform_impl/macos/app_delegate.rs +++ b/src/platform_impl/macos/app_delegate.rs @@ -72,7 +72,7 @@ extern "C" fn dealloc(this: &Object, _: Sel) { let state_ptr: *mut c_void = *(this.get_ivar(AUX_DELEGATE_STATE_NAME)); // As soon as the box is constructed it is immediately dropped, releasing the underlying // memory - Box::from_raw(state_ptr as *mut RefCell); + drop(Box::from_raw(state_ptr as *mut RefCell)); } } diff --git a/src/platform_impl/macos/event_loop.rs b/src/platform_impl/macos/event_loop.rs index c39036997d..4a8b058cab 100644 --- a/src/platform_impl/macos/event_loop.rs +++ b/src/platform_impl/macos/event_loop.rs @@ -210,7 +210,7 @@ impl EventLoop { // A bit of juggling with the callback references to make sure // that `self.callback` is the only owner of the callback. let weak_cb: Weak<_> = Rc::downgrade(&callback); - mem::drop(callback); + drop(callback); AppState::set_callback(weak_cb, Rc::clone(&self.window_target)); let _: () = msg_send![app, run]; diff --git a/src/platform_impl/macos/view.rs b/src/platform_impl/macos/view.rs index b8c93cb5e2..9fe6b0dd32 100644 --- a/src/platform_impl/macos/view.rs +++ b/src/platform_impl/macos/view.rs @@ -325,7 +325,7 @@ extern "C" fn dealloc(this: &Object, _sel: Sel) { let marked_text: id = *this.get_ivar("markedText"); let _: () = msg_send![marked_text, release]; let state: *mut c_void = *this.get_ivar("winitState"); - Box::from_raw(state as *mut ViewState); + drop(Box::from_raw(state as *mut ViewState)); } } diff --git a/src/platform_impl/macos/window_delegate.rs b/src/platform_impl/macos/window_delegate.rs index bb855cd5ef..001c89fdd3 100644 --- a/src/platform_impl/macos/window_delegate.rs +++ b/src/platform_impl/macos/window_delegate.rs @@ -241,7 +241,7 @@ fn with_state T, T>(this: &Object, callba extern "C" fn dealloc(this: &Object, _sel: Sel) { with_state(this, |state| unsafe { - Box::from_raw(state as *mut WindowDelegateState); + drop(Box::from_raw(state as *mut WindowDelegateState)); }); } diff --git a/src/platform_impl/windows/drop_handler.rs b/src/platform_impl/windows/drop_handler.rs index f273ad0036..2db12a67a8 100644 --- a/src/platform_impl/windows/drop_handler.rs +++ b/src/platform_impl/windows/drop_handler.rs @@ -80,7 +80,7 @@ impl FileDropHandler { let count = drop_handler.refcount.fetch_sub(1, Ordering::Release) - 1; if count == 0 { // Destroy the underlying data - Box::from_raw(drop_handler as *mut FileDropHandlerData); + drop(Box::from_raw(drop_handler as *mut FileDropHandlerData)); } count as u32 } diff --git a/src/platform_impl/windows/event_loop.rs b/src/platform_impl/windows/event_loop.rs index 870f323d96..1872f422e8 100644 --- a/src/platform_impl/windows/event_loop.rs +++ b/src/platform_impl/windows/event_loop.rs @@ -923,7 +923,7 @@ pub(super) unsafe extern "system" fn public_window_callback( }; if userdata_removed && recurse_depth == 0 { - Box::from_raw(userdata_ptr); + drop(Box::from_raw(userdata_ptr)); } result @@ -2154,7 +2154,7 @@ unsafe fn public_window_callback_inner( if window_state.current_theme != new_theme { window_state.current_theme = new_theme; - mem::drop(window_state); + drop(window_state); userdata.send_event(Event::WindowEvent { window_id: RootWindowId(WindowId(window)), event: ThemeChanged(new_theme), @@ -2413,7 +2413,7 @@ unsafe extern "system" fn thread_event_target_callback( .catch_unwind(callback) .unwrap_or(-1); if userdata_removed { - mem::drop(userdata); + drop(userdata); } else { Box::into_raw(userdata); } From 5397b53e04c5e9071db770d65478fa0691abad83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20R=C3=B8yset?= Date: Wed, 20 Jul 2022 13:45:12 +0200 Subject: [PATCH 43/60] Tidy up "platform-specifc" doc sections (#2356) * Tidy up "platform-specific" doc sections * Unrelated grammatical fix * Subjective improvements --- src/event.rs | 18 +++++++++++------- src/event_loop.rs | 9 +++++---- src/platform/run_return.rs | 2 +- src/window.rs | 33 +++++++++++++++++---------------- 4 files changed, 34 insertions(+), 28 deletions(-) diff --git a/src/event.rs b/src/event.rs index e6710d4454..9b0b94d80a 100644 --- a/src/event.rs +++ b/src/event.rs @@ -277,17 +277,19 @@ pub enum WindowEvent<'a> { /// The keyboard modifiers have changed. /// - /// Platform-specific behavior: - /// - **Web**: This API is currently unimplemented on the web. This isn't by design - it's an + /// ## Platform-specific + /// + /// - **Web:** This API is currently unimplemented on the web. This isn't by design - it's an /// issue, and it should get fixed - but it's the current state of the API. ModifiersChanged(ModifiersState), - /// An event from input method. + /// An event from an input method. /// - /// **Note :** You have to explicitly enable this event using [`Window::set_ime_allowed`]. + /// **Note:** You have to explicitly enable this event using [`Window::set_ime_allowed`]. /// - /// Platform-specific behavior: - /// - **iOS / Android / Web :** Unsupported. + /// ## Platform-specific + /// + /// - **iOS / Android / Web:** Unsupported. Ime(Ime), /// The cursor has moved on the window. @@ -370,6 +372,8 @@ pub enum WindowEvent<'a> { /// Applications might wish to react to this to change the theme of the content of the window /// when the system changes the window theme. /// + /// ## Platform-specific + /// /// At the moment this is only supported on Windows. ThemeChanged(Theme), @@ -379,7 +383,7 @@ pub enum WindowEvent<'a> { /// minimised, set invisible, or fully occluded by another window. /// /// Platform-specific behavior: - /// - **iOS / Android / Web / Wayland / Windows :** Unsupported. + /// - **iOS / Android / Web / Wayland / Windows:** Unsupported. Occluded(bool), } diff --git a/src/event_loop.rs b/src/event_loop.rs index dc95934f5a..6b5e054ef1 100644 --- a/src/event_loop.rs +++ b/src/event_loop.rs @@ -146,6 +146,7 @@ pub enum ControlFlow { /// whether or not new events are available to process. /// /// ## Platform-specific + /// /// - **Web:** Events are queued and usually sent when `requestAnimationFrame` fires but sometimes /// the events in the queue may be sent before the next `requestAnimationFrame` callback, for /// example when the scaling of the page has changed. This should be treated as an implementation @@ -171,8 +172,8 @@ pub enum ControlFlow { /// /// ## Platform-specific /// - /// - **Android / iOS / WASM**: The supplied exit code is unused. - /// - **Unix**: On most Unix-like platforms, only the 8 least significant bits will be used, + /// - **Android / iOS / WASM:** The supplied exit code is unused. + /// - **Unix:** On most Unix-like platforms, only the 8 least significant bits will be used, /// which can cause surprises with negative exit values (`-42` would end up as `214`). See /// [`std::process::exit`]. /// @@ -264,7 +265,7 @@ impl EventLoop { /// /// ## Platform-specific /// - /// - **X11 / Wayland**: The program terminates with exit code 1 if the display server + /// - **X11 / Wayland:** The program terminates with exit code 1 if the display server /// disconnects. /// /// [`ControlFlow`]: crate::event_loop::ControlFlow @@ -321,7 +322,7 @@ impl EventLoopWindowTarget { /// /// ## Platform-specific /// - /// - **Wayland / Windows / macOS / iOS / Android / Web**: Unsupported. + /// - **Wayland / Windows / macOS / iOS / Android / Web:** Unsupported. /// /// [`DeviceEvent`]: crate::event::DeviceEvent pub fn set_device_event_filter(&self, _filter: DeviceEventFilter) { diff --git a/src/platform/run_return.rs b/src/platform/run_return.rs index 5c25461540..5013823bc1 100644 --- a/src/platform/run_return.rs +++ b/src/platform/run_return.rs @@ -37,7 +37,7 @@ pub trait EventLoopExtRunReturn { /// /// ## Platform-specific /// - /// - **Unix-alikes** (**X11** or **Wayland**): This function returns `1` upon disconnection from + /// - **X11 / Wayland:** This function returns `1` upon disconnection from /// the display server. fn run_return(&mut self, event_handler: F) -> i32 where diff --git a/src/window.rs b/src/window.rs index a3598ff54c..0dc31e226f 100644 --- a/src/window.rs +++ b/src/window.rs @@ -203,20 +203,20 @@ impl WindowBuilder { /// /// ## Platform-specific /// - /// - **macOS**: The top left corner position of the window content, the + /// - **macOS:** The top left corner position of the window content, the /// window's "inner" position. The window title bar will be placed above /// it. The window will be positioned such that it fits on screen, /// maintaining set `inner_size` if any. /// If you need to precisely position the top left corner of the whole /// window you have to use [`Window::set_outer_position`] after creating /// the window. - /// - **Windows**: The top left corner position of the window title bar, + /// - **Windows:** The top left corner position of the window title bar, /// the window's "outer" position. /// There may be a small gap between this position and the window due to /// the specifics of the Window Manager. - /// - **X11**: The top left corner of the window, the window's "outer" + /// - **X11:** The top left corner of the window, the window's "outer" /// position. - /// - **Others**: Ignored. + /// - **Others:** Ignored. #[inline] pub fn with_position>(mut self, position: P) -> Self { self.window.position = Some(position.into()); @@ -333,8 +333,9 @@ impl WindowBuilder { /// /// Possible causes of error include denied permission, incompatible system, and lack of memory. /// - /// Platform-specific behavior: - /// - **Web**: The window is created but not inserted into the web page automatically. Please + /// ## Platform-specific + /// + /// - **Web:** The window is created but not inserted into the web page automatically. Please /// see the web platform module for more information. #[inline] pub fn build( @@ -359,8 +360,9 @@ impl Window { /// Error should be very rare and only occur in case of permission denied, incompatible system, /// out of memory, etc. /// - /// Platform-specific behavior: - /// - **Web**: The window is created but not inserted into the web page automatically. Please + /// ## Platform-specific + /// + /// - **Web:** The window is created but not inserted into the web page automatically. Please /// see the web platform module for more information. /// /// [`WindowBuilder::new().build(event_loop)`]: WindowBuilder::build @@ -648,10 +650,7 @@ impl Window { /// /// This only has an effect on desktop platforms. /// - /// Due to a bug in XFCE, this has no effect on Xfwm. - /// - /// ## Platform-specific - /// + /// - **X11:** Due to a bug in XFCE, this has no effect on Xfwm. /// - **iOS / Android / Web:** Unsupported. /// /// [`WindowEvent::Resized`]: crate::event::WindowEvent::Resized @@ -839,7 +838,7 @@ impl Window { /// ## Platform-specific /// /// - **macOS:** IME must be enabled to receive text-input where dead-key sequences are combined. - /// - ** iOS / Android / Web :** Unsupported. + /// - **iOS / Android / Web:** Unsupported. /// /// [`Ime`]: crate::event::WindowEvent::Ime /// [`KeyboardInput`]: crate::event::WindowEvent::KeyboardInput @@ -873,7 +872,7 @@ impl Window { /// /// ## Platform-specific /// - /// - **iOS / Android / Web :** Unsupported. + /// - **iOS / Android / Web:** Unsupported. /// - **macOS:** `None` has no effect. /// - **X11:** Requests for user attention must be manually cleared. /// - **Wayland:** Requires `xdg_activation_v1` protocol, `None` has no effect. @@ -1072,7 +1071,7 @@ pub enum CursorGrabMode { /// ## Platform-specific /// /// - **macOS:** Not implemented. Always returns [`ExternalError::NotSupported`] for now. - /// - ** iOS / Android / Web:** Always returns an [`ExternalError::NotSupported`]. + /// - **iOS / Android / Web:** Always returns an [`ExternalError::NotSupported`]. Confined, /// The cursor is locked inside the window area to the certain position. @@ -1083,7 +1082,7 @@ pub enum CursorGrabMode { /// ## Platform-specific /// /// - **X11 / Windows:** Not implemented. Always returns [`ExternalError::NotSupported`] for now. - /// - ** iOS / Android:** Always returns an [`ExternalError::NotSupported`]. + /// - **iOS / Android:** Always returns an [`ExternalError::NotSupported`]. Locked, } @@ -1176,10 +1175,12 @@ pub enum Theme { #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum UserAttentionType { /// ## Platform-specific + /// /// - **macOS:** Bounces the dock icon until the application is in focus. /// - **Windows:** Flashes both the window and the taskbar button until the application is in focus. Critical, /// ## Platform-specific + /// /// - **macOS:** Bounces the dock icon once. /// - **Windows:** Flashes the taskbar button until the application is in focus. Informational, From 3e991e13dc07be89748b8c97006e100b9027f306 Mon Sep 17 00:00:00 2001 From: Rodrigo Batista de Moraes <51273772+Rodrigodd@users.noreply.github.com> Date: Wed, 20 Jul 2022 14:52:36 -0300 Subject: [PATCH 44/60] Android: avoid deadlocks while handling UserEvent (#2343) Replace `Arc>` by `mpsc` --- src/platform_impl/android/mod.rs | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/src/platform_impl/android/mod.rs b/src/platform_impl/android/mod.rs index 405130fcd9..65e1a8cf73 100644 --- a/src/platform_impl/android/mod.rs +++ b/src/platform_impl/android/mod.rs @@ -2,7 +2,7 @@ use std::{ collections::VecDeque, - sync::{Arc, Mutex, RwLock}, + sync::{mpsc, RwLock}, time::{Duration, Instant}, }; @@ -235,7 +235,8 @@ fn poll(poll: Poll) -> Option { pub struct EventLoop { window_target: event_loop::EventLoopWindowTarget, - user_queue: Arc>>, + user_events_sender: mpsc::Sender, + user_events_receiver: mpsc::Receiver, first_event: Option, start_cause: event::StartCause, looper: ThreadLooper, @@ -258,6 +259,7 @@ macro_rules! call_event_handler { impl EventLoop { pub(crate) fn new(_: &PlatformSpecificEventLoopAttributes) -> Self { + let (user_events_sender, user_events_receiver) = mpsc::channel(); Self { window_target: event_loop::EventLoopWindowTarget { p: EventLoopWindowTarget { @@ -265,7 +267,8 @@ impl EventLoop { }, _marker: std::marker::PhantomData, }, - user_queue: Default::default(), + user_events_sender, + user_events_receiver, first_event: None, start_cause: event::StartCause::Init, looper: ThreadLooper::for_thread().unwrap(), @@ -493,8 +496,9 @@ impl EventLoop { } } Some(EventSource::User) => { - let mut user_queue = self.user_queue.lock().unwrap(); - while let Some(event) = user_queue.pop_front() { + // try_recv only errors when empty (expected) or disconnect. But because Self + // contains a Sender it will never disconnect, so no error handling need. + while let Ok(event) = self.user_events_receiver.try_recv() { call_event_handler!( event_handler, self.window_target(), @@ -595,20 +599,22 @@ impl EventLoop { pub fn create_proxy(&self) -> EventLoopProxy { EventLoopProxy { - queue: self.user_queue.clone(), + user_events_sender: self.user_events_sender.clone(), looper: ForeignLooper::for_thread().expect("called from event loop thread"), } } } pub struct EventLoopProxy { - queue: Arc>>, + user_events_sender: mpsc::Sender, looper: ForeignLooper, } impl EventLoopProxy { pub fn send_event(&self, event: T) -> Result<(), event_loop::EventLoopClosed> { - self.queue.lock().unwrap().push_back(event); + self.user_events_sender + .send(event) + .map_err(|mpsc::SendError(x)| event_loop::EventLoopClosed(x))?; self.looper.wake(); Ok(()) } @@ -617,7 +623,7 @@ impl EventLoopProxy { impl Clone for EventLoopProxy { fn clone(&self) -> Self { EventLoopProxy { - queue: self.queue.clone(), + user_events_sender: self.user_events_sender.clone(), looper: self.looper.clone(), } } From 653bc5981393b5c06ae732f8838774fb00a68335 Mon Sep 17 00:00:00 2001 From: Kirill Chibisov Date: Thu, 21 Jul 2022 22:22:36 +0300 Subject: [PATCH 45/60] Update raw-window-handle to v0.5.0 This updates raw-window-handle to v0.5.0. --- CHANGELOG.md | 1 + Cargo.toml | 6 +++--- src/event_loop.rs | 8 +++++++ src/lib.rs | 7 ++++--- src/platform_impl/android/mod.rs | 12 ++++++++++- src/platform_impl/ios/event_loop.rs | 6 ++++++ src/platform_impl/ios/window.rs | 16 ++++++++------ src/platform_impl/linux/mod.rs | 19 ++++++++++------- .../linux/wayland/event_loop/mod.rs | 10 +++++++++ src/platform_impl/linux/wayland/window/mod.rs | 20 ++++++++++++------ src/platform_impl/linux/x11/mod.rs | 9 ++++++++ src/platform_impl/linux/x11/window.rs | 21 ++++++++++++------- src/platform_impl/macos/event_loop.rs | 6 ++++++ src/platform_impl/macos/window.rs | 18 +++++++++++----- .../web/event_loop/window_target.rs | 15 +++++++++---- src/platform_impl/web/window.rs | 13 ++++++++---- src/platform_impl/windows/event_loop.rs | 10 +++++++-- src/platform_impl/windows/window.rs | 17 ++++++++++----- src/window.rs | 19 ++++++++++++++--- 19 files changed, 177 insertions(+), 56 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5e412377c2..0a024d7866 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -72,6 +72,7 @@ And please only add new entries to the top of this list, right below the `# Unre - **Breaking**, Replaced `VideoMode::refresh_rate` with `VideoMode::refresh_rate_millihertz` providing better precision. - On Web, add `with_prevent_default` and `with_focusable` to `WindowBuilderExtWebSys` to control whether events should be propagated. - On Windows, fix focus events being sent to inactive windows. +- **Breaking**, update `raw-window-handle` to `v0.5` and implement `HasRawDisplayHandle` for `Window` and `EventLoopWindowTarget`. # 0.26.1 (2022-01-05) diff --git a/Cargo.toml b/Cargo.toml index 7171992486..0458e18084 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -45,7 +45,7 @@ instant = { version = "0.1", features = ["wasm-bindgen"] } once_cell = "1.12" log = "0.4" serde = { version = "1", optional = true, features = ["serde_derive"] } -raw-window-handle = "0.4.2" +raw-window-handle = "0.5.0" bitflags = "1" mint = { version = "0.5.6", optional = true } @@ -55,8 +55,8 @@ simple_logger = "2.1.0" [target.'cfg(target_os = "android")'.dependencies] # Coordinate the next winit release with android-ndk-rs: https://github.com/rust-windowing/winit/issues/1995 -ndk = { git = "https://github.com/rust-windowing/android-ndk-rs", rev = "a0c5e99" } -ndk-glue = { git = "https://github.com/rust-windowing/android-ndk-rs", rev = "a0c5e99" } +ndk = { git = "https://github.com/rust-windowing/android-ndk-rs", rev = "814be08" } +ndk-glue = { git = "https://github.com/rust-windowing/android-ndk-rs", rev = "814be08" } [target.'cfg(any(target_os = "ios", target_os = "macos"))'.dependencies] objc = "0.2.7" diff --git a/src/event_loop.rs b/src/event_loop.rs index 6b5e054ef1..b96c80c9bc 100644 --- a/src/event_loop.rs +++ b/src/event_loop.rs @@ -13,6 +13,7 @@ use std::{error, fmt}; use instant::Instant; use once_cell::sync::OnceCell; +use raw_window_handle::{HasRawDisplayHandle, RawDisplayHandle}; use crate::{event::Event, monitor::MonitorHandle, platform_impl}; @@ -337,6 +338,13 @@ impl EventLoopWindowTarget { } } +unsafe impl HasRawDisplayHandle for EventLoopWindowTarget { + /// Returns a [`raw_window_handle::RawDisplayHandle`] for the event loop. + fn raw_display_handle(&self) -> RawDisplayHandle { + self.p.raw_display_handle() + } +} + /// Used to send custom events to [`EventLoop`]. pub struct EventLoopProxy { event_loop_proxy: platform_impl::EventLoopProxy, diff --git a/src/lib.rs b/src/lib.rs index 3b519f002a..4871bb3c58 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -98,9 +98,9 @@ //! # Drawing on the window //! //! Winit doesn't directly provide any methods for drawing on a [`Window`]. However it allows you to -//! retrieve the raw handle of the window (see the [`platform`] module and/or the -//! [`raw_window_handle`] method), which in turn allows you to create an -//! OpenGL/Vulkan/DirectX/Metal/etc. context that can be used to render graphics. +//! retrieve the raw handle of the window and display (see the [`platform`] module and/or the +//! [`raw_window_handle`] and [`raw_display_handle`] methods), which in turn allows +//! you to create an OpenGL/Vulkan/DirectX/Metal/etc. context that can be used to render graphics. //! //! Note that many platforms will display garbage data in the window's client area if the //! application doesn't render anything to the window by the time the desktop compositor is ready to @@ -129,6 +129,7 @@ //! [`LoopDestroyed`]: event::Event::LoopDestroyed //! [`platform`]: platform //! [`raw_window_handle`]: ./window/struct.Window.html#method.raw_window_handle +//! [`raw_display_handle`]: ./window/struct.Window.html#method.raw_display_handle #![deny(rust_2018_idioms)] #![deny(rustdoc::broken_intra_doc_links)] diff --git a/src/platform_impl/android/mod.rs b/src/platform_impl/android/mod.rs index 65e1a8cf73..9c275e6ed8 100644 --- a/src/platform_impl/android/mod.rs +++ b/src/platform_impl/android/mod.rs @@ -14,7 +14,9 @@ use ndk::{ }; use ndk_glue::{Event, LockReadGuard, Rect}; use once_cell::sync::Lazy; -use raw_window_handle::{HasRawWindowHandle, RawWindowHandle}; +use raw_window_handle::{ + AndroidDisplayHandle, HasRawWindowHandle, RawDisplayHandle, RawWindowHandle, +}; use crate::{ dpi::{PhysicalPosition, PhysicalSize, Position, Size}, @@ -645,6 +647,10 @@ impl EventLoopWindowTarget { v.push_back(MonitorHandle); v } + + pub fn raw_display_handle(&self) -> RawDisplayHandle { + RawDisplayHandle::Android(AndroidDisplayHandle::empty()) + } } #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] @@ -835,6 +841,10 @@ impl Window { } } + pub fn raw_display_handle(&self) -> RawDisplayHandle { + RawDisplayHandle::Android(AndroidDisplayHandle::empty()) + } + pub fn config(&self) -> Configuration { CONFIG.read().unwrap().clone() } diff --git a/src/platform_impl/ios/event_loop.rs b/src/platform_impl/ios/event_loop.rs index 1a32fe56b2..eeaf2e6894 100644 --- a/src/platform_impl/ios/event_loop.rs +++ b/src/platform_impl/ios/event_loop.rs @@ -7,6 +7,8 @@ use std::{ sync::mpsc::{self, Receiver, Sender}, }; +use raw_window_handle::{RawDisplayHandle, UiKitDisplayHandle}; + use crate::{ dpi::LogicalSize, event::Event, @@ -63,6 +65,10 @@ impl EventLoopWindowTarget { Some(RootMonitorHandle { inner: monitor }) } + + pub fn raw_display_handle(&self) -> RawDisplayHandle { + RawDisplayHandle::UiKit(UiKitDisplayHandle::empty()) + } } pub struct EventLoop { diff --git a/src/platform_impl/ios/window.rs b/src/platform_impl/ios/window.rs index 2fc9f3ed6f..0a4b81c243 100644 --- a/src/platform_impl/ios/window.rs +++ b/src/platform_impl/ios/window.rs @@ -1,10 +1,10 @@ -use raw_window_handle::{RawWindowHandle, UiKitHandle}; use std::{ collections::VecDeque, ops::{Deref, DerefMut}, }; use objc::runtime::{Class, Object, BOOL, NO, YES}; +use raw_window_handle::{RawDisplayHandle, RawWindowHandle, UiKitDisplayHandle, UiKitWindowHandle}; use crate::{ dpi::{self, LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize, Position, Size}, @@ -332,11 +332,15 @@ impl Inner { } pub fn raw_window_handle(&self) -> RawWindowHandle { - let mut handle = UiKitHandle::empty(); - handle.ui_window = self.window as _; - handle.ui_view = self.view as _; - handle.ui_view_controller = self.view_controller as _; - RawWindowHandle::UiKit(handle) + let mut window_handle = UiKitWindowHandle::empty(); + window_handle.ui_window = self.window as _; + window_handle.ui_view = self.view as _; + window_handle.ui_view_controller = self.view_controller as _; + RawWindowHandle::UiKit(window_handle) + } + + pub fn raw_display_handle(&self) -> RawDisplayHandle { + RawDisplayHandle::UiKit(UiKitDisplayHandle::empty()) } } diff --git a/src/platform_impl/linux/mod.rs b/src/platform_impl/linux/mod.rs index 6876c48b8a..989bfec5e3 100644 --- a/src/platform_impl/linux/mod.rs +++ b/src/platform_impl/linux/mod.rs @@ -20,7 +20,7 @@ use std::{ffi::CStr, mem::MaybeUninit, os::raw::*, sync::Arc}; use once_cell::sync::Lazy; #[cfg(feature = "x11")] use parking_lot::Mutex; -use raw_window_handle::RawWindowHandle; +use raw_window_handle::{RawDisplayHandle, RawWindowHandle}; #[cfg(feature = "x11")] pub use self::x11::XNotSupported; @@ -572,13 +572,14 @@ impl Window { } } + #[inline] pub fn raw_window_handle(&self) -> RawWindowHandle { - match self { - #[cfg(feature = "x11")] - Window::X(ref window) => RawWindowHandle::Xlib(window.raw_window_handle()), - #[cfg(feature = "wayland")] - Window::Wayland(ref window) => RawWindowHandle::Wayland(window.raw_window_handle()), - } + x11_or_wayland!(match self; Window(window) => window.raw_window_handle()) + } + + #[inline] + pub fn raw_display_handle(&self) -> RawDisplayHandle { + x11_or_wayland!(match self; Window(window) => window.raw_display_handle()) } } @@ -810,6 +811,10 @@ impl EventLoopWindowTarget { EventLoopWindowTarget::X(ref evlp) => evlp.set_device_event_filter(_filter), } } + + pub fn raw_display_handle(&self) -> raw_window_handle::RawDisplayHandle { + x11_or_wayland!(match self; Self(evlp) => evlp.raw_display_handle()) + } } fn sticky_exit_callback( diff --git a/src/platform_impl/linux/wayland/event_loop/mod.rs b/src/platform_impl/linux/wayland/event_loop/mod.rs index 8fe13bdee0..0f8f8a1718 100644 --- a/src/platform_impl/linux/wayland/event_loop/mod.rs +++ b/src/platform_impl/linux/wayland/event_loop/mod.rs @@ -6,6 +6,8 @@ use std::process; use std::rc::Rc; use std::time::{Duration, Instant}; +use raw_window_handle::{RawDisplayHandle, WaylandDisplayHandle}; + use sctk::reexports::client::protocol::wl_compositor::WlCompositor; use sctk::reexports::client::protocol::wl_shm::WlShm; use sctk::reexports::client::Display; @@ -71,6 +73,14 @@ pub struct EventLoopWindowTarget { _marker: std::marker::PhantomData, } +impl EventLoopWindowTarget { + pub fn raw_display_handle(&self) -> RawDisplayHandle { + let mut display_handle = WaylandDisplayHandle::empty(); + display_handle.display = self.display.get_display_ptr() as *mut _; + RawDisplayHandle::Wayland(display_handle) + } +} + pub struct EventLoop { /// Event loop. event_loop: calloop::EventLoop<'static, WinitState>, diff --git a/src/platform_impl/linux/wayland/window/mod.rs b/src/platform_impl/linux/wayland/window/mod.rs index c061352872..a471cc138d 100644 --- a/src/platform_impl/linux/wayland/window/mod.rs +++ b/src/platform_impl/linux/wayland/window/mod.rs @@ -7,7 +7,9 @@ use sctk::reexports::client::Display; use sctk::reexports::calloop; -use raw_window_handle::WaylandHandle; +use raw_window_handle::{ + RawDisplayHandle, RawWindowHandle, WaylandDisplayHandle, WaylandWindowHandle, +}; use sctk::window::Decorations; use crate::dpi::{LogicalSize, PhysicalPosition, PhysicalSize, Position, Size}; @@ -579,11 +581,17 @@ impl Window { } #[inline] - pub fn raw_window_handle(&self) -> WaylandHandle { - let mut handle = WaylandHandle::empty(); - handle.display = self.display.get_display_ptr() as *mut _; - handle.surface = self.surface.as_ref().c_ptr() as *mut _; - handle + pub fn raw_window_handle(&self) -> RawWindowHandle { + let mut window_handle = WaylandWindowHandle::empty(); + window_handle.surface = self.surface.as_ref().c_ptr() as *mut _; + RawWindowHandle::Wayland(window_handle) + } + + #[inline] + pub fn raw_display_handle(&self) -> RawDisplayHandle { + let mut display_handle = WaylandDisplayHandle::empty(); + display_handle.display = self.display.get_display_ptr() as *mut _; + RawDisplayHandle::Wayland(display_handle) } #[inline] diff --git a/src/platform_impl/linux/x11/mod.rs b/src/platform_impl/linux/x11/mod.rs index ff6f012338..2fe262cc12 100644 --- a/src/platform_impl/linux/x11/mod.rs +++ b/src/platform_impl/linux/x11/mod.rs @@ -40,6 +40,7 @@ use std::{ use libc::{self, setlocale, LC_CTYPE}; use mio::{unix::SourceFd, Events, Interest, Poll, Token, Waker}; +use raw_window_handle::{RawDisplayHandle, XlibDisplayHandle}; use self::{ dnd::{Dnd, DndState}, @@ -558,6 +559,14 @@ impl EventLoopWindowTarget { .select_xinput_events(self.root, ffi::XIAllMasterDevices, mask) .queue(); } + + pub fn raw_display_handle(&self) -> raw_window_handle::RawDisplayHandle { + let mut display_handle = XlibDisplayHandle::empty(); + display_handle.display = self.xconn.display as *mut _; + display_handle.screen = + unsafe { (self.xconn.xlib.XDefaultScreen)(self.xconn.display as *mut _) }; + RawDisplayHandle::Xlib(display_handle) + } } impl EventLoopProxy { diff --git a/src/platform_impl/linux/x11/window.rs b/src/platform_impl/linux/x11/window.rs index fa4bd1440b..eb3326475b 100644 --- a/src/platform_impl/linux/x11/window.rs +++ b/src/platform_impl/linux/x11/window.rs @@ -1,4 +1,3 @@ -use raw_window_handle::XlibHandle; use std::{ cmp, env, ffi::CString, @@ -8,10 +7,11 @@ use std::{ ptr, slice, sync::Arc, }; -use x11_dl::xlib::TrueColor; use libc; use parking_lot::Mutex; +use raw_window_handle::{RawDisplayHandle, RawWindowHandle, XlibDisplayHandle, XlibWindowHandle}; +use x11_dl::xlib::TrueColor; use crate::{ dpi::{PhysicalPosition, PhysicalSize, Position, Size}, @@ -1509,10 +1509,17 @@ impl UnownedWindow { } #[inline] - pub fn raw_window_handle(&self) -> XlibHandle { - let mut handle = XlibHandle::empty(); - handle.window = self.xlib_window(); - handle.display = self.xlib_display(); - handle + pub fn raw_window_handle(&self) -> RawWindowHandle { + let mut window_handle = XlibWindowHandle::empty(); + window_handle.window = self.xlib_window(); + RawWindowHandle::Xlib(window_handle) + } + + #[inline] + pub fn raw_display_handle(&self) -> RawDisplayHandle { + let mut display_handle = XlibDisplayHandle::empty(); + display_handle.display = self.xlib_display(); + display_handle.screen = self.screen_id; + RawDisplayHandle::Xlib(display_handle) } } diff --git a/src/platform_impl/macos/event_loop.rs b/src/platform_impl/macos/event_loop.rs index 4a8b058cab..ea0315139c 100644 --- a/src/platform_impl/macos/event_loop.rs +++ b/src/platform_impl/macos/event_loop.rs @@ -17,6 +17,7 @@ use cocoa::{ foundation::{NSInteger, NSPoint, NSTimeInterval}, }; use objc::rc::autoreleasepool; +use raw_window_handle::{AppKitDisplayHandle, RawDisplayHandle}; use crate::{ event::Event, @@ -87,6 +88,11 @@ impl EventLoopWindowTarget { let monitor = monitor::primary_monitor(); Some(RootMonitorHandle { inner: monitor }) } + + #[inline] + pub fn raw_display_handle(&self) -> RawDisplayHandle { + RawDisplayHandle::AppKit(AppKitDisplayHandle::empty()) + } } impl EventLoopWindowTarget { diff --git a/src/platform_impl/macos/window.rs b/src/platform_impl/macos/window.rs index 11d43f5a87..f804a275f2 100644 --- a/src/platform_impl/macos/window.rs +++ b/src/platform_impl/macos/window.rs @@ -1,4 +1,3 @@ -use raw_window_handle::{AppKitHandle, RawWindowHandle}; use std::{ collections::VecDeque, convert::TryInto, @@ -10,6 +9,10 @@ use std::{ }, }; +use raw_window_handle::{ + AppKitDisplayHandle, AppKitWindowHandle, RawDisplayHandle, RawWindowHandle, +}; + use crate::{ dpi::{ LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize, Position, Size, Size::Logical, @@ -1144,10 +1147,15 @@ impl UnownedWindow { #[inline] pub fn raw_window_handle(&self) -> RawWindowHandle { - let mut handle = AppKitHandle::empty(); - handle.ns_window = *self.ns_window as *mut _; - handle.ns_view = *self.ns_view as *mut _; - RawWindowHandle::AppKit(handle) + let mut window_handle = AppKitWindowHandle::empty(); + window_handle.ns_window = *self.ns_window as *mut _; + window_handle.ns_view = *self.ns_view as *mut _; + RawWindowHandle::AppKit(window_handle) + } + + #[inline] + pub fn raw_display_handle(&self) -> RawDisplayHandle { + RawDisplayHandle::AppKit(AppKitDisplayHandle::empty()) } } diff --git a/src/platform_impl/web/event_loop/window_target.rs b/src/platform_impl/web/event_loop/window_target.rs index b936762945..6b15e3dd22 100644 --- a/src/platform_impl/web/event_loop/window_target.rs +++ b/src/platform_impl/web/event_loop/window_target.rs @@ -1,3 +1,10 @@ +use std::cell::RefCell; +use std::clone::Clone; +use std::collections::{vec_deque::IntoIter as VecDequeIter, VecDeque}; +use std::rc::Rc; + +use raw_window_handle::{RawDisplayHandle, WebDisplayHandle}; + use super::{ super::monitor::MonitorHandle, backend, device::DeviceId, proxy::EventLoopProxy, runner, window::WindowId, @@ -10,10 +17,6 @@ use crate::event::{ use crate::event_loop::ControlFlow; use crate::monitor::MonitorHandle as RootMH; use crate::window::{Theme, WindowId as RootWindowId}; -use std::cell::RefCell; -use std::clone::Clone; -use std::collections::{vec_deque::IntoIter as VecDequeIter, VecDeque}; -use std::rc::Rc; pub struct EventLoopWindowTarget { pub(crate) runner: runner::Shared, @@ -279,4 +282,8 @@ impl EventLoopWindowTarget { inner: MonitorHandle, }) } + + pub fn raw_display_handle(&self) -> RawDisplayHandle { + RawDisplayHandle::Web(WebDisplayHandle::empty()) + } } diff --git a/src/platform_impl/web/window.rs b/src/platform_impl/web/window.rs index fe1d4b229a..492baefc16 100644 --- a/src/platform_impl/web/window.rs +++ b/src/platform_impl/web/window.rs @@ -7,7 +7,7 @@ use crate::window::{ CursorGrabMode, CursorIcon, Fullscreen, UserAttentionType, WindowAttributes, WindowId as RootWI, }; -use raw_window_handle::{RawWindowHandle, WebHandle}; +use raw_window_handle::{RawDisplayHandle, RawWindowHandle, WebDisplayHandle, WebWindowHandle}; use super::{backend, monitor::MonitorHandle, EventLoopWindowTarget}; @@ -359,9 +359,14 @@ impl Window { #[inline] pub fn raw_window_handle(&self) -> RawWindowHandle { - let mut handle = WebHandle::empty(); - handle.id = self.id.0; - RawWindowHandle::Web(handle) + let mut window_handle = WebWindowHandle::empty(); + window_handle.id = self.id.0; + RawWindowHandle::Web(window_handle) + } + + #[inline] + pub fn raw_display_handle(&self) -> RawDisplayHandle { + RawDisplayHandle::Web(WebDisplayHandle::empty()) } } diff --git a/src/platform_impl/windows/event_loop.rs b/src/platform_impl/windows/event_loop.rs index 1872f422e8..a89ab134f9 100644 --- a/src/platform_impl/windows/event_loop.rs +++ b/src/platform_impl/windows/event_loop.rs @@ -2,8 +2,6 @@ mod runner; -use once_cell::sync::Lazy; -use parking_lot::Mutex; use std::{ cell::Cell, collections::VecDeque, @@ -19,6 +17,10 @@ use std::{ time::{Duration, Instant}, }; +use once_cell::sync::Lazy; +use parking_lot::Mutex; +use raw_window_handle::{RawDisplayHandle, WindowsDisplayHandle}; + use windows_sys::Win32::{ Devices::HumanInterfaceDevice::MOUSE_MOVE_RELATIVE, Foundation::{BOOL, HANDLE, HWND, LPARAM, LRESULT, POINT, RECT, WAIT_TIMEOUT, WPARAM}, @@ -314,6 +316,10 @@ impl EventLoopWindowTarget { let monitor = monitor::primary_monitor(); Some(RootMonitorHandle { inner: monitor }) } + + pub fn raw_display_handle(&self) -> RawDisplayHandle { + RawDisplayHandle::Windows(WindowsDisplayHandle::empty()) + } } /// Returns the id of the main thread. diff --git a/src/platform_impl/windows/window.rs b/src/platform_impl/windows/window.rs index bc55a2a4b1..1041c8474b 100644 --- a/src/platform_impl/windows/window.rs +++ b/src/platform_impl/windows/window.rs @@ -1,7 +1,9 @@ #![cfg(target_os = "windows")] use parking_lot::Mutex; -use raw_window_handle::{RawWindowHandle, Win32Handle}; +use raw_window_handle::{ + RawDisplayHandle, RawWindowHandle, Win32WindowHandle, WindowsDisplayHandle, +}; use std::{ cell::Cell, ffi::c_void, @@ -260,10 +262,15 @@ impl Window { #[inline] pub fn raw_window_handle(&self) -> RawWindowHandle { - let mut handle = Win32Handle::empty(); - handle.hwnd = self.window.0 as *mut _; - handle.hinstance = self.hinstance() as *mut _; - RawWindowHandle::Win32(handle) + let mut window_handle = Win32WindowHandle::empty(); + window_handle.hwnd = self.window.0 as *mut _; + window_handle.hinstance = self.hinstance() as *mut _; + RawWindowHandle::Win32(window_handle) + } + + #[inline] + pub fn raw_display_handle(&self) -> RawDisplayHandle { + RawDisplayHandle::Windows(WindowsDisplayHandle::empty()) } #[inline] diff --git a/src/window.rs b/src/window.rs index 0dc31e226f..4e1cb18b7e 100644 --- a/src/window.rs +++ b/src/window.rs @@ -1,6 +1,10 @@ //! The [`Window`] struct and associated types. use std::fmt; +use raw_window_handle::{ + HasRawDisplayHandle, HasRawWindowHandle, RawDisplayHandle, RawWindowHandle, +}; + use crate::{ dpi::{PhysicalPosition, PhysicalSize, Position, Size}, error::{ExternalError, NotSupportedError, OsError}, @@ -1032,8 +1036,7 @@ impl Window { self.window.primary_monitor() } } - -unsafe impl raw_window_handle::HasRawWindowHandle for Window { +unsafe impl HasRawWindowHandle for Window { /// Returns a [`raw_window_handle::RawWindowHandle`] for the Window /// /// ## Platform-specific @@ -1049,11 +1052,21 @@ unsafe impl raw_window_handle::HasRawWindowHandle for Window { /// /// [`Event::Resumed`]: crate::event::Event::Resumed /// [`Event::Suspended`]: crate::event::Event::Suspended - fn raw_window_handle(&self) -> raw_window_handle::RawWindowHandle { + fn raw_window_handle(&self) -> RawWindowHandle { self.window.raw_window_handle() } } +unsafe impl HasRawDisplayHandle for Window { + /// Returns a [`raw_window_handle::RawDisplayHandle`] used by the [`EventLoop`] that + /// created a window. + /// + /// [`EventLoop`]: crate::event_loop::EventLoop + fn raw_display_handle(&self) -> RawDisplayHandle { + self.window.raw_display_handle() + } +} + /// The behavior of cursor grabbing. /// /// Use this enum with [`Window::set_cursor_grab`] to grab the cursor. From f10ef5f33119712d9b3bf9a81ea67793b3aebd84 Mon Sep 17 00:00:00 2001 From: Kirill Chibisov Date: Thu, 21 Jul 2022 22:23:22 +0300 Subject: [PATCH 46/60] On macOS, fix confirmed character inserted When confirming input in e.g. Korean IME or using characters like `+` winit was sending those twice, once via `Ime::Commit` and the other one via `ReceivedCharacter`, since those events weren't generating any `Ime::Preedit` and were forwarded due to `do_command_by_selector`. --- src/platform_impl/macos/view.rs | 58 +++++++++++++++++++++++++-------- 1 file changed, 44 insertions(+), 14 deletions(-) diff --git a/src/platform_impl/macos/view.rs b/src/platform_impl/macos/view.rs index 9fe6b0dd32..9f11481db1 100644 --- a/src/platform_impl/macos/view.rs +++ b/src/platform_impl/macos/view.rs @@ -54,11 +54,19 @@ impl Default for CursorState { } } -#[derive(Eq, PartialEq)] +#[derive(Debug, Eq, PartialEq)] enum ImeState { + /// The IME events are disabled, so only `ReceivedCharacter` is being sent to the user. Disabled, + + /// The IME events are enabled. Enabled, + + /// The IME is in preedit. Preedit, + + /// The text was just commited, so the next input from the keyboard must be ignored. + Commited, } pub(super) struct ViewState { @@ -527,17 +535,24 @@ extern "C" fn set_marked_text( })); } - let cursor_start = preedit_string.len(); - let cursor_end = preedit_string.len(); - state.ime_state = ImeState::Preedit; + // Don't update state to preedit when we've just commited a string, since the following + // preedit string will be None anyway. + if state.ime_state != ImeState::Commited { + state.ime_state = ImeState::Preedit; + } + + // Empty string basically means that there's no preedit, so indicate that by sending + // `None` cursor range. + let cursor_range = if preedit_string.is_empty() { + None + } else { + Some((preedit_string.len(), preedit_string.len())) + }; // Send WindowEvent for updating marked text AppState::queue_event(EventWrapper::StaticEvent(Event::WindowEvent { window_id: WindowId(get_window_id(state.ns_window)), - event: WindowEvent::Ime(Ime::Preedit( - preedit_string, - Some((cursor_start, cursor_end)), - )), + event: WindowEvent::Ime(Ime::Preedit(preedit_string, cursor_range)), })); } } @@ -557,7 +572,7 @@ extern "C" fn unmark_text(this: &Object, _sel: Sel) { let state = &mut *(state_ptr as *mut ViewState); AppState::queue_event(EventWrapper::StaticEvent(Event::WindowEvent { window_id: WindowId(get_window_id(state.ns_window)), - event: WindowEvent::Ime(Ime::Preedit(String::new(), Some((0, 0)))), + event: WindowEvent::Ime(Ime::Preedit(String::new(), None)), })); if state.is_ime_enabled() { // Leave the Preedit state @@ -630,19 +645,26 @@ extern "C" fn insert_text(this: &Object, _sel: Sel, string: id, _replacement_ran window_id: WindowId(get_window_id(state.ns_window)), event: WindowEvent::Ime(Ime::Commit(string)), })); - state.ime_state = ImeState::Enabled; + state.ime_state = ImeState::Commited; } } } extern "C" fn do_command_by_selector(this: &Object, _sel: Sel, _command: Sel) { trace_scope!("doCommandBySelector:"); - // Basically, we're sent this message whenever a keyboard event that doesn't generate a "human readable" character - // happens, i.e. newlines, tabs, and Ctrl+C. + // Basically, we're sent this message whenever a keyboard event that doesn't generate a "human + // readable" character happens, i.e. newlines, tabs, and Ctrl+C. unsafe { let state_ptr: *mut c_void = *this.get_ivar("winitState"); let state = &mut *(state_ptr as *mut ViewState); + // We shouldn't forward any character from just commited text, since we'll end up sending + // it twice with some IMEs like Korean one. We'll also always send `Enter` in that case, + // which is not desired given it was used to confirm IME input. + if state.ime_state == ImeState::Commited { + return; + } + state.forward_key_to_app = true; let has_marked_text: BOOL = msg_send![this, hasMarkedText]; @@ -748,6 +770,7 @@ extern "C" fn key_down(this: &Object, _sel: Sel, event: id) { // we must send the `KeyboardInput` event during IME if it triggered // `doCommandBySelector`. (doCommandBySelector means that the keyboard input // is not handled by IME and should be handled by the application) + let mut text_commited = false; if state.ime_allowed { let events_for_nsview: id = msg_send![class!(NSArray), arrayWithObject: event]; let _: () = msg_send![this, interpretKeyEvents: events_for_nsview]; @@ -758,6 +781,12 @@ extern "C" fn key_down(this: &Object, _sel: Sel, event: id) { // some of the reads (eg `state.ime_state`) that happen after this // point are not needed. compiler_fence(Ordering::SeqCst); + + // If the text was commited we must treat the next keyboard event as IME related. + if state.ime_state == ImeState::Commited { + state.ime_state = ImeState::Enabled; + text_commited = true; + } } let now_in_preedit = state.ime_state == ImeState::Preedit; @@ -767,8 +796,9 @@ extern "C" fn key_down(this: &Object, _sel: Sel, event: id) { update_potentially_stale_modifiers(state, event); - let preedit_related = was_in_preedit || now_in_preedit; - if !preedit_related || state.forward_key_to_app || !state.ime_allowed { + let ime_related = was_in_preedit || now_in_preedit || text_commited; + + if !ime_related || state.forward_key_to_app || !state.ime_allowed { #[allow(deprecated)] let window_event = Event::WindowEvent { window_id, From 1ec976f95eadd3369cbca466e4a8e8243f8fafdc Mon Sep 17 00:00:00 2001 From: Kirill Chibisov Date: Fri, 22 Jul 2022 20:21:28 +0300 Subject: [PATCH 47/60] Add method to hook xlib error handler This should help glutin to handle errors coming from GLX and offer multithreading support in a safe way. Fixes #2378. --- CHANGELOG.md | 1 + src/platform/unix.rs | 25 ++++++++++++++++++++++++- src/platform_impl/linux/mod.rs | 17 ++++++++++++++++- 3 files changed, 41 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0a024d7866..d13fccb555 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -73,6 +73,7 @@ And please only add new entries to the top of this list, right below the `# Unre - On Web, add `with_prevent_default` and `with_focusable` to `WindowBuilderExtWebSys` to control whether events should be propagated. - On Windows, fix focus events being sent to inactive windows. - **Breaking**, update `raw-window-handle` to `v0.5` and implement `HasRawDisplayHandle` for `Window` and `EventLoopWindowTarget`. +- On X11, add function `register_xlib_error_hook` into `winit::platform::unix` to subscribe for errors comming from Xlib. # 0.26.1 (2022-01-05) diff --git a/src/platform/unix.rs b/src/platform/unix.rs index ae0c994252..591f038f25 100644 --- a/src/platform/unix.rs +++ b/src/platform/unix.rs @@ -19,7 +19,7 @@ use crate::{ #[cfg(feature = "x11")] use crate::dpi::Size; #[cfg(feature = "x11")] -use crate::platform_impl::x11::{ffi::XVisualInfo, XConnection}; +use crate::platform_impl::{x11::ffi::XVisualInfo, x11::XConnection, XLIB_ERROR_HOOKS}; use crate::platform_impl::{ ApplicationName, Backend, EventLoopWindowTarget as LinuxEventLoopWindowTarget, Window as LinuxWindow, @@ -35,6 +35,29 @@ pub use crate::platform_impl::{x11::util::WindowType as XWindowType, XNotSupport #[cfg(feature = "wayland")] pub use crate::window::Theme; +/// The first argument in the provided hook will be the pointer to XDisplay +/// and the second one the pointer to XError. The return `bool` is an indicator +/// whether error was handled by the callback. +#[cfg(feature = "x11")] +pub type XlibErrorHook = + Box bool + Send + Sync>; + +/// Hook to winit's xlib error handling callback. +/// +/// This method is provided as a safe way to handle the errors comming from X11 when using xlib +/// in external crates, like glutin for GLX access. Trying to handle errors by speculating with +/// `XSetErrorHandler` is [`unsafe`]. +/// +/// [`unsafe`]: https://www.remlab.net/op/xlib.shtml +#[inline] +#[cfg(feature = "x11")] +pub fn register_xlib_error_hook(hook: XlibErrorHook) { + // Append new hook. + unsafe { + XLIB_ERROR_HOOKS.lock().push(hook); + } +} + /// Additional methods on [`EventLoopWindowTarget`] that are specific to Unix. pub trait EventLoopWindowTargetExtUnix { /// True if the [`EventLoopWindowTarget`] uses Wayland. diff --git a/src/platform_impl/linux/mod.rs b/src/platform_impl/linux/mod.rs index 989bfec5e3..d73eb725ff 100644 --- a/src/platform_impl/linux/mod.rs +++ b/src/platform_impl/linux/mod.rs @@ -26,6 +26,8 @@ use raw_window_handle::{RawDisplayHandle, RawWindowHandle}; pub use self::x11::XNotSupported; #[cfg(feature = "x11")] use self::x11::{ffi::XVisualInfo, util::WindowType as XWindowType, XConnection, XError}; +#[cfg(feature = "x11")] +use crate::platform::unix::XlibErrorHook; #[cfg(feature = "wayland")] use crate::window::Theme; use crate::{ @@ -583,6 +585,10 @@ impl Window { } } +/// Hooks for X11 errors. +#[cfg(feature = "x11")] +pub(crate) static mut XLIB_ERROR_HOOKS: Mutex> = Mutex::new(Vec::new()); + #[cfg(feature = "x11")] unsafe extern "C" fn x_error_callback( display: *mut x11::ffi::Display, @@ -590,6 +596,12 @@ unsafe extern "C" fn x_error_callback( ) -> c_int { let xconn_lock = X11_BACKEND.lock(); if let Ok(ref xconn) = *xconn_lock { + // Call all the hooks. + let mut error_handled = false; + for hook in XLIB_ERROR_HOOKS.lock().iter() { + error_handled |= hook(display as *mut _, event as *mut _); + } + // `assume_init` is safe here because the array consists of `MaybeUninit` values, // which do not require initialization. let mut buf: [MaybeUninit; 1024] = MaybeUninit::uninit().assume_init(); @@ -608,7 +620,10 @@ unsafe extern "C" fn x_error_callback( minor_code: (*event).minor_code, }; - error!("X11 error: {:#?}", error); + // Don't log error. + if !error_handled { + error!("X11 error: {:#?}", error); + } *xconn.latest_error.lock() = Some(error); } From 1cd0e94c26d6ec09b084267298fec7be5e0a3e25 Mon Sep 17 00:00:00 2001 From: Amr Bashir Date: Fri, 22 Jul 2022 19:33:22 +0200 Subject: [PATCH 48/60] Windows: apply skip taskbar state when taskbar is restarted (#2380) --- src/platform_impl/windows/event_loop.rs | 10 ++++ src/platform_impl/windows/window.rs | 69 ++++++++++++----------- src/platform_impl/windows/window_state.rs | 4 ++ 3 files changed, 50 insertions(+), 33 deletions(-) diff --git a/src/platform_impl/windows/event_loop.rs b/src/platform_impl/windows/event_loop.rs index a89ab134f9..632fc854f9 100644 --- a/src/platform_impl/windows/event_loop.rs +++ b/src/platform_impl/windows/event_loop.rs @@ -95,6 +95,8 @@ use crate::{ }; use runner::{EventLoopRunner, EventLoopRunnerShared}; +use super::window::set_skip_taskbar; + type GetPointerFrameInfoHistory = unsafe extern "system" fn( pointerId: u32, entriesCount: *mut u32, @@ -616,6 +618,10 @@ pub static SET_RETAIN_STATE_ON_SIZE_MSG_ID: Lazy = Lazy::new(|| unsafe { RegisterWindowMessageA("Winit::SetRetainMaximized\0".as_ptr()) }); static THREAD_EVENT_TARGET_WINDOW_CLASS: Lazy> = Lazy::new(|| util::encode_wide("Winit Thread Event Target")); +/// When the taskbar is created, it registers a message with the "TaskbarCreated" string and then broadcasts this message to all top-level windows +/// +pub static TASKBAR_CREATED: Lazy = + Lazy::new(|| unsafe { RegisterWindowMessageA("TaskbarCreated\0".as_ptr()) }); fn create_event_target_window() -> HWND { unsafe { @@ -2181,6 +2187,10 @@ unsafe fn public_window_callback_inner( f.set(WindowFlags::MARKER_RETAIN_STATE_ON_SIZE, wparam != 0) }); 0 + } else if msg == *TASKBAR_CREATED { + let window_state = userdata.window_state.lock(); + set_skip_taskbar(window, window_state.skip_taskbar); + DefWindowProcW(window, msg, wparam, lparam) } else { DefWindowProcW(window, msg, wparam, lparam) } diff --git a/src/platform_impl/windows/window.rs b/src/platform_impl/windows/window.rs index 1041c8474b..73680cdad9 100644 --- a/src/platform_impl/windows/window.rs +++ b/src/platform_impl/windows/window.rs @@ -687,39 +687,8 @@ impl Window { #[inline] pub fn set_skip_taskbar(&self, skip: bool) { - com_initialized(); - unsafe { - TASKBAR_LIST.with(|task_bar_list_ptr| { - let mut task_bar_list = task_bar_list_ptr.get(); - - if task_bar_list.is_null() { - let hr = CoCreateInstance( - &CLSID_TaskbarList, - ptr::null_mut(), - CLSCTX_ALL, - &IID_ITaskbarList, - &mut task_bar_list as *mut _ as *mut _, - ); - - let hr_init = (*(*task_bar_list).lpVtbl).HrInit; - - if hr != S_OK || hr_init(task_bar_list.cast()) != S_OK { - // In some old windows, the taskbar object could not be created, we just ignore it - return; - } - task_bar_list_ptr.set(task_bar_list) - } - - task_bar_list = task_bar_list_ptr.get(); - if skip { - let delete_tab = (*(*task_bar_list).lpVtbl).DeleteTab; - delete_tab(task_bar_list, self.window.0); - } else { - let add_tab = (*(*task_bar_list).lpVtbl).AddTab; - add_tab(task_bar_list, self.window.0); - } - }); - } + self.window_state.lock().skip_taskbar = skip; + unsafe { set_skip_taskbar(self.hwnd(), skip) }; } #[inline] @@ -1106,6 +1075,40 @@ unsafe fn taskbar_mark_fullscreen(handle: HWND, fullscreen: bool) { }) } +pub(crate) unsafe fn set_skip_taskbar(hwnd: HWND, skip: bool) { + com_initialized(); + TASKBAR_LIST.with(|task_bar_list_ptr| { + let mut task_bar_list = task_bar_list_ptr.get(); + + if task_bar_list.is_null() { + let hr = CoCreateInstance( + &CLSID_TaskbarList, + ptr::null_mut(), + CLSCTX_ALL, + &IID_ITaskbarList, + &mut task_bar_list as *mut _ as *mut _, + ); + + let hr_init = (*(*task_bar_list).lpVtbl).HrInit; + + if hr != S_OK || hr_init(task_bar_list.cast()) != S_OK { + // In some old windows, the taskbar object could not be created, we just ignore it + return; + } + task_bar_list_ptr.set(task_bar_list) + } + + task_bar_list = task_bar_list_ptr.get(); + if skip { + let delete_tab = (*(*task_bar_list).lpVtbl).DeleteTab; + delete_tab(task_bar_list, hwnd); + } else { + let add_tab = (*(*task_bar_list).lpVtbl).AddTab; + add_tab(task_bar_list, hwnd); + } + }); +} + unsafe fn force_window_active(handle: HWND) { // In some situation, calling SetForegroundWindow could not bring up the window, // This is a little hack which can "steal" the foreground window permission diff --git a/src/platform_impl/windows/window_state.rs b/src/platform_impl/windows/window_state.rs index 35a8f0d51e..242d701da5 100644 --- a/src/platform_impl/windows/window_state.rs +++ b/src/platform_impl/windows/window_state.rs @@ -49,6 +49,8 @@ pub struct WindowState { // Used by WM_NCACTIVATE, WM_SETFOCUS and WM_KILLFOCUS pub is_active: bool, pub is_focused: bool, + + pub skip_taskbar: bool, } #[derive(Clone)] @@ -152,6 +154,8 @@ impl WindowState { is_active: false, is_focused: false, + + skip_taskbar: false, } } From 08d025968e2ff403081d7af5ce931f6b62968370 Mon Sep 17 00:00:00 2001 From: Amr Bashir Date: Sat, 23 Jul 2022 14:23:58 +0200 Subject: [PATCH 49/60] Fix hiding a maximized window On Windows (#2336) --- CHANGELOG.md | 1 + src/platform_impl/windows/window_state.rs | 22 ++++++++++------------ 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d13fccb555..99d1d70d03 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ And please only add new entries to the top of this list, right below the `# Unre # Unreleased +- On Windows, fix hiding a maximized window. - On Android, `ndk-glue`'s `NativeWindow` lock is now held between `Event::Resumed` and `Event::Suspended`. - On Web, added `EventLoopExtWebSys` with a `spawn` method to start the event loop without throwing an exception. - Added `WindowEvent::Occluded(bool)`, currently implemented on macOS and X11. diff --git a/src/platform_impl/windows/window_state.rs b/src/platform_impl/windows/window_state.rs index 242d701da5..21841f07c0 100644 --- a/src/platform_impl/windows/window_state.rs +++ b/src/platform_impl/windows/window_state.rs @@ -106,7 +106,6 @@ bitflags! { const EXCLUSIVE_FULLSCREEN_OR_MASK = WindowFlags::ALWAYS_ON_TOP.bits; const NO_DECORATIONS_AND_MASK = !WindowFlags::RESIZABLE.bits; - const INVISIBLE_AND_MASK = !WindowFlags::MAXIMIZED.bits; } } @@ -229,9 +228,6 @@ impl WindowFlags { if self.contains(WindowFlags::MARKER_EXCLUSIVE_FULLSCREEN) { self |= WindowFlags::EXCLUSIVE_FULLSCREEN_OR_MASK; } - if !self.contains(WindowFlags::VISIBLE) { - self &= WindowFlags::INVISIBLE_AND_MASK; - } if !self.contains(WindowFlags::DECORATIONS) { self &= WindowFlags::NO_DECORATIONS_AND_MASK; } @@ -294,21 +290,17 @@ impl WindowFlags { new = new.mask(); let diff = self ^ new; + if diff == WindowFlags::empty() { return; } - if diff.contains(WindowFlags::VISIBLE) { + if new.contains(WindowFlags::VISIBLE) { unsafe { - ShowWindow( - window, - match new.contains(WindowFlags::VISIBLE) { - true => SW_SHOW, - false => SW_HIDE, - }, - ); + ShowWindow(window, SW_SHOW); } } + if diff.contains(WindowFlags::ALWAYS_ON_TOP) { unsafe { SetWindowPos( @@ -352,6 +344,12 @@ impl WindowFlags { } } + if !new.contains(WindowFlags::VISIBLE) { + unsafe { + ShowWindow(window, SW_HIDE); + } + } + if diff != WindowFlags::empty() { let (style, style_ex) = new.to_window_styles(); From 5a0bad130dbc389d1f8fefc34be593c518c3ca98 Mon Sep 17 00:00:00 2001 From: Marijn Suijten Date: Mon, 25 Jul 2022 15:20:31 +0200 Subject: [PATCH 50/60] Bump `ndk` and `ndk-glue` dependencies to stable `0.7.0` release (#2392) --- CHANGELOG.md | 1 + Cargo.toml | 4 ++-- README.md | 14 +++++++------- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 99d1d70d03..e46453b193 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -75,6 +75,7 @@ And please only add new entries to the top of this list, right below the `# Unre - On Windows, fix focus events being sent to inactive windows. - **Breaking**, update `raw-window-handle` to `v0.5` and implement `HasRawDisplayHandle` for `Window` and `EventLoopWindowTarget`. - On X11, add function `register_xlib_error_hook` into `winit::platform::unix` to subscribe for errors comming from Xlib. +- On Android, upgrade `ndk` and `ndk-glue` dependencies to the recently released `0.7.0`. # 0.26.1 (2022-01-05) diff --git a/Cargo.toml b/Cargo.toml index 0458e18084..205e6f4c0b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -55,8 +55,8 @@ simple_logger = "2.1.0" [target.'cfg(target_os = "android")'.dependencies] # Coordinate the next winit release with android-ndk-rs: https://github.com/rust-windowing/winit/issues/1995 -ndk = { git = "https://github.com/rust-windowing/android-ndk-rs", rev = "814be08" } -ndk-glue = { git = "https://github.com/rust-windowing/android-ndk-rs", rev = "814be08" } +ndk = "0.7.0" +ndk-glue = "0.7.0" [target.'cfg(any(target_os = "ios", target_os = "macos"))'.dependencies] objc = "0.2.7" diff --git a/README.md b/README.md index 0caad194c7..2e3880e7e1 100644 --- a/README.md +++ b/README.md @@ -101,16 +101,16 @@ book]. This library makes use of the [ndk-rs](https://github.com/rust-windowing/android-ndk-rs) crates, refer to that repo for more documentation. -The `ndk_glue` version needs to match the version used by `winit`. Otherwise, the application will not start correctly as `ndk_glue`'s internal NativeActivity static is not the same due to version mismatch. +The `ndk-glue` version needs to match the version used by `winit`. Otherwise, the application will not start correctly as `ndk-glue`'s internal `NativeActivity` static is not the same due to version mismatch. -`ndk_glue` <-> `winit` version comparison compatibility: +`winit` compatibility table with `ndk-glue`: -| winit | ndk_glue | +| winit | ndk-glue | | :---: | :------------------: | -| 0.24 | `ndk_glue = "0.2.0"` | -| 0.25 | `ndk_glue = "0.3.0"` | -| 0.26 | `ndk_glue = "0.5.0"` | -| 0.27 | `ndk_glue = "0.6.0"` | +| 0.24 | `ndk-glue = "0.2.0"` | +| 0.25 | `ndk-glue = "0.3.0"` | +| 0.26 | `ndk-glue = "0.5.0"` | +| 0.27 | `ndk-glue = "0.7.0"` | Running on an Android device needs a dynamic system library, add this to Cargo.toml: From 4fd52af68281eb94c75b631f3adf6ffb50c0dab4 Mon Sep 17 00:00:00 2001 From: Kirill Chibisov Date: Tue, 26 Jul 2022 16:02:09 +0300 Subject: [PATCH 51/60] Fix type hint reference for xlib hook --- src/platform/unix.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/platform/unix.rs b/src/platform/unix.rs index 591f038f25..60a40a0f38 100644 --- a/src/platform/unix.rs +++ b/src/platform/unix.rs @@ -35,9 +35,11 @@ pub use crate::platform_impl::{x11::util::WindowType as XWindowType, XNotSupport #[cfg(feature = "wayland")] pub use crate::window::Theme; -/// The first argument in the provided hook will be the pointer to XDisplay -/// and the second one the pointer to XError. The return `bool` is an indicator -/// whether error was handled by the callback. +/// The first argument in the provided hook will be the pointer to `XDisplay` +/// and the second one the pointer to [`XErrorEvent`]. The returned `bool` is an +/// indicator whether the error was handled by the callback. +/// +/// [`XErrorEvent`]: https://linux.die.net/man/3/xerrorevent #[cfg(feature = "x11")] pub type XlibErrorHook = Box bool + Send + Sync>; From 6cdb3179c8da1e72fe7c177f26e1ccc5ee22b1fe Mon Sep 17 00:00:00 2001 From: Robert Bragg Date: Tue, 26 Jul 2022 14:03:12 +0100 Subject: [PATCH 52/60] Consistently deliver a Resumed event on all platforms MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To be more consistent with mobile platforms this updates the Windows, macOS, Wayland, X11 and Web backends to all emit a Resumed event immediately after the initial `NewEvents(StartCause::Init)` event. The documentation for Suspended and Resumed has also been updated to provide general recommendations for how to handle Suspended and Resumed events in portable applications as well as providing Android and iOS specific details. This consistency makes it possible to write applications that lazily initialize their graphics state when the application resumes without any platform-specific knowledge. Previously, applications that wanted to run on Android and other systems would have to maintain two, mutually-exclusive, initialization paths. Note: This patch does nothing to guarantee that Suspended events will be delivered. It's still reasonable to say that most OSs without a formal lifecycle for applications will simply never "suspend" your application. There are currently no known portability issues caused by not delivering `Suspended` events consistently and technically it's not possible to guarantee the delivery of `Suspended` events if the OS doesn't define an application lifecycle. (app can always be terminated without any kind of clean up notification on most non-mobile OSs) Fixes #2185. Co-authored-by: Marijn Suijten Co-authored-by: Markus Røyset --- CHANGELOG.md | 1 + src/event.rs | 98 +++++++++++++++++++ .../linux/wayland/event_loop/mod.rs | 4 + src/platform_impl/linux/x11/mod.rs | 11 +++ src/platform_impl/macos/app_state.rs | 3 + src/platform_impl/web/event_loop/runner.rs | 5 +- .../windows/event_loop/runner.rs | 5 + 7 files changed, 125 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e46453b193..e2249f9673 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -76,6 +76,7 @@ And please only add new entries to the top of this list, right below the `# Unre - **Breaking**, update `raw-window-handle` to `v0.5` and implement `HasRawDisplayHandle` for `Window` and `EventLoopWindowTarget`. - On X11, add function `register_xlib_error_hook` into `winit::platform::unix` to subscribe for errors comming from Xlib. - On Android, upgrade `ndk` and `ndk-glue` dependencies to the recently released `0.7.0`. +- All platforms can now be relied on to emit a `Resumed` event. Applications are recommended to lazily initialize graphics state and windows on first resume for portability. # 0.26.1 (2022-01-05) diff --git a/src/event.rs b/src/event.rs index 9b0b94d80a..25cae732ba 100644 --- a/src/event.rs +++ b/src/event.rs @@ -74,9 +74,107 @@ pub enum Event<'a, T: 'static> { UserEvent(T), /// Emitted when the application has been suspended. + /// + /// # Portability + /// + /// Not all platforms support the notion of suspending applications, and there may be no + /// technical way to guarantee being able to emit a `Suspended` event if the OS has + /// no formal application lifecycle (currently only Android and iOS do). For this reason, + /// Winit does not currently try to emit pseudo `Suspended` events before the application + /// quits on platforms without an application lifecycle. + /// + /// Considering that the implementation of `Suspended` and [`Resumed`] events may be internally + /// driven by multiple platform-specific events, and that there may be subtle differences across + /// platforms with how these internal events are delivered, it's recommended that applications + /// be able to gracefully handle redundant (i.e. back-to-back) `Suspended` or [`Resumed`] events. + /// + /// Also see [`Resumed`] notes. + /// + /// ## Android + /// + /// On Android, the `Suspended` event is only sent when the application's associated + /// [`SurfaceView`] is destroyed. This is expected to closely correlate with the [`onPause`] + /// lifecycle event but there may technically be a discrepancy. + /// + /// [`onPause`]: https://developer.android.com/reference/android/app/Activity#onPause() + /// + /// Applications that need to run on Android should assume their [`SurfaceView`] has been + /// destroyed, which indirectly invalidates any existing render surfaces that may have been + /// created outside of Winit (such as an `EGLSurface`, [`VkSurfaceKHR`] or [`wgpu::Surface`]). + /// + /// After being `Suspended` on Android applications must drop all render surfaces before + /// the event callback completes, which may be re-created when the application is next [`Resumed`]. + /// + /// [`SurfaceView`]: https://developer.android.com/reference/android/view/SurfaceView + /// [Activity lifecycle]: https://developer.android.com/guide/components/activities/activity-lifecycle + /// [`VkSurfaceKHR`]: https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/VkSurfaceKHR.html + /// [`wgpu::Surface`]: https://docs.rs/wgpu/latest/wgpu/struct.Surface.html + /// + /// ## iOS + /// + /// On iOS, the `Suspended` event is currently emitted in response to an + /// [`applicationWillResignActive`] callback which means that the application is + /// about to transition from the active to inactive state (according to the + /// [iOS application lifecycle]). + /// + /// [`applicationWillResignActive`]: https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1622950-applicationwillresignactive + /// [iOS application lifecycle]: https://developer.apple.com/documentation/uikit/app_and_environment/managing_your_app_s_life_cycle + /// + /// [`Resumed`]: Self::Resumed Suspended, /// Emitted when the application has been resumed. + /// + /// For consistency, all platforms emit a `Resumed` event even if they don't themselves have a + /// formal suspend/resume lifecycle. For systems without a standard suspend/resume lifecycle + /// the `Resumed` event is always emitted after the [`NewEvents(StartCause::Init)`][StartCause::Init] + /// event. + /// + /// # Portability + /// + /// It's recommended that applications should only initialize their graphics context and create + /// a window after they have received their first `Resumed` event. Some systems + /// (specifically Android) won't allow applications to create a render surface until they are + /// resumed. + /// + /// Considering that the implementation of [`Suspended`] and `Resumed` events may be internally + /// driven by multiple platform-specific events, and that there may be subtle differences across + /// platforms with how these internal events are delivered, it's recommended that applications + /// be able to gracefully handle redundant (i.e. back-to-back) [`Suspended`] or `Resumed` events. + /// + /// Also see [`Suspended`] notes. + /// + /// ## Android + /// + /// On Android, the `Resumed` event is sent when a new [`SurfaceView`] has been created. This is + /// expected to closely correlate with the [`onResume`] lifecycle event but there may technically + /// be a discrepancy. + /// + /// [`onResume`]: https://developer.android.com/reference/android/app/Activity#onResume() + /// + /// Applications that need to run on Android must wait until they have been `Resumed` + /// before they will be able to create a render surface (such as an `EGLSurface`, + /// [`VkSurfaceKHR`] or [`wgpu::Surface`]) which depend on having a + /// [`SurfaceView`]. Applications must also assume that if they are [`Suspended`], then their + /// render surfaces are invalid and should be dropped. + /// + /// Also see [`Suspended`] notes. + /// + /// [`SurfaceView`]: https://developer.android.com/reference/android/view/SurfaceView + /// [Activity lifecycle]: https://developer.android.com/guide/components/activities/activity-lifecycle + /// [`VkSurfaceKHR`]: https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/VkSurfaceKHR.html + /// [`wgpu::Surface`]: https://docs.rs/wgpu/latest/wgpu/struct.Surface.html + /// + /// ## iOS + /// + /// On iOS, the `Resumed` event is emitted in response to an [`applicationDidBecomeActive`] + /// callback which means the application is "active" (according to the + /// [iOS application lifecycle]). + /// + /// [`applicationDidBecomeActive`]: https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1622956-applicationdidbecomeactive + /// [iOS application lifecycle]: https://developer.apple.com/documentation/uikit/app_and_environment/managing_your_app_s_life_cycle + /// + /// [`Suspended`]: Self::Suspended Resumed, /// Emitted when all of the event loop's input events have been processed and redraw processing diff --git a/src/platform_impl/linux/wayland/event_loop/mod.rs b/src/platform_impl/linux/wayland/event_loop/mod.rs index 0f8f8a1718..01b47f47d9 100644 --- a/src/platform_impl/linux/wayland/event_loop/mod.rs +++ b/src/platform_impl/linux/wayland/event_loop/mod.rs @@ -232,6 +232,10 @@ impl EventLoop { &mut control_flow, ); + // NB: For consistency all platforms must emit a 'resumed' event even though Wayland + // applications don't themselves have a formal suspend/resume lifecycle. + callback(Event::Resumed, &self.window_target, &mut control_flow); + let mut window_updates: Vec<(WindowId, WindowUpdate)> = Vec::new(); let mut event_sink_back_buffer = Vec::new(); diff --git a/src/platform_impl/linux/x11/mod.rs b/src/platform_impl/linux/x11/mod.rs index 2fe262cc12..c34eb2d460 100644 --- a/src/platform_impl/linux/x11/mod.rs +++ b/src/platform_impl/linux/x11/mod.rs @@ -333,6 +333,17 @@ impl EventLoop { callback, ); + // NB: For consistency all platforms must emit a 'resumed' event even though X11 + // applications don't themselves have a formal suspend/resume lifecycle. + if *cause == StartCause::Init { + sticky_exit_callback( + crate::event::Event::Resumed, + &this.target, + control_flow, + callback, + ); + } + // Process all pending events this.drain_events(callback, control_flow); diff --git a/src/platform_impl/macos/app_state.rs b/src/platform_impl/macos/app_state.rs index 98093963cc..711c99b158 100644 --- a/src/platform_impl/macos/app_state.rs +++ b/src/platform_impl/macos/app_state.rs @@ -302,6 +302,9 @@ impl AppState { HANDLER.handle_nonuser_event(EventWrapper::StaticEvent(Event::NewEvents( StartCause::Init, ))); + // NB: For consistency all platforms must emit a 'resumed' event even though macOS + // applications don't themselves have a formal suspend/resume lifecycle. + HANDLER.handle_nonuser_event(EventWrapper::StaticEvent(Event::Resumed)); HANDLER.set_in_callback(false); } diff --git a/src/platform_impl/web/event_loop/runner.rs b/src/platform_impl/web/event_loop/runner.rs index fd9a2e850e..55acc1ec1c 100644 --- a/src/platform_impl/web/event_loop/runner.rs +++ b/src/platform_impl/web/event_loop/runner.rs @@ -158,8 +158,9 @@ impl Shared { } pub fn init(&self) { - let start_cause = Event::NewEvents(StartCause::Init); - self.run_until_cleared(iter::once(start_cause)); + // NB: For consistency all platforms must emit a 'resumed' event even though web + // applications don't themselves have a formal suspend/resume lifecycle. + self.run_until_cleared([Event::NewEvents(StartCause::Init), Event::Resumed].into_iter()); } // Run the polling logic for the Poll ControlFlow, which involves clearing the queue diff --git a/src/platform_impl/windows/event_loop/runner.rs b/src/platform_impl/windows/event_loop/runner.rs index fab5c61360..af03005557 100644 --- a/src/platform_impl/windows/event_loop/runner.rs +++ b/src/platform_impl/windows/event_loop/runner.rs @@ -393,6 +393,11 @@ impl EventLoopRunner { } }; self.call_event_handler(Event::NewEvents(start_cause)); + // NB: For consistency all platforms must emit a 'resumed' event even though Windows + // applications don't themselves have a formal suspend/resume lifecycle. + if init { + self.call_event_handler(Event::Resumed); + } self.dispatch_buffered_events(); RedrawWindow(self.thread_msg_target, ptr::null(), 0, RDW_INTERNALPAINT); } From 4895a29e92a6fa9f7640558bd2676cdc48f66d8d Mon Sep 17 00:00:00 2001 From: Marijn Suijten Date: Tue, 26 Jul 2022 18:55:06 +0200 Subject: [PATCH 53/60] ci: manually point ANDROID_NDK_ROOT to latest supplied version It seems the symlink to `ndk-bundle` and this environment variable pointing to it have been removed to prevent the sdkmanager from failing, when finding the SDK setup to be in an "indeterminate" state. It is now up to the users themselves to install an NDK through that tool or point the right variables to a preinstalled "latest" NDK. https://github.com/actions/virtual-environments/issues/2689 https://github.com/actions/virtual-environments/pull/5926 --- .github/workflows/ci.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b02af77fb9..589c772200 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -67,6 +67,11 @@ jobs: targets: ${{ matrix.platform.target }} components: clippy + - name: Setup NDK path + shell: bash + # "Temporary" workaround until https://github.com/actions/virtual-environments/issues/5879#issuecomment-1195156618 + # gets looked into. + run: echo "ANDROID_NDK_ROOT=$ANDROID_NDK_LATEST_HOME" >> $GITHUB_ENV - name: Install Linux dependencies if: (matrix.platform.os == 'ubuntu-latest') run: sudo apt-get update && sudo apt-get install pkg-config cmake libfreetype6-dev libfontconfig1-dev From 64c22f907518e9ed598a67e11a7f3fef49fab097 Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Tue, 26 Jul 2022 21:25:00 +0200 Subject: [PATCH 54/60] Fix changelog entry wrt scrolling The breaking change was put into the wrong release section. --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e2249f9673..e9e6bc91b9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -77,6 +77,7 @@ And please only add new entries to the top of this list, right below the `# Unre - On X11, add function `register_xlib_error_hook` into `winit::platform::unix` to subscribe for errors comming from Xlib. - On Android, upgrade `ndk` and `ndk-glue` dependencies to the recently released `0.7.0`. - All platforms can now be relied on to emit a `Resumed` event. Applications are recommended to lazily initialize graphics state and windows on first resume for portability. +- **Breaking:**: Reverse horizontal scrolling sign in `MouseScrollDelta` to match the direction of vertical scrolling. A positive X value now means moving the content to the right. The meaning of vertical scrolling stays the same: a positive Y value means moving the content down. # 0.26.1 (2022-01-05) @@ -85,7 +86,6 @@ And please only add new entries to the top of this list, right below the `# Unre - On X11, add mappings for numpad comma, numpad enter, numlock and pause. - On macOS, fix Pinyin IME input by reverting a change that intended to improve IME. - On Windows, fix a crash with transparent windows on Windows 11. -- **Breaking:**: Reverse horizontal scrolling sign in `MouseScrollDelta` to match the direction of vertical scrolling. A positive X value now means moving the content to the right. The meaning of vertical scrolling stays the same: a positive Y value means moving the content down. # 0.26.0 (2021-12-01) From 50035643f73efd01b223dc25f771a3a1dee04a27 Mon Sep 17 00:00:00 2001 From: Kirill Chibisov Date: Tue, 26 Jul 2022 23:54:12 +0300 Subject: [PATCH 55/60] Release 0.27.0 version --- CHANGELOG.md | 2 ++ Cargo.toml | 2 +- README.md | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e9e6bc91b9..d873f0e231 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,8 @@ And please only add new entries to the top of this list, right below the `# Unre # Unreleased +# 0.27.0 (2022-07-26) + - On Windows, fix hiding a maximized window. - On Android, `ndk-glue`'s `NativeWindow` lock is now held between `Event::Resumed` and `Event::Suspended`. - On Web, added `EventLoopExtWebSys` with a `spawn` method to start the event loop without throwing an exception. diff --git a/Cargo.toml b/Cargo.toml index 205e6f4c0b..b04a8a9fbb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "winit" -version = "0.26.1" +version = "0.27.0" authors = ["The winit contributors", "Pierre Krieger "] description = "Cross-platform window creation library." edition = "2021" diff --git a/README.md b/README.md index 2e3880e7e1..dcb1158448 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ ```toml [dependencies] -winit = "0.26.1" +winit = "0.27.0" ``` ## [Documentation](https://docs.rs/winit) From bf537009d9d36659cf12e56265faf14fedab4c2e Mon Sep 17 00:00:00 2001 From: Kirill Chibisov Date: Fri, 29 Jul 2022 14:39:41 +0300 Subject: [PATCH 56/60] Explicitly specify minimum supported rust version This should help with distributing apps using winit. Fixes #1075. --- .github/workflows/ci.yml | 4 ++-- CHANGELOG.md | 2 ++ CONTRIBUTING.md | 1 + Cargo.toml | 1 + examples/fullscreen.rs | 4 ++-- src/platform/unix.rs | 3 ++- src/platform_impl/ios/app_state.rs | 2 +- src/platform_impl/linux/mod.rs | 7 ++++--- 8 files changed, 15 insertions(+), 9 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 589c772200..493cad3183 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -22,7 +22,7 @@ jobs: strategy: fail-fast: false matrix: - rust_version: [stable, nightly] + rust_version: [1.57.0, stable, nightly] platform: # Note: Make sure that we test all the `docs.rs` targets defined in Cargo.toml! - { target: x86_64-pc-windows-msvc, os: windows-latest, } @@ -103,7 +103,7 @@ jobs: - name: Lint with clippy shell: bash - if: (matrix.rust_version != 'nightly') && !contains(matrix.platform.options, '--no-default-features') + if: (matrix.rust_version == '1.57.0') && !contains(matrix.platform.options, '--no-default-features') run: cargo clippy --all-targets --target ${{ matrix.platform.target }} $OPTIONS --features $FEATURES -- -Dwarnings - name: Build with serde enabled diff --git a/CHANGELOG.md b/CHANGELOG.md index d873f0e231..4c968c6dd9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,8 @@ And please only add new entries to the top of this list, right below the `# Unre # Unreleased +- The minimum supported Rust version was lowered to `1.57.0` and now explicitly tested. + # 0.27.0 (2022-07-26) - On Windows, fix hiding a maximized window. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 5f90b74dff..021d8a043e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -20,6 +20,7 @@ your description of the issue as detailed as possible: When making a code contribution to winit, before opening your pull request, please make sure that: +- your patch builds with Winit's minimal supported rust version - Rust 1.57.0. - you tested your modifications on all the platforms impacted, or if not possible detail which platforms were not tested, and what should be tested, so that a maintainer or another contributor can test them - you updated any relevant documentation in winit diff --git a/Cargo.toml b/Cargo.toml index b04a8a9fbb..34944f321f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,6 +10,7 @@ readme = "README.md" repository = "https://github.com/rust-windowing/winit" documentation = "https://docs.rs/winit" categories = ["gui"] +rust-version = "1.57.0" [package.metadata.docs.rs] features = ["serde"] diff --git a/examples/fullscreen.rs b/examples/fullscreen.rs index 212bd75505..6bfda4c2c9 100644 --- a/examples/fullscreen.rs +++ b/examples/fullscreen.rs @@ -59,12 +59,12 @@ fn main() { } VirtualKeyCode::F => { let fullscreen = Some(Fullscreen::Exclusive(mode.clone())); - println!("Setting mode: {fullscreen:?}"); + println!("Setting mode: {:?}", fullscreen); window.set_fullscreen(fullscreen); } VirtualKeyCode::B => { let fullscreen = Some(Fullscreen::Borderless(Some(monitor.clone()))); - println!("Setting mode: {fullscreen:?}"); + println!("Setting mode: {:?}", fullscreen); window.set_fullscreen(fullscreen); } VirtualKeyCode::S => { diff --git a/src/platform/unix.rs b/src/platform/unix.rs index 60a40a0f38..1d33e846bd 100644 --- a/src/platform/unix.rs +++ b/src/platform/unix.rs @@ -299,10 +299,11 @@ impl WindowExtUnix for Window { #[inline] #[cfg(feature = "wayland")] fn wayland_set_csd_theme(&self, theme: Theme) { + #[allow(clippy::single_match)] match self.window { LinuxWindow::Wayland(ref w) => w.set_csd_theme(theme), #[cfg(feature = "x11")] - _ => {} + _ => (), } } diff --git a/src/platform_impl/ios/app_state.rs b/src/platform_impl/ios/app_state.rs index 55ed3e9855..0586084476 100644 --- a/src/platform_impl/ios/app_state.rs +++ b/src/platform_impl/ios/app_state.rs @@ -793,7 +793,7 @@ pub unsafe fn handle_main_events_cleared() { return; } match this.state_mut() { - &mut AppStateImpl::ProcessingEvents { .. } => {} + AppStateImpl::ProcessingEvents { .. } => {} _ => bug!("`ProcessingRedraws` happened unexpectedly"), }; drop(this); diff --git a/src/platform_impl/linux/mod.rs b/src/platform_impl/linux/mod.rs index d73eb725ff..f3c9e9c2e8 100644 --- a/src/platform_impl/linux/mod.rs +++ b/src/platform_impl/linux/mod.rs @@ -587,7 +587,8 @@ impl Window { /// Hooks for X11 errors. #[cfg(feature = "x11")] -pub(crate) static mut XLIB_ERROR_HOOKS: Mutex> = Mutex::new(Vec::new()); +pub(crate) static mut XLIB_ERROR_HOOKS: Lazy>> = + Lazy::new(|| Mutex::new(Vec::new())); #[cfg(feature = "x11")] unsafe extern "C" fn x_error_callback( @@ -633,7 +634,7 @@ unsafe extern "C" fn x_error_callback( pub enum EventLoop { #[cfg(feature = "wayland")] - Wayland(wayland::EventLoop), + Wayland(Box>), #[cfg(feature = "x11")] X(x11::EventLoop), } @@ -723,7 +724,7 @@ impl EventLoop { #[cfg(feature = "wayland")] fn new_wayland_any_thread() -> Result, Box> { - wayland::EventLoop::new().map(EventLoop::Wayland) + wayland::EventLoop::new().map(|evlp| EventLoop::Wayland(Box::new(evlp))) } #[cfg(feature = "x11")] From 95246d81c1cec4a46852118ee884ed516db461e6 Mon Sep 17 00:00:00 2001 From: Kirill Chibisov Date: Sat, 30 Jul 2022 17:15:57 +0300 Subject: [PATCH 57/60] On X11, fix crash when can't disable IME Fixes #2402. --- src/platform_impl/linux/x11/ime/context.rs | 13 ++++---- src/platform_impl/linux/x11/ime/mod.rs | 39 ++++++++++++++++------ 2 files changed, 35 insertions(+), 17 deletions(-) diff --git a/src/platform_impl/linux/x11/ime/context.rs b/src/platform_impl/linux/x11/ime/context.rs index f0a751b745..a3109d57db 100644 --- a/src/platform_impl/linux/x11/ime/context.rs +++ b/src/platform_impl/linux/x11/ime/context.rs @@ -1,13 +1,14 @@ -use std::mem::transmute; +use std::ffi::CStr; use std::os::raw::c_short; -use std::ptr; use std::sync::Arc; +use std::{mem, ptr}; -use super::{ffi, util, XConnection, XError}; -use crate::platform_impl::platform::x11::ime::{ImeEvent, ImeEventSender}; -use std::ffi::CStr; use x11_dl::xlib::{XIMCallback, XIMPreeditCaretCallbackStruct, XIMPreeditDrawCallbackStruct}; +use crate::platform_impl::platform::x11::ime::{ImeEvent, ImeEventSender}; + +use super::{ffi, util, XConnection, XError}; + /// IME creation error. #[derive(Debug)] pub enum ImeContextCreationError { @@ -163,7 +164,7 @@ struct PreeditCallbacks { impl PreeditCallbacks { pub fn new(client_data: ffi::XPointer) -> PreeditCallbacks { let start_callback = create_xim_callback(client_data, unsafe { - transmute(preedit_start_callback as usize) + mem::transmute(preedit_start_callback as usize) }); let done_callback = create_xim_callback(client_data, preedit_done_callback); let caret_callback = create_xim_callback(client_data, preedit_caret_callback); diff --git a/src/platform_impl/linux/x11/ime/mod.rs b/src/platform_impl/linux/x11/ime/mod.rs index 4125c0ac0a..746ba9ea90 100644 --- a/src/platform_impl/linux/x11/ime/mod.rs +++ b/src/platform_impl/linux/x11/ime/mod.rs @@ -120,7 +120,33 @@ impl Ime { // Create empty entry in map, so that when IME is rebuilt, this window has a context. None } else { - let event = if with_preedit { + let context = unsafe { + ImeContext::new( + &self.inner.xconn, + self.inner.im, + window, + None, + with_preedit, + self.inner.event_sender.clone(), + ) + .or_else(|_| { + debug!( + "failed to create an IME context {} preedit support", + if with_preedit { "with" } else { "without" } + ); + ImeContext::new( + &self.inner.xconn, + self.inner.im, + window, + None, + !with_preedit, + self.inner.event_sender.clone(), + ) + }) + }?; + + // Check the state on the context, since it could fail to enable or disable preedit. + let event = if context.is_allowed { ImeEvent::Enabled } else { // There's no IME without preedit. @@ -132,16 +158,7 @@ impl Ime { .send((window, event)) .expect("Failed to send enabled event"); - Some(unsafe { - ImeContext::new( - &self.inner.xconn, - self.inner.im, - window, - None, - with_preedit, - self.inner.event_sender.clone(), - ) - }?) + Some(context) }; self.inner.contexts.insert(window, context); Ok(!self.is_destroyed()) From c53a574bff147ac35ac69c81bc48007094642108 Mon Sep 17 00:00:00 2001 From: Kirill Chibisov Date: Sat, 30 Jul 2022 19:33:23 +0300 Subject: [PATCH 58/60] Release 0.27.1 version --- CHANGELOG.md | 3 +++ Cargo.toml | 2 +- README.md | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4c968c6dd9..539b1ca923 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,10 @@ And please only add new entries to the top of this list, right below the `# Unre # Unreleased +# 0.27.1 (2022-07-30) + - The minimum supported Rust version was lowered to `1.57.0` and now explicitly tested. +- On X11, fix crash on start due to inability to create an IME context without any preedit. # 0.27.0 (2022-07-26) diff --git a/Cargo.toml b/Cargo.toml index 34944f321f..5365442273 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "winit" -version = "0.27.0" +version = "0.27.1" authors = ["The winit contributors", "Pierre Krieger "] description = "Cross-platform window creation library." edition = "2021" diff --git a/README.md b/README.md index dcb1158448..c42910fd5d 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ ```toml [dependencies] -winit = "0.27.0" +winit = "0.27.1" ``` ## [Documentation](https://docs.rs/winit) From 6b7ceedc9178b552935f289649e0a5abb316050e Mon Sep 17 00:00:00 2001 From: Amr Bashir Date: Thu, 4 Aug 2022 23:03:55 +0200 Subject: [PATCH 59/60] Windows: respect min/max sizes when creating the window (#2393) --- CHANGELOG.md | 2 ++ src/dpi.rs | 23 +++++++++++++++++++++++ src/platform_impl/windows/window.rs | 11 +++++++++-- 3 files changed, 34 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 539b1ca923..cc0402ccfc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,8 @@ And please only add new entries to the top of this list, right below the `# Unre # Unreleased +- On Windows, respect min/max inner sizes when creating the window. + # 0.27.1 (2022-07-30) - The minimum supported Rust version was lowered to `1.57.0` and now explicitly tested. diff --git a/src/dpi.rs b/src/dpi.rs index 3c3829359f..7d372d8899 100644 --- a/src/dpi.rs +++ b/src/dpi.rs @@ -511,6 +511,29 @@ impl Size { Size::Logical(size) => size.to_physical(scale_factor), } } + + pub fn clamp>(input: S, min: S, max: S, scale_factor: f64) -> Size { + let (input, min, max) = ( + input.into().to_physical::(scale_factor), + min.into().to_physical::(scale_factor), + max.into().to_physical::(scale_factor), + ); + + let clamp = |input: f64, min: f64, max: f64| { + if input < min { + min + } else if input > max { + max + } else { + input + } + }; + + let width = clamp(input.width, min.width, max.width); + let height = clamp(input.height, min.height, max.height); + + PhysicalSize::new(width, height).into() + } } impl From> for Size { diff --git a/src/platform_impl/windows/window.rs b/src/platform_impl/windows/window.rs index 73680cdad9..e63075a833 100644 --- a/src/platform_impl/windows/window.rs +++ b/src/platform_impl/windows/window.rs @@ -879,10 +879,17 @@ impl<'a, T: 'static> InitData<'a, T> { win.set_fullscreen(attributes.fullscreen); force_window_active(win.window.0); } else { - let dimensions = attributes + let size = attributes .inner_size .unwrap_or_else(|| PhysicalSize::new(800, 600).into()); - win.set_inner_size(dimensions); + let max_size = attributes + .max_inner_size + .unwrap_or_else(|| PhysicalSize::new(f64::MAX, f64::MAX).into()); + let min_size = attributes + .min_inner_size + .unwrap_or_else(|| PhysicalSize::new(0, 0).into()); + let clamped_size = Size::clamp(size, min_size, max_size, win.scale_factor()); + win.set_inner_size(clamped_size); if attributes.maximized { // Need to set MAXIMIZED after setting `inner_size` as From cdbaf4816a8f03b3177255e4b0e5f4676033bc3a Mon Sep 17 00:00:00 2001 From: Anton Bulakh Date: Mon, 8 Aug 2022 17:27:11 +0300 Subject: [PATCH 60/60] On X11, fix window hints not persisting This commit fixes the issue with min, max, and resize increments not persisting across the dpi changes. --- CHANGELOG.md | 1 + src/platform_impl/linux/x11/window.rs | 12 ++++++------ 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cc0402ccfc..5633f7b23d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ And please only add new entries to the top of this list, right below the `# Unre # Unreleased +- On X11, fix min, max and resize increment hints not persisting for resizable windows (e.g. on DPI change). - On Windows, respect min/max inner sizes when creating the window. # 0.27.1 (2022-07-30) diff --git a/src/platform_impl/linux/x11/window.rs b/src/platform_impl/linux/x11/window.rs index eb3326475b..ab7cddfa42 100644 --- a/src/platform_impl/linux/x11/window.rs +++ b/src/platform_impl/linux/x11/window.rs @@ -371,15 +371,15 @@ impl UnownedWindow { } else { max_inner_size = Some(dimensions.into()); min_inner_size = Some(dimensions.into()); - - let mut shared_state = window.shared_state.get_mut(); - shared_state.min_inner_size = window_attrs.min_inner_size; - shared_state.max_inner_size = window_attrs.max_inner_size; - shared_state.resize_increments = pl_attribs.resize_increments; - shared_state.base_size = pl_attribs.base_size; } } + let mut shared_state = window.shared_state.get_mut(); + shared_state.min_inner_size = min_inner_size.map(Into::into); + shared_state.max_inner_size = max_inner_size.map(Into::into); + shared_state.resize_increments = pl_attribs.resize_increments; + shared_state.base_size = pl_attribs.base_size; + let mut normal_hints = util::NormalHints::new(xconn); normal_hints.set_position(position.map(|PhysicalPosition { x, y }| (x, y))); normal_hints.set_size(Some(dimensions));