diff --git a/CHANGELOG.md b/CHANGELOG.md index f4cd72ed..173f17c5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ - Officially remove the Emscripten backend. - On Windows, fix handling of surrogate pairs when dispatching `ReceivedCharacter`. - On macOS 10.15, fix freeze upon exiting exclusive fullscreen mode. +- On iOS, fix null window on initial `HiDpiFactorChanged` event. # 0.20.0 Alpha 3 (2019-08-14) diff --git a/src/platform_impl/ios/view.rs b/src/platform_impl/ios/view.rs index 8c6ba1e7..5868059b 100644 --- a/src/platform_impl/ios/view.rs +++ b/src/platform_impl/ios/view.rs @@ -101,6 +101,7 @@ unsafe fn get_view_class(root_view_class: &'static Class) -> &'static Class { extern "C" fn draw_rect(object: &Object, _: Sel, rect: CGRect) { unsafe { let window: id = msg_send![object, window]; + assert!(!window.is_null()); app_state::handle_nonuser_event(Event::WindowEvent { window_id: RootWindowId(window.into()), event: WindowEvent::RedrawRequested, @@ -116,6 +117,7 @@ unsafe fn get_view_class(root_view_class: &'static Class) -> &'static Class { let () = msg_send![super(object, superclass), layoutSubviews]; let window: id = msg_send![object, window]; + assert!(!window.is_null()); let bounds: CGRect = msg_send![window, bounds]; let screen: id = msg_send![window, screen]; let screen_space: id = msg_send![screen, coordinateSpace]; @@ -144,23 +146,24 @@ unsafe fn get_view_class(root_view_class: &'static Class) -> &'static Class { setContentScaleFactor: untrusted_hidpi_factor ]; - // On launch, iOS sets the contentScaleFactor to 0.0. This is a sentinel value that - // iOS appears to use to "reset" the contentScaleFactor to the device specific - // default value. - // - // The workaround is to not trust the value received by this function, and always - // go through the getter. + let window: id = msg_send![object, window]; + // `window` is null when `setContentScaleFactor` is invoked prior to `[UIWindow + // makeKeyAndVisible]` at window creation time (either manually or internally by + // UIKit when the `UIView` is first created), in which case we send no events here + if window.is_null() { + return; + } + // `setContentScaleFactor` may be called with a value of 0, which means "reset the + // content scale factor to a device-specific default value", so we can't use the + // parameter here. We can query the actual factor using the getter let hidpi_factor: CGFloat = msg_send![object, contentScaleFactor]; assert!( !hidpi_factor.is_nan() && hidpi_factor.is_finite() && hidpi_factor.is_sign_positive() && hidpi_factor > 0.0, - "invalid hidpi_factor set on UIWindow", + "invalid hidpi_factor set on UIView", ); - - let window: id = msg_send![object, window]; - let bounds: CGRect = msg_send![object, bounds]; let screen: id = msg_send![window, screen]; let screen_space: id = msg_send![screen, coordinateSpace]; @@ -186,6 +189,7 @@ unsafe fn get_view_class(root_view_class: &'static Class) -> &'static Class { extern "C" fn handle_touches(object: &Object, _: Sel, touches: id, _: id) { unsafe { let window: id = msg_send![object, window]; + assert!(!window.is_null()); let uiscreen: id = msg_send![window, screen]; let touches_enum: id = msg_send![touches, objectEnumerator]; let mut touch_events = Vec::new(); diff --git a/src/platform_impl/ios/window.rs b/src/platform_impl/ios/window.rs index 0b609c60..eea0c304 100644 --- a/src/platform_impl/ios/window.rs +++ b/src/platform_impl/ios/window.rs @@ -9,6 +9,7 @@ use objc::runtime::{Class, Object, BOOL, NO, YES}; use crate::{ dpi::{self, LogicalPosition, LogicalSize}, error::{ExternalError, NotSupportedError, OsError as RootOsError}, + event::{Event, WindowEvent}, icon::Icon, monitor::MonitorHandle as RootMonitorHandle, platform::ios::{MonitorHandleExtIOS, ScreenEdge, ValidOrientations}, @@ -20,7 +21,7 @@ use crate::{ }, monitor, view, EventLoopWindowTarget, MonitorHandle, }, - window::{CursorIcon, Fullscreen, WindowAttributes}, + window::{CursorIcon, Fullscreen, WindowAttributes, WindowId as RootWindowId}, }; pub struct Inner { @@ -377,6 +378,32 @@ impl Window { }, }; app_state::set_key_window(window); + + // Like the Windows and macOS backends, we send a `HiDpiFactorChanged` and `Resized` + // event on window creation if the DPI factor != 1.0 + let hidpi_factor: CGFloat = msg_send![view, contentScaleFactor]; + if hidpi_factor != 1.0 { + let bounds: CGRect = msg_send![view, bounds]; + let screen: id = msg_send![window, screen]; + let screen_space: id = msg_send![screen, coordinateSpace]; + let screen_frame: CGRect = + msg_send![view, convertRect:bounds toCoordinateSpace:screen_space]; + let size = crate::dpi::LogicalSize { + width: screen_frame.size.width as _, + height: screen_frame.size.height as _, + }; + app_state::handle_nonuser_events( + std::iter::once(Event::WindowEvent { + window_id: RootWindowId(window.into()), + event: WindowEvent::HiDpiFactorChanged(hidpi_factor as _), + }) + .chain(std::iter::once(Event::WindowEvent { + window_id: RootWindowId(window.into()), + event: WindowEvent::Resized(size), + })), + ); + } + Ok(result) } }