From 0994b5ceb810f3551a04d559d896200ed3642b3b Mon Sep 17 00:00:00 2001 From: Marijn Suijten Date: Sat, 4 Nov 2023 12:18:55 +0100 Subject: [PATCH 01/15] Disable `default-features` for the `ndk` crate We decided to add `rwh_06` to the `default` list of features in the `ndk` to [nudge users to upgrade], but this forces `winit` to always (transitively) include `raw-window-handle 0.6` even if the user has set a different `rwh_xx` feature on the `winit` crate. `winit` already forwards the respective `rwh_xx` feaure to the `ndk` crate anyway, so this default should just be turned off. At the time of writing this is the only `default` feature of the `ndk`. Links: https://github.com/rust-mobile/ndk/pull/434#issuecomment-1752089087 --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index b54d37c06a..078967adc1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -74,7 +74,7 @@ softbuffer = "0.3.0" [target.'cfg(target_os = "android")'.dependencies] android-activity = "0.5.0" -ndk = "0.8.0" +ndk = { version = "0.8.0", default-features = false } ndk-sys = "0.5.0" [target.'cfg(any(target_os = "ios", target_os = "macos"))'.dependencies] From e9210555c187c0573b1d13d44fa83035ef3323b0 Mon Sep 17 00:00:00 2001 From: Kirill Chibisov Date: Sat, 4 Nov 2023 15:19:15 +0400 Subject: [PATCH 02/15] On X11, try alternative cursor icon names as well This should cover more icons. --- CHANGELOG.md | 1 + Cargo.toml | 2 +- src/platform_impl/linux/x11/util/cursor.rs | 19 ++++++++++++++++--- 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 561e24cb17..66b609513e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ Unreleased` header. # Unreleased +- On X11, check common alternative cursor names when loading cursor. - On Windows, fix so `drag_window` and `drag_resize_window` can be called from another thread. # 0.29.3 diff --git a/Cargo.toml b/Cargo.toml index 078967adc1..64c1f6e3b9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -54,7 +54,7 @@ cfg_aliases = "0.1.1" [dependencies] bitflags = "2" -cursor-icon = "1.0.0" +cursor-icon = "1.1.0" log = "0.4" mint = { version = "0.5.6", optional = true } once_cell = "1.12" diff --git a/src/platform_impl/linux/x11/util/cursor.rs b/src/platform_impl/linux/x11/util/cursor.rs index 5845511d07..8d62cfa7f0 100644 --- a/src/platform_impl/linux/x11/util/cursor.rs +++ b/src/platform_impl/linux/x11/util/cursor.rs @@ -1,4 +1,5 @@ use std::ffi::CString; +use std::iter; use x11rb::connection::Connection; @@ -56,10 +57,22 @@ impl XConnection { None => return self.create_empty_cursor(), }; - let name = CString::new(cursor.name()).unwrap(); - unsafe { - (self.xcursor.XcursorLibraryLoadCursor)(self.display, name.as_ptr() as *const c_char) + let mut xcursor = 0; + for &name in iter::once(&cursor.name()).chain(cursor.alt_names().iter()) { + let name = CString::new(name).unwrap(); + xcursor = unsafe { + (self.xcursor.XcursorLibraryLoadCursor)( + self.display, + name.as_ptr() as *const c_char, + ) + }; + + if xcursor != 0 { + break; + } } + + xcursor } fn update_cursor(&self, window: xproto::Window, cursor: ffi::Cursor) -> Result<(), X11Error> { From c89e6df758652443db9d0b6e1e2af2b35cd3af8e Mon Sep 17 00:00:00 2001 From: Nathan Lilienthal Date: Mon, 6 Nov 2023 17:26:02 -0500 Subject: [PATCH 03/15] On macOS, send a Resized event after ScaleFactorChanged Fixes #3213. --- CHANGELOG.md | 1 + src/platform_impl/macos/app_state.rs | 10 ++++++++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 66b609513e..5be490485e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ Unreleased` header. - On X11, check common alternative cursor names when loading cursor. - On Windows, fix so `drag_window` and `drag_resize_window` can be called from another thread. +- On macOS, send a `Resized` event after each `ScaleFactorChanged` event. # 0.29.3 diff --git a/src/platform_impl/macos/app_state.rs b/src/platform_impl/macos/app_state.rs index 114c124f5e..33cc1d88e6 100644 --- a/src/platform_impl/macos/app_state.rs +++ b/src/platform_impl/macos/app_state.rs @@ -329,7 +329,7 @@ impl Handler { ) { if let Some(ref mut callback) = *self.callback.lock().unwrap() { let new_inner_size = Arc::new(Mutex::new(suggested_size)); - let event = Event::WindowEvent { + let scale_factor_changed_event = Event::WindowEvent { window_id: WindowId(window.id()), event: WindowEvent::ScaleFactorChanged { scale_factor, @@ -337,13 +337,19 @@ impl Handler { }, }; - callback.handle_nonuser_event(event); + callback.handle_nonuser_event(scale_factor_changed_event); let physical_size = *new_inner_size.lock().unwrap(); drop(new_inner_size); let logical_size = physical_size.to_logical(scale_factor); let size = NSSize::new(logical_size.width, logical_size.height); window.setContentSize(size); + + let resized_event = Event::WindowEvent { + window_id: WindowId(window.id()), + event: WindowEvent::Resized(physical_size), + }; + callback.handle_nonuser_event(resized_event); } } } From 21701a33de20775fd1fb11ca75b23bf5438a7acf Mon Sep 17 00:00:00 2001 From: Olivier Goffart Date: Wed, 8 Nov 2023 16:21:33 +0100 Subject: [PATCH 04/15] On Windows, fix `set_control_flow` from `AboutToWait In case the AboutToWait event sets the control flow to another value it's not being used on this iteration. Fixes #3215. --- CHANGELOG.md | 1 + src/platform_impl/windows/event_loop.rs | 25 ++++++++++++------------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5be490485e..a0c8775bbf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ Unreleased` header. - On X11, check common alternative cursor names when loading cursor. - On Windows, fix so `drag_window` and `drag_resize_window` can be called from another thread. +- On Windows, fix `set_control_flow` in `AboutToWait` not being taken in account. - On macOS, send a `Resized` event after each `ScaleFactorChanged` event. # 0.29.3 diff --git a/src/platform_impl/windows/event_loop.rs b/src/platform_impl/windows/event_loop.rs index 2bedd9a4be..d4263920af 100644 --- a/src/platform_impl/windows/event_loop.rs +++ b/src/platform_impl/windows/event_loop.rs @@ -356,19 +356,6 @@ impl EventLoop { /// Wait for one message and dispatch it, optionally with a timeout fn wait_and_dispatch_message(&mut self, timeout: Option) { - let start = Instant::now(); - - let runner = &self.window_target.p.runner_shared; - - let control_flow_timeout = match runner.control_flow() { - ControlFlow::Wait => None, - ControlFlow::Poll => Some(Duration::ZERO), - ControlFlow::WaitUntil(wait_deadline) => { - Some(wait_deadline.saturating_duration_since(start)) - } - }; - let timeout = min_timeout(control_flow_timeout, timeout); - fn get_msg_with_timeout(msg: &mut MSG, timeout: Option) -> PumpStatus { unsafe { // A timeout of None means wait indefinitely (so we don't need to call SetTimer) @@ -404,6 +391,8 @@ impl EventLoop { } } + let runner = &self.window_target.p.runner_shared; + // We aim to be consistent with the MacOS backend which has a RunLoop // observer that will dispatch AboutToWait when about to wait for // events, and NewEvents after the RunLoop wakes up. @@ -415,6 +404,16 @@ impl EventLoop { // runner.prepare_wait(); + let control_flow_timeout = match runner.control_flow() { + ControlFlow::Wait => None, + ControlFlow::Poll => Some(Duration::ZERO), + ControlFlow::WaitUntil(wait_deadline) => { + let start = Instant::now(); + Some(wait_deadline.saturating_duration_since(start)) + } + }; + let timeout = min_timeout(control_flow_timeout, timeout); + // # Safety // The Windows API has no documented requirement for bitwise // initializing a `MSG` struct (it can be uninitialized memory for the C From eab982c40257d2d75859fe4edc543a5e3c7478ec Mon Sep 17 00:00:00 2001 From: daxpedda Date: Fri, 10 Nov 2023 22:46:51 +0100 Subject: [PATCH 05/15] Web: forbid additional functions in favor of caching them (#3219) --- clippy.toml | 1 + src/platform_impl/web/web_sys/canvas.rs | 55 +++++++++++--- src/platform_impl/web/web_sys/mod.rs | 72 +++++++------------ .../web/web_sys/resize_scaling.rs | 23 +++--- src/platform_impl/web/window.rs | 13 ++-- 5 files changed, 87 insertions(+), 77 deletions(-) diff --git a/clippy.toml b/clippy.toml index cd4c241255..0b6f05a204 100644 --- a/clippy.toml +++ b/clippy.toml @@ -6,6 +6,7 @@ disallowed-methods = [ { path = "web_sys::HtmlCanvasElement::set_height", reason = "Winit shouldn't touch the internal canvas size" }, { path = "web_sys::Window::document", reason = "cache this to reduce calls to JS" }, { path = "web_sys::Window::get_computed_style", reason = "cache this to reduce calls to JS" }, + { path = "web_sys::HtmlElement::style", reason = "cache this to reduce calls to JS" }, { path = "web_sys::Element::request_fullscreen", reason = "Doesn't account for compatibility with Safari" }, { path = "web_sys::Document::exit_fullscreen", reason = "Doesn't account for compatibility with Safari" }, { path = "web_sys::Document::fullscreen_element", reason = "Doesn't account for compatibility with Safari" }, diff --git a/src/platform_impl/web/web_sys/canvas.rs b/src/platform_impl/web/web_sys/canvas.rs index cc7e4b054e..6cf5d339b0 100644 --- a/src/platform_impl/web/web_sys/canvas.rs +++ b/src/platform_impl/web/web_sys/canvas.rs @@ -49,12 +49,18 @@ pub struct Common { pub document: Document, /// Note: resizing the HTMLCanvasElement should go through `backend::set_canvas_size` to ensure the DPI factor is maintained. pub raw: HtmlCanvasElement, - style: CssStyleDeclaration, + style: Style, old_size: Rc>>, current_size: Rc>>, fullscreen_handler: Rc, } +#[derive(Clone)] +pub struct Style { + read: CssStyleDeclaration, + write: CssStyleDeclaration, +} + impl Canvas { pub fn create( id: WindowId, @@ -90,12 +96,7 @@ impl Canvas { .map_err(|_| os_error!(OsError("Failed to set a tabindex".to_owned())))?; } - #[allow(clippy::disallowed_methods)] - let style = window - .get_computed_style(&canvas) - .expect("Failed to obtain computed style") - // this can't fail: we aren't using a pseudo-element - .expect("Invalid pseudo-element"); + let style = Style::new(&window, &canvas); let common = Common { window: window.clone(), @@ -178,9 +179,7 @@ impl Canvas { y: bounds.y(), }; - if self.document().contains(Some(self.raw())) - && self.style().get_property_value("display").unwrap() != "none" - { + if self.document().contains(Some(self.raw())) && self.style().get("display") != "none" { position.x += super::style_size_property(self.style(), "border-left-width") + super::style_size_property(self.style(), "padding-left"); position.y += super::style_size_property(self.style(), "border-top-width") @@ -226,7 +225,7 @@ impl Canvas { } #[inline] - pub fn style(&self) -> &CssStyleDeclaration { + pub fn style(&self) -> &Style { &self.common.style } @@ -563,3 +562,37 @@ impl Common { }) } } + +impl Style { + fn new(window: &web_sys::Window, canvas: &HtmlCanvasElement) -> Self { + #[allow(clippy::disallowed_methods)] + let read = window + .get_computed_style(canvas) + .expect("Failed to obtain computed style") + // this can't fail: we aren't using a pseudo-element + .expect("Invalid pseudo-element"); + + #[allow(clippy::disallowed_methods)] + let write = canvas.style(); + + Self { read, write } + } + + pub(crate) fn get(&self, property: &str) -> String { + self.read + .get_property_value(property) + .expect("Invalid property") + } + + pub(crate) fn remove(&self, property: &str) { + self.write + .remove_property(property) + .expect("Property is read only"); + } + + pub(crate) fn set(&self, property: &str, value: &str) { + self.write + .set_property(property, value) + .expect("Property is read only"); + } +} diff --git a/src/platform_impl/web/web_sys/mod.rs b/src/platform_impl/web/web_sys/mod.rs index 45dd7bf487..d86f991c47 100644 --- a/src/platform_impl/web/web_sys/mod.rs +++ b/src/platform_impl/web/web_sys/mod.rs @@ -10,6 +10,7 @@ mod resize_scaling; mod schedule; pub use self::canvas::Canvas; +use self::canvas::Style; pub use self::event::ButtonsState; pub use self::event_handle::EventListenerHandle; pub use self::resize_scaling::ResizeScaleHandle; @@ -17,9 +18,7 @@ pub use self::schedule::Schedule; use crate::dpi::{LogicalPosition, LogicalSize}; use wasm_bindgen::closure::Closure; -use web_sys::{ - CssStyleDeclaration, Document, HtmlCanvasElement, PageTransitionEvent, VisibilityState, -}; +use web_sys::{Document, HtmlCanvasElement, PageTransitionEvent, VisibilityState}; pub fn throw(msg: &str) { wasm_bindgen::throw_str(msg); @@ -51,8 +50,8 @@ pub fn scale_factor(window: &web_sys::Window) -> f64 { window.device_pixel_ratio() } -fn fix_canvas_size(style: &CssStyleDeclaration, mut size: LogicalSize) -> LogicalSize { - if style.get_property_value("box-sizing").unwrap() == "border-box" { +fn fix_canvas_size(style: &Style, mut size: LogicalSize) -> LogicalSize { + if style.get("box-sizing") == "border-box" { size.width += style_size_property(style, "border-left-width") + style_size_property(style, "border-right-width") + style_size_property(style, "padding-left") @@ -69,76 +68,68 @@ fn fix_canvas_size(style: &CssStyleDeclaration, mut size: LogicalSize) -> L pub fn set_canvas_size( document: &Document, raw: &HtmlCanvasElement, - style: &CssStyleDeclaration, + style: &Style, new_size: LogicalSize, ) { - if !document.contains(Some(raw)) || style.get_property_value("display").unwrap() == "none" { + if !document.contains(Some(raw)) || style.get("display") == "none" { return; } let new_size = fix_canvas_size(style, new_size); - set_canvas_style_property(raw, "width", &format!("{}px", new_size.width)); - set_canvas_style_property(raw, "height", &format!("{}px", new_size.height)); + style.set("width", &format!("{}px", new_size.width)); + style.set("height", &format!("{}px", new_size.height)); } pub fn set_canvas_min_size( document: &Document, raw: &HtmlCanvasElement, - style: &CssStyleDeclaration, + style: &Style, dimensions: Option>, ) { if let Some(dimensions) = dimensions { - if !document.contains(Some(raw)) || style.get_property_value("display").unwrap() == "none" { + if !document.contains(Some(raw)) || style.get("display") == "none" { return; } let new_size = fix_canvas_size(style, dimensions); - set_canvas_style_property(raw, "min-width", &format!("{}px", new_size.width)); - set_canvas_style_property(raw, "min-height", &format!("{}px", new_size.height)); + style.set("min-width", &format!("{}px", new_size.width)); + style.set("min-height", &format!("{}px", new_size.height)); } else { - style - .remove_property("min-width") - .expect("Property is read only"); - style - .remove_property("min-height") - .expect("Property is read only"); + style.remove("min-width"); + style.remove("min-height"); } } pub fn set_canvas_max_size( document: &Document, raw: &HtmlCanvasElement, - style: &CssStyleDeclaration, + style: &Style, dimensions: Option>, ) { if let Some(dimensions) = dimensions { - if !document.contains(Some(raw)) || style.get_property_value("display").unwrap() == "none" { + if !document.contains(Some(raw)) || style.get("display") == "none" { return; } let new_size = fix_canvas_size(style, dimensions); - set_canvas_style_property(raw, "max-width", &format!("{}px", new_size.width)); - set_canvas_style_property(raw, "max-height", &format!("{}px", new_size.height)); + style.set("max-width", &format!("{}px", new_size.width)); + style.set("max-height", &format!("{}px", new_size.height)); } else { - style - .remove_property("max-width") - .expect("Property is read only"); - style - .remove_property("max-height") - .expect("Property is read only"); + style.remove("max-width"); + style.remove("max-height"); } } pub fn set_canvas_position( document: &Document, raw: &HtmlCanvasElement, - style: &CssStyleDeclaration, + style: &Style, mut position: LogicalPosition, ) { - if document.contains(Some(raw)) && style.get_property_value("display").unwrap() != "none" { + if document.contains(Some(raw)) && style.get("display") != "none" { position.x -= style_size_property(style, "margin-left") + style_size_property(style, "border-left-width") + style_size_property(style, "padding-left"); @@ -147,30 +138,21 @@ pub fn set_canvas_position( + style_size_property(style, "padding-top"); } - set_canvas_style_property(raw, "position", "fixed"); - set_canvas_style_property(raw, "left", &format!("{}px", position.x)); - set_canvas_style_property(raw, "top", &format!("{}px", position.y)); + style.set("position", "fixed"); + style.set("left", &format!("{}px", position.x)); + style.set("top", &format!("{}px", position.y)); } /// This function will panic if the element is not inserted in the DOM /// or is not a CSS property that represents a size in pixel. -pub fn style_size_property(style: &CssStyleDeclaration, property: &str) -> f64 { - let prop = style - .get_property_value(property) - .expect("Found invalid property"); +pub fn style_size_property(style: &Style, property: &str) -> f64 { + let prop = style.get(property); prop.strip_suffix("px") .expect("Element was not inserted into the DOM or is not a size in pixel") .parse() .expect("CSS property is not a size in pixel") } -pub fn set_canvas_style_property(raw: &HtmlCanvasElement, property: &str, value: &str) { - let style = raw.style(); - style - .set_property(property, value) - .unwrap_or_else(|err| panic!("error: {err:?}\nFailed to set {property}")) -} - pub fn is_dark_mode(window: &web_sys::Window) -> Option { window .match_media("(prefers-color-scheme: dark)") diff --git a/src/platform_impl/web/web_sys/resize_scaling.rs b/src/platform_impl/web/web_sys/resize_scaling.rs index 75776d210f..4506b0b317 100644 --- a/src/platform_impl/web/web_sys/resize_scaling.rs +++ b/src/platform_impl/web/web_sys/resize_scaling.rs @@ -2,14 +2,14 @@ use js_sys::{Array, Object}; use wasm_bindgen::prelude::{wasm_bindgen, Closure}; use wasm_bindgen::{JsCast, JsValue}; use web_sys::{ - CssStyleDeclaration, Document, HtmlCanvasElement, MediaQueryList, ResizeObserver, - ResizeObserverBoxOptions, ResizeObserverEntry, ResizeObserverOptions, ResizeObserverSize, - Window, + Document, HtmlCanvasElement, MediaQueryList, ResizeObserver, ResizeObserverBoxOptions, + ResizeObserverEntry, ResizeObserverOptions, ResizeObserverSize, Window, }; use crate::dpi::{LogicalSize, PhysicalSize}; use super::super::backend; +use super::canvas::Style; use super::media_query_handle::MediaQueryListHandle; use std::cell::{Cell, RefCell}; @@ -22,7 +22,7 @@ impl ResizeScaleHandle { window: Window, document: Document, canvas: HtmlCanvasElement, - style: CssStyleDeclaration, + style: Style, scale_handler: S, resize_handler: R, ) -> Self @@ -51,7 +51,7 @@ struct ResizeScaleInternal { window: Window, document: Document, canvas: HtmlCanvasElement, - style: CssStyleDeclaration, + style: Style, mql: MediaQueryListHandle, observer: ResizeObserver, _observer_closure: Closure, @@ -65,7 +65,7 @@ impl ResizeScaleInternal { window: Window, document: Document, canvas: HtmlCanvasElement, - style: CssStyleDeclaration, + style: Style, scale_handler: S, resize_handler: R, ) -> Rc> @@ -152,9 +152,7 @@ impl ResizeScaleInternal { } fn notify(&mut self) { - if !self.document.contains(Some(&self.canvas)) - || self.style.get_property_value("display").unwrap() == "none" - { + if !self.document.contains(Some(&self.canvas)) || self.style.get("display") == "none" { let size = PhysicalSize::new(0, 0); if self.notify_scale.replace(false) { @@ -180,7 +178,7 @@ impl ResizeScaleInternal { backend::style_size_property(&self.style, "height"), ); - if self.style.get_property_value("box-sizing").unwrap() == "border-box" { + if self.style.get("box-sizing") == "border-box" { size.width -= backend::style_size_property(&self.style, "border-left-width") + backend::style_size_property(&self.style, "border-right-width") + backend::style_size_property(&self.style, "padding-left") @@ -246,10 +244,7 @@ impl ResizeScaleInternal { .get(0) .unchecked_into(); - let writing_mode = self - .style - .get_property_value("writing-mode") - .expect("`writing-mode` is a valid CSS property"); + let writing_mode = self.style.get("writing-mode"); // means the canvas is not inserted into the DOM if writing_mode.is_empty() { diff --git a/src/platform_impl/web/window.rs b/src/platform_impl/web/window.rs index 7d54528702..6536bf42c0 100644 --- a/src/platform_impl/web/window.rs +++ b/src/platform_impl/web/window.rs @@ -196,7 +196,7 @@ impl Inner { #[inline] pub fn set_cursor_icon(&self, cursor: CursorIcon) { *self.previous_pointer.borrow_mut() = cursor.name(); - backend::set_canvas_style_property(self.canvas.borrow().raw(), "cursor", cursor.name()); + self.canvas.borrow().style().set("cursor", cursor.name()); } #[inline] @@ -223,13 +223,12 @@ impl Inner { #[inline] pub fn set_cursor_visible(&self, visible: bool) { if !visible { - backend::set_canvas_style_property(self.canvas.borrow().raw(), "cursor", "none"); + self.canvas.borrow().style().set("cursor", "none"); } else { - backend::set_canvas_style_property( - self.canvas.borrow().raw(), - "cursor", - &self.previous_pointer.borrow(), - ); + self.canvas + .borrow() + .style() + .set("cursor", &self.previous_pointer.borrow()); } } From 14140607d120e6cd832ccb72305f18ad042bbc95 Mon Sep 17 00:00:00 2001 From: Kirill Chibisov Date: Sat, 11 Nov 2023 20:35:30 +0400 Subject: [PATCH 06/15] On Wayland, fix wl_surface being dropped first The surface was automatically dropped due to new RAII type in SCTK when dropping the Window, which was not the case at some point with SCTK. Thus destroying objects associated with it where causing issues with some window managers. Links: https://github.com/neovide/neovide/issues/2109 --- CHANGELOG.md | 1 + .../linux/wayland/window/state.rs | 21 +++++++------------ 2 files changed, 8 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a0c8775bbf..541d954efe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ Unreleased` header. - On Windows, fix so `drag_window` and `drag_resize_window` can be called from another thread. - On Windows, fix `set_control_flow` in `AboutToWait` not being taken in account. - On macOS, send a `Resized` event after each `ScaleFactorChanged` event. +- On Wayland, fix `wl_surface` being destroyed before associated objects. # 0.29.3 diff --git a/src/platform_impl/linux/wayland/window/state.rs b/src/platform_impl/linux/wayland/window/state.rs index f9f3187efe..0b04ce932e 100644 --- a/src/platform_impl/linux/wayland/window/state.rs +++ b/src/platform_impl/linux/wayland/window/state.rs @@ -1,6 +1,5 @@ //! The state of the window, which is shared with the event-loop. -use std::mem::ManuallyDrop; use std::num::NonZeroU32; use std::sync::{Arc, Weak}; use std::time::Duration; @@ -55,9 +54,6 @@ pub struct WindowState { /// The connection to Wayland server. pub connection: Connection, - /// The underlying SCTK window. - pub window: ManuallyDrop, - /// The window frame, which is created from the configure request. frame: Option, @@ -149,6 +145,9 @@ pub struct WindowState { /// /// The value is the serial of the event triggered moved. has_pending_move: Option, + + /// The underlying SCTK window. + pub window: Window, } impl WindowState { @@ -206,7 +205,7 @@ impl WindowState { title: String::default(), transparent: false, viewport, - window: ManuallyDrop::new(window), + window, } } @@ -271,7 +270,7 @@ impl WindowState { && !self.csd_fails { match WinitFrame::new( - &*self.window, + &self.window, shm, subcompositor.clone(), self.queue_handle.clone(), @@ -1026,13 +1025,6 @@ impl WindowState { impl Drop for WindowState { fn drop(&mut self) { - let surface = self.window.wl_surface().clone(); - unsafe { - ManuallyDrop::drop(&mut self.window); - } - - // Cleanup objects. - if let Some(blur) = self.blur.take() { blur.release(); } @@ -1045,7 +1037,8 @@ impl Drop for WindowState { viewport.destroy(); } - surface.destroy(); + // NOTE: the wl_surface used by the window is being cleaned up when + // dropping SCTK `Window`. } } From 7bed5eecfdcbde16e5619fd137f0229e8e7e8ed4 Mon Sep 17 00:00:00 2001 From: Kirill Chibisov Date: Fri, 17 Nov 2023 15:56:03 +0400 Subject: [PATCH 07/15] On macOS, fix assertion when pressing `Fn` key --- CHANGELOG.md | 1 + src/platform_impl/macos/event.rs | 12 +-- src/platform_impl/macos/view.rs | 175 ++++++++++++++++--------------- 3 files changed, 96 insertions(+), 92 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 541d954efe..05ba2d40e6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ Unreleased` header. - On Windows, fix `set_control_flow` in `AboutToWait` not being taken in account. - On macOS, send a `Resized` event after each `ScaleFactorChanged` event. - On Wayland, fix `wl_surface` being destroyed before associated objects. +- On macOS, fix assertion when pressing `Fn` key. # 0.29.3 diff --git a/src/platform_impl/macos/event.rs b/src/platform_impl/macos/event.rs index af5af175a7..9c4a9a95c6 100644 --- a/src/platform_impl/macos/event.rs +++ b/src/platform_impl/macos/event.rs @@ -156,13 +156,11 @@ pub(crate) fn create_key_event( // Also not checking if this is a release event because then this issue would // still affect the key release. Some(text) if !has_ctrl => Key::Character(text.clone()), - _ => { - let modifierless_chars = match key_without_modifiers.as_ref() { - Key::Character(ch) => ch, - _ => "", - }; - get_logical_key_char(ns_event, modifierless_chars) - } + _ => match key_without_modifiers.as_ref() { + Key::Character(ch) => get_logical_key_char(ns_event, ch), + // Don't try to get text for events which likely don't have it. + _ => key_without_modifiers.clone(), + }, }; (logical_key, key_without_modifiers) diff --git a/src/platform_impl/macos/view.rs b/src/platform_impl/macos/view.rs index c5b5655761..bac77f9e2b 100644 --- a/src/platform_impl/macos/view.rs +++ b/src/platform_impl/macos/view.rs @@ -74,8 +74,8 @@ enum ImeState { bitflags! { #[derive(Debug, Clone, Copy, PartialEq)] struct ModLocationMask: u8 { - const LEFT = 1; - const RIGHT = 2; + const LEFT = 0b0001; + const RIGHT = 0b0010; } } impl ModLocationMask { @@ -88,13 +88,13 @@ impl ModLocationMask { } } -fn key_to_modifier(key: &Key) -> ModifiersState { +fn key_to_modifier(key: &Key) -> Option { match key { - Key::Named(NamedKey::Alt) => ModifiersState::ALT, - Key::Named(NamedKey::Control) => ModifiersState::CONTROL, - Key::Named(NamedKey::Super) => ModifiersState::SUPER, - Key::Named(NamedKey::Shift) => ModifiersState::SHIFT, - _ => unreachable!(), + Key::Named(NamedKey::Alt) => Some(ModifiersState::ALT), + Key::Named(NamedKey::Control) => Some(ModifiersState::CONTROL), + Key::Named(NamedKey::Super) => Some(ModifiersState::SUPER), + Key::Named(NamedKey::Shift) => Some(ModifiersState::SHIFT), + _ => None, } } @@ -924,91 +924,96 @@ impl WinitView { // event, thus we can't generate regular presses based on that. The `ModifiersChanged` // later will work though, since the flags are attached to the event and contain valid // information. - if is_flags_changed_event && ns_event.key_code() != 0 { - let scancode = ns_event.key_code(); - let physical_key = PhysicalKey::from_scancode(scancode as u32); - - // We'll correct the `is_press` later. - let mut event = create_key_event(ns_event, false, false, Some(physical_key)); - - let key = code_to_key(physical_key, scancode); - let event_modifier = key_to_modifier(&key); - event.physical_key = physical_key; - event.logical_key = key.clone(); - event.location = code_to_location(physical_key); - let location_mask = ModLocationMask::from_location(event.location); - - let mut phys_mod_state = self.state.phys_modifiers.borrow_mut(); - let phys_mod = phys_mod_state - .entry(key) - .or_insert(ModLocationMask::empty()); - - let is_active = current_modifiers.state().contains(event_modifier); - let mut events = VecDeque::with_capacity(2); - - // There is no API for getting whether the button was pressed or released - // during this event. For this reason we have to do a bit of magic below - // to come up with a good guess whether this key was pressed or released. - // (This is not trivial because there are multiple buttons that may affect - // the same modifier) - if !is_active { - event.state = Released; - if phys_mod.contains(ModLocationMask::LEFT) { - let mut event = event.clone(); - event.location = KeyLocation::Left; - event.physical_key = get_left_modifier_code(&event.logical_key).into(); - events.push_back(WindowEvent::KeyboardInput { - device_id: DEVICE_ID, - event, - is_synthetic: false, - }); - } - if phys_mod.contains(ModLocationMask::RIGHT) { - event.location = KeyLocation::Right; - event.physical_key = get_right_modifier_code(&event.logical_key).into(); + 'send_event: { + if is_flags_changed_event && ns_event.key_code() != 0 { + let scancode = ns_event.key_code(); + let physical_key = PhysicalKey::from_scancode(scancode as u32); + + // We'll correct the `is_press` later. + let mut event = create_key_event(ns_event, false, false, Some(physical_key)); + + let key = code_to_key(physical_key, scancode); + // Ignore processing of unkown modifiers because we can't determine whether + // it was pressed or release reliably. + let Some(event_modifier) = key_to_modifier(&key) else { + break 'send_event; + }; + event.physical_key = physical_key; + event.logical_key = key.clone(); + event.location = code_to_location(physical_key); + let location_mask = ModLocationMask::from_location(event.location); + + let mut phys_mod_state = self.state.phys_modifiers.borrow_mut(); + let phys_mod = phys_mod_state + .entry(key) + .or_insert(ModLocationMask::empty()); + + let is_active = current_modifiers.state().contains(event_modifier); + let mut events = VecDeque::with_capacity(2); + + // There is no API for getting whether the button was pressed or released + // during this event. For this reason we have to do a bit of magic below + // to come up with a good guess whether this key was pressed or released. + // (This is not trivial because there are multiple buttons that may affect + // the same modifier) + if !is_active { + event.state = Released; + if phys_mod.contains(ModLocationMask::LEFT) { + let mut event = event.clone(); + event.location = KeyLocation::Left; + event.physical_key = get_left_modifier_code(&event.logical_key).into(); + events.push_back(WindowEvent::KeyboardInput { + device_id: DEVICE_ID, + event, + is_synthetic: false, + }); + } + if phys_mod.contains(ModLocationMask::RIGHT) { + event.location = KeyLocation::Right; + event.physical_key = get_right_modifier_code(&event.logical_key).into(); + events.push_back(WindowEvent::KeyboardInput { + device_id: DEVICE_ID, + event, + is_synthetic: false, + }); + } + *phys_mod = ModLocationMask::empty(); + } else { + if *phys_mod == location_mask { + // Here we hit a contradiction: + // The modifier state was "changed" to active, + // yet the only pressed modifier key was the one that we + // just got a change event for. + // This seemingly means that the only pressed modifier is now released, + // but at the same time the modifier became active. + // + // But this scenario is possible if we released modifiers + // while the application was not in focus. (Because we don't + // get informed of modifier key events while the application + // is not focused) + + // In this case we prioritize the information + // about the current modifier state which means + // that the button was pressed. + event.state = Pressed; + } else { + phys_mod.toggle(location_mask); + let is_pressed = phys_mod.contains(location_mask); + event.state = if is_pressed { Pressed } else { Released }; + } + events.push_back(WindowEvent::KeyboardInput { device_id: DEVICE_ID, event, is_synthetic: false, }); } - *phys_mod = ModLocationMask::empty(); - } else { - // is_active - if *phys_mod == location_mask { - // Here we hit a contradiction: - // The modifier state was "changed" to active, - // yet the only pressed modifier key was the one that we - // just got a change event for. - // This seemingly means that the only pressed modifier is now released, - // but at the same time the modifier became active. - // - // But this scenario is possible if we released modifiers - // while the application was not in focus. (Because we don't - // get informed of modifier key events while the application - // is not focused) - - // In this case we prioritize the information - // about the current modifier state which means - // that the button was pressed. - event.state = Pressed; - } else { - phys_mod.toggle(location_mask); - let is_pressed = phys_mod.contains(location_mask); - event.state = if is_pressed { Pressed } else { Released }; - } - events.push_back(WindowEvent::KeyboardInput { - device_id: DEVICE_ID, - event, - is_synthetic: false, - }); - } + drop(phys_mod_state); - drop(phys_mod_state); - - for event in events { - self.queue_event(event); + for event in events { + self.queue_event(event); + } } } From d3ca685b7763fac10c5ae2db7339adb136585e92 Mon Sep 17 00:00:00 2001 From: Arend van Beelen jr Date: Wed, 22 Nov 2023 13:14:51 +0100 Subject: [PATCH 08/15] Fix crash when running iPad build on macOS --- CHANGELOG.md | 1 + src/platform_impl/ios/app_state.rs | 14 +++++++++++--- src/platform_impl/ios/event_loop.rs | 4 ++-- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 05ba2d40e6..7993a0003f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ Unreleased` header. # Unreleased +- Fix crash when running iOS app on macOS. - On X11, check common alternative cursor names when loading cursor. - On Windows, fix so `drag_window` and `drag_resize_window` can be called from another thread. - On Windows, fix `set_control_flow` in `AboutToWait` not being taken in account. diff --git a/src/platform_impl/ios/app_state.rs b/src/platform_impl/ios/app_state.rs index 5a4a9cb25b..b486c2f624 100644 --- a/src/platform_impl/ios/app_state.rs +++ b/src/platform_impl/ios/app_state.rs @@ -200,6 +200,10 @@ impl AppState { ) } + fn has_terminated(&self) -> bool { + matches!(self.state(), AppStateImpl::Terminated) + } + fn will_launch_transition(&mut self, queued_event_handler: Box) { let (queued_windows, queued_events, queued_gpu_redraws) = match self.take_state() { AppStateImpl::NotLaunched { @@ -243,7 +247,7 @@ impl AppState { fn wakeup_transition(&mut self) -> Option { // before `AppState::did_finish_launching` is called, pretend there is no running // event loop. - if !self.has_launched() { + if !self.has_launched() || self.has_terminated() { return None; } @@ -390,7 +394,7 @@ impl AppState { } fn events_cleared_transition(&mut self) { - if !self.has_launched() { + if !self.has_launched() || self.has_terminated() { return; } let (waiting_event_handler, old) = match self.take_state() { @@ -586,6 +590,10 @@ pub(crate) fn handle_nonuser_events>( events: I, ) { let mut this = AppState::get_mut(mtm); + if this.has_terminated() { + return; + } + let (mut event_handler, active_control_flow, processing_redraws) = match this.try_user_callback_transition() { UserCallbackTransitionResult::ReentrancyPrevented { queued_events } => { @@ -737,7 +745,7 @@ fn handle_user_events(mtm: MainThreadMarker) { pub fn handle_main_events_cleared(mtm: MainThreadMarker) { let mut this = AppState::get_mut(mtm); - if !this.has_launched() { + if !this.has_launched() || this.has_terminated() { return; } match this.state_mut() { diff --git a/src/platform_impl/ios/event_loop.rs b/src/platform_impl/ios/event_loop.rs index dab69606ee..a260f78aec 100644 --- a/src/platform_impl/ios/event_loop.rs +++ b/src/platform_impl/ios/event_loop.rs @@ -289,7 +289,7 @@ fn setup_control_flow_observers() { #[allow(non_upper_case_globals)] match activity { kCFRunLoopBeforeWaiting => app_state::handle_main_events_cleared(mtm), - kCFRunLoopExit => unimplemented!(), // not expected to ever happen + kCFRunLoopExit => {} // may happen when running on macOS _ => unreachable!(), } } @@ -304,7 +304,7 @@ fn setup_control_flow_observers() { #[allow(non_upper_case_globals)] match activity { kCFRunLoopBeforeWaiting => app_state::handle_events_cleared(mtm), - kCFRunLoopExit => unimplemented!(), // not expected to ever happen + kCFRunLoopExit => {} // may happen when running on macOS _ => unreachable!(), } } From 5612626944469623a8d21469357bf0ab69a6ca73 Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Thu, 23 Nov 2023 08:18:05 +0100 Subject: [PATCH 09/15] Make Android docs build on docs.rs (#3236) --- Cargo.toml | 9 ++++++++- src/platform/android.rs | 5 +++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 64c1f6e3b9..d20d049a22 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,7 +13,14 @@ categories = ["gui"] rust-version = "1.65.0" [package.metadata.docs.rs] -features = ["rwh_04", "rwh_05", "rwh_06", "serde"] +features = [ + "rwh_04", + "rwh_05", + "rwh_06", + "serde", + # Enabled to get docs to compile + "android-native-activity", +] default-target = "x86_64-unknown-linux-gnu" # These are all tested in CI targets = [ diff --git a/src/platform/android.rs b/src/platform/android.rs index cb0528ef35..b5fde4ad49 100644 --- a/src/platform/android.rs +++ b/src/platform/android.rs @@ -84,5 +84,10 @@ impl EventLoopBuilderExtAndroid for EventLoopBuilder { /// use winit::platform::android::activity::AndroidApp; /// ``` pub mod activity { + // We enable the `"native-activity"` feature just so that we can build the + // docs, but it'll be very confusing for users to see the docs with that + // feature enabled, so we avoid inlining it so that they're forced to view + // it on the crate's own docs.rs page. + #[doc(no_inline)] pub use android_activity::*; } From 81a1d9c3960fb8cdd837198aed8549d96ac8e96b Mon Sep 17 00:00:00 2001 From: Kirill Chibisov Date: Thu, 23 Nov 2023 22:15:17 +0400 Subject: [PATCH 10/15] Fix infinite recursion in `BadIcon` reporting (#3237) --- src/icon.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/icon.rs b/src/icon.rs index 2927665935..b61cdd3b7f 100644 --- a/src/icon.rs +++ b/src/icon.rs @@ -49,11 +49,7 @@ impl fmt::Display for BadIcon { } } -impl Error for BadIcon { - fn source(&self) -> Option<&(dyn Error + 'static)> { - Some(self) - } -} +impl Error for BadIcon {} #[derive(Debug, Clone, PartialEq, Eq)] pub(crate) struct RgbaIcon { From b3c87caa7c040111d8dcae764fe903f54df77426 Mon Sep 17 00:00:00 2001 From: John Nunley Date: Fri, 24 Nov 2023 00:14:06 -0800 Subject: [PATCH 11/15] On X11, reload DPI on PropertyChange Signed-off-by: John Nunley Fixes #1228. --- CHANGELOG.md | 1 + .../linux/x11/event_processor.rs | 114 ++++++++---------- src/platform_impl/linux/x11/window.rs | 49 +++++++- 3 files changed, 97 insertions(+), 67 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7993a0003f..df94dffedc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ Unreleased` header. - Fix crash when running iOS app on macOS. - On X11, check common alternative cursor names when loading cursor. +- On X11, reload the DPI after a property change event. - On Windows, fix so `drag_window` and `drag_resize_window` can be called from another thread. - On Windows, fix `set_control_flow` in `AboutToWait` not being taken in account. - On macOS, send a `Resized` event after each `ScaleFactorChanged` event. diff --git a/src/platform_impl/linux/x11/event_processor.rs b/src/platform_impl/linux/x11/event_processor.rs index 3e3c0f5e91..6395d35828 100644 --- a/src/platform_impl/linux/x11/event_processor.rs +++ b/src/platform_impl/linux/x11/event_processor.rs @@ -570,6 +570,14 @@ impl EventProcessor { event: WindowEvent::Destroyed, }); } + ffi::PropertyNotify => { + let xev: &ffi::XPropertyEvent = xev.as_ref(); + let atom = xev.atom as xproto::Atom; + + if atom == xproto::Atom::from(xproto::AtomEnum::RESOURCE_MANAGER) { + self.process_dpi_change(&mut callback); + } + } ffi::VisibilityNotify => { let xev: &ffi::XVisibilityEvent = xev.as_ref(); @@ -1306,72 +1314,7 @@ impl EventProcessor { } } if event_type == self.randr_event_offset as c_int { - // In the future, it would be quite easy to emit monitor hotplug events. - let prev_list = wt.xconn.invalidate_cached_monitor_list(); - if let Some(prev_list) = prev_list { - let new_list = wt - .xconn - .available_monitors() - .expect("Failed to get monitor list"); - for new_monitor in new_list { - // Previous list may be empty, in case of disconnecting and - // reconnecting the only one monitor. We still need to emit events in - // this case. - let maybe_prev_scale_factor = prev_list - .iter() - .find(|prev_monitor| prev_monitor.name == new_monitor.name) - .map(|prev_monitor| prev_monitor.scale_factor); - if Some(new_monitor.scale_factor) != maybe_prev_scale_factor { - for (window_id, window) in wt.windows.borrow().iter() { - if let Some(window) = window.upgrade() { - // Check if the window is on this monitor - let monitor = - window.shared_state_lock().last_monitor.clone(); - if monitor.name == new_monitor.name { - let (width, height) = window.inner_size_physical(); - let (new_width, new_height) = window.adjust_for_dpi( - // If we couldn't determine the previous scale - // factor (e.g., because all monitors were closed - // before), just pick whatever the current monitor - // has set as a baseline. - maybe_prev_scale_factor - .unwrap_or(monitor.scale_factor), - new_monitor.scale_factor, - width, - height, - &window.shared_state_lock(), - ); - - let window_id = crate::window::WindowId(*window_id); - let old_inner_size = PhysicalSize::new(width, height); - let inner_size = Arc::new(Mutex::new( - PhysicalSize::new(new_width, new_height), - )); - callback(Event::WindowEvent { - window_id, - event: WindowEvent::ScaleFactorChanged { - scale_factor: new_monitor.scale_factor, - inner_size_writer: InnerSizeWriter::new( - Arc::downgrade(&inner_size), - ), - }, - }); - - let new_inner_size = *inner_size.lock().unwrap(); - drop(inner_size); - - if new_inner_size != old_inner_size { - let (new_width, new_height) = new_inner_size.into(); - window.request_inner_size_physical( - new_width, new_height, - ); - } - } - } - } - } - } - } + self.process_dpi_change(&mut callback); } } } @@ -1464,6 +1407,45 @@ impl EventProcessor { }); } } + + fn process_dpi_change(&self, callback: &mut F) + where + F: FnMut(Event), + { + let wt = get_xtarget(&self.target); + + // In the future, it would be quite easy to emit monitor hotplug events. + let prev_list = { + let prev_list = wt.xconn.invalidate_cached_monitor_list(); + match prev_list { + Some(prev_list) => prev_list, + None => return, + } + }; + + let new_list = wt + .xconn + .available_monitors() + .expect("Failed to get monitor list"); + for new_monitor in new_list { + // Previous list may be empty, in case of disconnecting and + // reconnecting the only one monitor. We still need to emit events in + // this case. + let maybe_prev_scale_factor = prev_list + .iter() + .find(|prev_monitor| prev_monitor.name == new_monitor.name) + .map(|prev_monitor| prev_monitor.scale_factor); + if Some(new_monitor.scale_factor) != maybe_prev_scale_factor { + for window in wt.windows.borrow().iter().filter_map(|(_, w)| w.upgrade()) { + window.refresh_dpi_for_monitor( + &new_monitor, + maybe_prev_scale_factor, + &mut *callback, + ) + } + } + } + } } fn is_first_touch(first: &mut Option, num: &mut u32, id: u64, phase: TouchPhase) -> bool { diff --git a/src/platform_impl/linux/x11/window.rs b/src/platform_impl/linux/x11/window.rs index 1b08869f5f..b2e45bd91c 100644 --- a/src/platform_impl/linux/x11/window.rs +++ b/src/platform_impl/linux/x11/window.rs @@ -22,6 +22,7 @@ use x11rb::{ use crate::{ dpi::{PhysicalPosition, PhysicalSize, Position, Size}, error::{ExternalError, NotSupportedError, OsError as RootOsError}, + event::{Event, InnerSizeWriter, WindowEvent}, event_loop::AsyncRequestSerial, platform_impl::{ x11::{atoms::*, MonitorHandle as X11MonitorHandle, WakeSender, X11Error}, @@ -276,7 +277,8 @@ impl UnownedWindow { | EventMask::KEYMAP_STATE | EventMask::BUTTON_PRESS | EventMask::BUTTON_RELEASE - | EventMask::POINTER_MOTION; + | EventMask::POINTER_MOTION + | EventMask::PROPERTY_CHANGE; aux = aux.event_mask(event_mask).border_pixel(0); @@ -923,6 +925,51 @@ impl UnownedWindow { }) } + /// Refresh the API for the given monitor. + #[inline] + pub(super) fn refresh_dpi_for_monitor( + &self, + new_monitor: &X11MonitorHandle, + maybe_prev_scale_factor: Option, + mut callback: impl FnMut(Event), + ) { + // Check if the self is on this monitor + let monitor = self.shared_state_lock().last_monitor.clone(); + if monitor.name == new_monitor.name { + let (width, height) = self.inner_size_physical(); + let (new_width, new_height) = self.adjust_for_dpi( + // If we couldn't determine the previous scale + // factor (e.g., because all monitors were closed + // before), just pick whatever the current monitor + // has set as a baseline. + maybe_prev_scale_factor.unwrap_or(monitor.scale_factor), + new_monitor.scale_factor, + width, + height, + &self.shared_state_lock(), + ); + + let window_id = crate::window::WindowId(self.id()); + let old_inner_size = PhysicalSize::new(width, height); + let inner_size = Arc::new(Mutex::new(PhysicalSize::new(new_width, new_height))); + callback(Event::WindowEvent { + window_id, + event: WindowEvent::ScaleFactorChanged { + scale_factor: new_monitor.scale_factor, + inner_size_writer: InnerSizeWriter::new(Arc::downgrade(&inner_size)), + }, + }); + + let new_inner_size = *inner_size.lock().unwrap(); + drop(inner_size); + + if new_inner_size != old_inner_size { + let (new_width, new_height) = new_inner_size.into(); + self.request_inner_size_physical(new_width, new_height); + } + } + } + fn set_minimized_inner(&self, minimized: bool) -> Result, X11Error> { let atoms = self.xconn.atoms(); From 17296e98783a19c2f0a552340682e3de2b863de8 Mon Sep 17 00:00:00 2001 From: Kirill Chibisov Date: Fri, 24 Nov 2023 18:32:53 +0400 Subject: [PATCH 12/15] Bump version on master This commit does not represent a release and only synchronizes CHANGELOG from the latest release. --- CHANGELOG.md | 2 ++ Cargo.toml | 2 +- README.md | 4 ++-- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index df94dffedc..d51fcaeb41 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,8 @@ Unreleased` header. # Unreleased +# 0.29.4 + - Fix crash when running iOS app on macOS. - On X11, check common alternative cursor names when loading cursor. - On X11, reload the DPI after a property change event. diff --git a/Cargo.toml b/Cargo.toml index d20d049a22..a08492af68 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "winit" -version = "0.29.3" +version = "0.29.4" authors = ["The winit contributors", "Pierre Krieger "] description = "Cross-platform window creation library." edition = "2021" diff --git a/README.md b/README.md index 165ce8636f..75abb89d08 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ ```toml [dependencies] -winit = "0.29.3" +winit = "0.29.4" ``` ## [Documentation](https://docs.rs/winit) @@ -157,7 +157,7 @@ For more details, refer to these `android-activity` [example applications](https If your application is currently based on `NativeActivity` via the `ndk-glue` crate and building with `cargo apk` then the minimal changes would be: 1. Remove `ndk-glue` from your `Cargo.toml` -2. Enable the `"android-native-activity"` feature for Winit: `winit = { version = "0.29.3", features = [ "android-native-activity" ] }` +2. Enable the `"android-native-activity"` feature for Winit: `winit = { version = "0.29.4", features = [ "android-native-activity" ] }` 3. Add an `android_main` entrypoint (as above), instead of using the '`[ndk_glue::main]` proc macro from `ndk-macros` (optionally add a dependency on `android_logger` and initialize logging as above). 4. Pass a clone of the `AndroidApp` that your application receives to Winit when building your event loop (as shown above). From a7241b3db33ecbc07aa1a056257d646ae1fef986 Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Tue, 28 Nov 2023 20:19:16 +0100 Subject: [PATCH 13/15] On macOS, remove spurious error logging when handling `Fn` Fixes #3246. --- CHANGELOG.md | 2 ++ src/platform_impl/macos/event.rs | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d51fcaeb41..5df8dabd56 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,8 @@ Unreleased` header. # Unreleased +- On macOS, remove spurious error logging when handling `Fn`. + # 0.29.4 - Fix crash when running iOS app on macOS. diff --git a/src/platform_impl/macos/event.rs b/src/platform_impl/macos/event.rs index 9c4a9a95c6..cdd79297ec 100644 --- a/src/platform_impl/macos/event.rs +++ b/src/platform_impl/macos/event.rs @@ -88,7 +88,9 @@ pub fn get_modifierless_char(scancode: u16) -> Key { return Key::Unidentified(NativeKey::MacOS(scancode)); } if result_len == 0 { - log::error!("`UCKeyTranslate` was succesful but gave a string of 0 length."); + // This is fine - not all keys have text representation. + // For instance, users that have mapped the `Fn` key to toggle + // keyboard layouts will hit this code path. return Key::Unidentified(NativeKey::MacOS(scancode)); } let chars = String::from_utf16_lossy(&string[0..result_len as usize]); From 075996b1fa98c7c7eed1a447cb89022c1f72d629 Mon Sep 17 00:00:00 2001 From: OG Date: Tue, 28 Nov 2023 23:39:12 +0300 Subject: [PATCH 14/15] feat: macos services menu added (#3231) --- CHANGELOG.md | 1 + src/platform_impl/macos/appkit/application.rs | 3 +++ src/platform_impl/macos/appkit/menu_item.rs | 6 ++++- src/platform_impl/macos/menu.rs | 27 ++++++++++++++----- 4 files changed, 30 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5df8dabd56..415f989bb6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ Unreleased` header. # Unreleased +- On macOS, add services menu. - On macOS, remove spurious error logging when handling `Fn`. # 0.29.4 diff --git a/src/platform_impl/macos/appkit/application.rs b/src/platform_impl/macos/appkit/application.rs index f5f2e77d14..22d166f261 100644 --- a/src/platform_impl/macos/appkit/application.rs +++ b/src/platform_impl/macos/appkit/application.rs @@ -80,6 +80,9 @@ extern_methods!( #[method(setMainMenu:)] pub fn setMainMenu(&self, menu: &NSMenu); + #[method(setServicesMenu:)] + pub fn setServicesMenu(&self, menu: &NSMenu); + #[method_id(effectiveAppearance)] pub fn effectiveAppearance(&self) -> Id; diff --git a/src/platform_impl/macos/appkit/menu_item.rs b/src/platform_impl/macos/appkit/menu_item.rs index 63c9a501cc..c406529139 100644 --- a/src/platform_impl/macos/appkit/menu_item.rs +++ b/src/platform_impl/macos/appkit/menu_item.rs @@ -20,7 +20,11 @@ extern_methods!( #[method_id(new)] pub fn new() -> Id; - pub fn newWithTitle(title: &NSString, action: Sel, key_equivalent: &NSString) -> Id { + pub fn newWithTitle( + title: &NSString, + action: Option, + key_equivalent: &NSString, + ) -> Id { unsafe { msg_send_id![ Self::alloc(), diff --git a/src/platform_impl/macos/menu.rs b/src/platform_impl/macos/menu.rs index 8e69cbbb26..978e0bff5a 100644 --- a/src/platform_impl/macos/menu.rs +++ b/src/platform_impl/macos/menu.rs @@ -21,7 +21,16 @@ pub fn initialize() { // About menu item let about_item_title = ns_string!("About ").stringByAppendingString(&process_name); - let about_item = menu_item(&about_item_title, sel!(orderFrontStandardAboutPanel:), None); + let about_item = menu_item( + &about_item_title, + Some(sel!(orderFrontStandardAboutPanel:)), + None, + ); + + // Services menu item + let services_menu = NSMenu::new(); + let services_item = menu_item(ns_string!("Services"), None, None); + services_item.setSubmenu(&services_menu); // Seperator menu item let sep_first = NSMenuItem::separatorItem(); @@ -30,7 +39,7 @@ pub fn initialize() { let hide_item_title = ns_string!("Hide ").stringByAppendingString(&process_name); let hide_item = menu_item( &hide_item_title, - sel!(hide:), + Some(sel!(hide:)), Some(KeyEquivalent { key: ns_string!("h"), masks: None, @@ -41,7 +50,7 @@ pub fn initialize() { let hide_others_item_title = ns_string!("Hide Others"); let hide_others_item = menu_item( hide_others_item_title, - sel!(hideOtherApplications:), + Some(sel!(hideOtherApplications:)), Some(KeyEquivalent { key: ns_string!("h"), masks: Some( @@ -52,7 +61,11 @@ pub fn initialize() { // Show applications menu item let show_all_item_title = ns_string!("Show All"); - let show_all_item = menu_item(show_all_item_title, sel!(unhideAllApplications:), None); + let show_all_item = menu_item( + show_all_item_title, + Some(sel!(unhideAllApplications:)), + None, + ); // Seperator menu item let sep = NSMenuItem::separatorItem(); @@ -61,7 +74,7 @@ pub fn initialize() { let quit_item_title = ns_string!("Quit ").stringByAppendingString(&process_name); let quit_item = menu_item( &quit_item_title, - sel!(terminate:), + Some(sel!(terminate:)), Some(KeyEquivalent { key: ns_string!("q"), masks: None, @@ -70,6 +83,7 @@ pub fn initialize() { app_menu.addItem(&about_item); app_menu.addItem(&sep_first); + app_menu.addItem(&services_item); app_menu.addItem(&hide_item); app_menu.addItem(&hide_others_item); app_menu.addItem(&show_all_item); @@ -78,12 +92,13 @@ pub fn initialize() { app_menu_item.setSubmenu(&app_menu); let app = NSApp(); + app.setServicesMenu(&services_menu); app.setMainMenu(&menubar); } fn menu_item( title: &NSString, - selector: Sel, + selector: Option, key_equivalent: Option>, ) -> Id { let (key, masks) = match key_equivalent { From 0be2bb0a8c3d9f261b45faaa4056b5bb060333d6 Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Tue, 28 Nov 2023 21:56:11 +0100 Subject: [PATCH 15/15] Remove unused .gitmodules --- .gitmodules | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 .gitmodules diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index 9656dc7882..0000000000 --- a/.gitmodules +++ /dev/null @@ -1,3 +0,0 @@ -[submodule "deps/apk-builder"] - path = deps/apk-builder - url = https://github.com/rust-windowing/android-rs-glue