-
Notifications
You must be signed in to change notification settings - Fork 950
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Defer window creation until UIApplicationMain
on iOS
#1121
Conversation
r? @mtak- |
Looking into this now. There's a lot of other changes here. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for working on this. I know it was not a small task.
I wonder if we should just panic during window creation if +[UIApplication sharedApplication] == nil
. Basically nothing in UIKit is guaranteed to work correctly before UIApplicationMain
has been called. In iOS 14 getting the mainScreen
before UIApplicationMain
could well break. We're trying to fight the platform too much IMO. All windows can just be created during or after StartCause::Init
. This is what our code base naturally settled upon after working with winit on iOS for a while.
I'm pretty open to the window size/position changes, as the current situation isn't great. Mostly, I'd want "safe area" awareness to be something that the user opts into, and if they don't, then they get black bars around the safe area.
height: safe_area.size.height as _, | ||
} | ||
} | ||
self.outer_size() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's the reasoning behind this change?
I agree the current situation is not right, but this fix also isn't ideal. Safe area unaware apps should get the black-bar-under-notch effect that is so common in apps today. IIUC this code defaults to rendering under notches. I could be swayed, tho, that winit shouldn't attempt to solve that problem.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've tried to address the reasoning behind this change in #1122. The return value of this function doesn't necessarily affect rendering. When resizing the swapchain, gfx-hal applications generally call compatibility
and use the current_extent
(which returns the bounds of the CAMetalLayer
) instead of calling out to winit and using the inner_size
of the window.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ahh. Missed that. I agree with everything in #1122. The more I think about the "black bar" solution, the more I think it's specific to CAMetalLayer and CAEAGLLayer. IIUC, UIKit views automatically respond to constraints set by the safe area by default.
Your fix here looks good to me.
assert!( | ||
!view_controller.is_null(), | ||
"Failed to create `UIViewController` instance" | ||
); | ||
let view_controller: id = msg_send![view_controller, init]; | ||
view_controller = msg_send![view_controller, init]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why the random coding style change? I'm partial to the immutable version.
.. | ||
} => queued_windows.push(window), | ||
// `UIApplicationMain` already called, so initialize immediately | ||
_ => (*window).init(), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
init
should be performed after drop(this)
. Otherwise we get panics stemming from double borrow_mut
's on AppState
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we also should be queuing up windows during the Launching
state to preserve window creation order.
@@ -116,29 +97,36 @@ impl AppState { | |||
RefMut::map(guard, |state| state.as_mut().unwrap()) | |||
} | |||
|
|||
// requires main thread and window is a UIWindow | |||
// retains window |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A comment explaining why this function is/was unsafe
would be good.
drop(this); | ||
} | ||
|
||
pub unsafe fn cancel_deferred_window_init(window: *mut WindowInner) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This would be unnecessary if appstate held onto std::rc::Weak<RefCell<WindowInner>>
's.
let () = msg_send![self.window, setBounds: bounds]; | ||
} | ||
pub fn set_outer_position(&self, _position: LogicalPosition) { | ||
warn!("`Window::set_outer_position` is ignored on iOS") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What was the reasoning behind this change? We can set window positions on iOS. I'm not sure we should remove support for that.
if self.window.is_null() { | ||
view::frame_from_window_attributes(&self.window_attributes.borrow()) | ||
} else { | ||
msg_send![self.window, frame] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What problems are solved by not using screen_frame
here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems to me that frame
already returns the coordinates in screen-space, which would make screen_frame
and its associated functions unnecessary, and hence reduce complexity.
if self.window.is_null() { | ||
view::frame_from_window_attributes(&self.window_attributes.borrow()) | ||
} else { | ||
msg_send![self.window, frame] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Similar question about why screen_frame
is no longer used here.
@@ -1,6 +1,13 @@ | |||
# Unreleased | |||
|
|||
- On macOS, implement `run_return`. | |||
- On iOS 13.0, fix `NSInternalInconsistencyException` upon touching the screen. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Was this bug simulator only?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can also reproduce this on my iPhone 6s.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah. Bummer
- On iOS, `Window::set_outer_position` is now ignored. | ||
- On iOS, `WindowBuilder::with_inner_size` is now ignored (to match behaviour | ||
`Window::set_inner_size`, and additionally when combined with `with_fullscreen` this used to | ||
result in an incorrect size for the window). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
*to match the behavior of
I am strongly in favor of asserting that there is a current assert!(msg_send![class!(UIApplication), sharedApplication].is_null()); |
@Osspial What do you think of what I've suggested here? To summarize, iOS would not allow window creation (and eventually other things like getting monitor handles) until The current situation is that iOS implicitly requires a running eventloop for at least two things I'm aware of:
There are likely other things that don't work, and I'm concerned that this is a losing battle with UIKit. Apple is apparently happy to break UIKit code that is run before launch. |
Thank you for the review! I know it wasn't the easiest thing to review, especially as I've combined two separate changes here. This was done just to keep me from rewriting the code twice over.
I tend to agree. We should probably just bite the bullet and not allow windows to be created before the event loop starts. It's simpler than having to track all of this state. The examples should be updated accordingly. |
@Osspial I noticed there's no way to access |
@aleksijuvani The main reason we expose |
Closing due to #1120 (comment). |
Fixes #1120
cargo fmt
has been run on this branchCHANGELOG.md
if knowledge of this change could be valuable to users