Fix null window on initial HiDpiFactorChanged event on iOS (#1167)

This commit is contained in:
Aleksi Juvani 2019-09-16 21:27:46 +03:00 committed by Osspial
parent 3716f13d8e
commit 95581ab92f
3 changed files with 43 additions and 11 deletions

View file

@ -11,6 +11,7 @@
- Officially remove the Emscripten backend. - Officially remove the Emscripten backend.
- On Windows, fix handling of surrogate pairs when dispatching `ReceivedCharacter`. - On Windows, fix handling of surrogate pairs when dispatching `ReceivedCharacter`.
- On macOS 10.15, fix freeze upon exiting exclusive fullscreen mode. - 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) # 0.20.0 Alpha 3 (2019-08-14)

View file

@ -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) { extern "C" fn draw_rect(object: &Object, _: Sel, rect: CGRect) {
unsafe { unsafe {
let window: id = msg_send![object, window]; let window: id = msg_send![object, window];
assert!(!window.is_null());
app_state::handle_nonuser_event(Event::WindowEvent { app_state::handle_nonuser_event(Event::WindowEvent {
window_id: RootWindowId(window.into()), window_id: RootWindowId(window.into()),
event: WindowEvent::RedrawRequested, 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 () = msg_send![super(object, superclass), layoutSubviews];
let window: id = msg_send![object, window]; let window: id = msg_send![object, window];
assert!(!window.is_null());
let bounds: CGRect = msg_send![window, bounds]; let bounds: CGRect = msg_send![window, bounds];
let screen: id = msg_send![window, screen]; let screen: id = msg_send![window, screen];
let screen_space: id = msg_send![screen, coordinateSpace]; 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 setContentScaleFactor: untrusted_hidpi_factor
]; ];
// On launch, iOS sets the contentScaleFactor to 0.0. This is a sentinel value that let window: id = msg_send![object, window];
// iOS appears to use to "reset" the contentScaleFactor to the device specific // `window` is null when `setContentScaleFactor` is invoked prior to `[UIWindow
// default value. // 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
// The workaround is to not trust the value received by this function, and always if window.is_null() {
// go through the getter. 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]; let hidpi_factor: CGFloat = msg_send![object, contentScaleFactor];
assert!( assert!(
!hidpi_factor.is_nan() !hidpi_factor.is_nan()
&& hidpi_factor.is_finite() && hidpi_factor.is_finite()
&& hidpi_factor.is_sign_positive() && hidpi_factor.is_sign_positive()
&& hidpi_factor > 0.0, && 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 bounds: CGRect = msg_send![object, bounds];
let screen: id = msg_send![window, screen]; let screen: id = msg_send![window, screen];
let screen_space: id = msg_send![screen, coordinateSpace]; 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) { extern "C" fn handle_touches(object: &Object, _: Sel, touches: id, _: id) {
unsafe { unsafe {
let window: id = msg_send![object, window]; let window: id = msg_send![object, window];
assert!(!window.is_null());
let uiscreen: id = msg_send![window, screen]; let uiscreen: id = msg_send![window, screen];
let touches_enum: id = msg_send![touches, objectEnumerator]; let touches_enum: id = msg_send![touches, objectEnumerator];
let mut touch_events = Vec::new(); let mut touch_events = Vec::new();

View file

@ -9,6 +9,7 @@ use objc::runtime::{Class, Object, BOOL, NO, YES};
use crate::{ use crate::{
dpi::{self, LogicalPosition, LogicalSize}, dpi::{self, LogicalPosition, LogicalSize},
error::{ExternalError, NotSupportedError, OsError as RootOsError}, error::{ExternalError, NotSupportedError, OsError as RootOsError},
event::{Event, WindowEvent},
icon::Icon, icon::Icon,
monitor::MonitorHandle as RootMonitorHandle, monitor::MonitorHandle as RootMonitorHandle,
platform::ios::{MonitorHandleExtIOS, ScreenEdge, ValidOrientations}, platform::ios::{MonitorHandleExtIOS, ScreenEdge, ValidOrientations},
@ -20,7 +21,7 @@ use crate::{
}, },
monitor, view, EventLoopWindowTarget, MonitorHandle, monitor, view, EventLoopWindowTarget, MonitorHandle,
}, },
window::{CursorIcon, Fullscreen, WindowAttributes}, window::{CursorIcon, Fullscreen, WindowAttributes, WindowId as RootWindowId},
}; };
pub struct Inner { pub struct Inner {
@ -377,6 +378,32 @@ impl Window {
}, },
}; };
app_state::set_key_window(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) Ok(result)
} }
} }