Skip to content

Commit

Permalink
macOS/iOS: Remove window activation hacks (#3872)
Browse files Browse the repository at this point in the history
No longer necessary after a8c7109 and facb809.
  • Loading branch information
madsmtm authored Aug 18, 2024
1 parent a61e7bb commit 6c4da19
Show file tree
Hide file tree
Showing 6 changed files with 18 additions and 113 deletions.
9 changes: 0 additions & 9 deletions src/platform/ios.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,6 @@
//! Winit has an OS requirement of iOS 8 or higher, and is regularly tested on
//! iOS 9.3.
//!
//! ## Window initialization
//!
//! iOS's main `UIApplicationMain` does some init work that's required by all
//! UI-related code (see issue [#1705]). It is best to create your windows
//! inside [`ApplicationHandler::resumed`].
//!
//! [#1705]: https://github.com/rust-windowing/winit/issues/1705
//! [`ApplicationHandler::resumed`]: crate::application::ApplicationHandler::resumed
//!
//! ## Building app
//!
//! To build ios app you will need rustc built for this targets:
Expand Down
14 changes: 0 additions & 14 deletions src/platform/macos.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,6 @@
//! Winit has an OS requirement of macOS 10.11 or higher (same as Rust
//! itself), and is regularly tested on macOS 10.14.
//!
//! ## Window initialization
//!
//! 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
//! [`ApplicationHandler::resumed`].
//!
//! [#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
//! [`ApplicationHandler::resumed`]: crate::application::ApplicationHandler::resumed
//!
//! ## Custom `NSApplicationDelegate`
//!
//! Winit usually handles everything related to the lifecycle events of the application. Sometimes,
Expand Down
24 changes: 0 additions & 24 deletions src/platform_impl/apple/appkit/app_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,6 @@ impl AppState {
// menu bar is initially unresponsive on macOS 10.15.
app.setActivationPolicy(self.activation_policy);

window_activation_hack(&app);
#[allow(deprecated)]
app.activateIgnoringOtherApps(self.activate_ignoring_other_apps);

Expand Down Expand Up @@ -387,26 +386,3 @@ impl AppState {
fn min_timeout(a: Option<Instant>, b: Option<Instant>) -> Option<Instant> {
a.map_or(b, |a_timeout| b.map_or(Some(a_timeout), |b_timeout| Some(a_timeout.min(b_timeout))))
}

/// A hack to make activation of multiple windows work when creating them before
/// `applicationDidFinishLaunching:` / `Event::Event::NewEvents(StartCause::Init)`.
///
/// Alternative to this would be the user calling `window.set_visible(true)` in
/// `StartCause::Init`.
///
/// If this becomes too bothersome to maintain, it can probably be removed
/// without too much damage.
fn window_activation_hack(app: &NSApplication) {
// TODO: Proper ordering of the windows
app.windows().into_iter().for_each(|window| {
// Call `makeKeyAndOrderFront` if it was called on the window in `WinitWindow::new`
// This way we preserve the user's desired initial visibility status
// TODO: Also filter on the type/"level" of the window, and maybe other things?
if window.isVisible() {
tracing::trace!("Activating visible window");
window.makeKeyAndOrderFront(None);
} else {
tracing::trace!("Skipping activating invisible window");
}
})
}
5 changes: 5 additions & 0 deletions src/platform_impl/apple/appkit/window_delegate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -558,6 +558,11 @@ fn new_window(
masks |= NSWindowStyleMask::FullSizeContentView;
}

// NOTE: This should only be created after the application has started launching,
// (`applicationWillFinishLaunching:` at the earliest), otherwise you'll run into very
// confusing issues with the window not being properly activated.
//
// Winit ensures this by not allowing access to `ActiveEventLoop` before handling events.
let window: Option<Retained<WinitWindow>> = unsafe {
msg_send_id![
super(mtm.alloc().set_ivars(())),
Expand Down
71 changes: 7 additions & 64 deletions src/platform_impl/apple/uikit/app_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,7 @@ use core_foundation::runloop::{
CFRunLoopTimerInvalidate, CFRunLoopTimerRef, CFRunLoopTimerSetNextFireDate,
};
use objc2::rc::Retained;
use objc2::runtime::AnyObject;
use objc2::{msg_send, sel};
use objc2::sel;
use objc2_foundation::{
CGRect, CGSize, MainThreadMarker, NSInteger, NSObjectProtocol, NSOperatingSystemVersion,
NSProcessInfo,
Expand Down Expand Up @@ -114,7 +113,6 @@ impl Event {
#[must_use = "dropping `AppStateImpl` without inspecting it is probably a bug"]
enum AppStateImpl {
Initial {
queued_windows: Vec<Retained<WinitUIWindow>>,
queued_events: Vec<EventWrapper>,
queued_gpu_redraws: HashSet<Retained<WinitUIWindow>>,
},
Expand Down Expand Up @@ -160,7 +158,6 @@ impl AppState {
let waker = EventLoopWaker::new(unsafe { CFRunLoopGetMain() });
**guard = Some(AppState {
app_state: Some(AppStateImpl::Initial {
queued_windows: Vec::new(),
queued_events: Vec::new(),
queued_gpu_redraws: HashSet::new(),
}),
Expand Down Expand Up @@ -219,20 +216,18 @@ impl AppState {
matches!(self.state(), AppStateImpl::Terminated)
}

fn did_finish_launching_transition(
&mut self,
) -> (Vec<Retained<WinitUIWindow>>, Vec<EventWrapper>) {
let (windows, events, queued_gpu_redraws) = match self.take_state() {
AppStateImpl::Initial { queued_windows, queued_events, queued_gpu_redraws } => {
(queued_windows, queued_events, queued_gpu_redraws)
fn did_finish_launching_transition(&mut self) -> Vec<EventWrapper> {
let (events, queued_gpu_redraws) = match self.take_state() {
AppStateImpl::Initial { queued_events, queued_gpu_redraws } => {
(queued_events, queued_gpu_redraws)
},
s => bug!("unexpected state {:?}", s),
};
self.set_state(AppStateImpl::ProcessingEvents {
active_control_flow: self.control_flow,
queued_gpu_redraws,
});
(windows, events)
events
}

fn wakeup_transition(&mut self) -> Option<EventWrapper> {
Expand Down Expand Up @@ -393,26 +388,6 @@ impl AppState {
}
}

pub(crate) fn set_key_window(mtm: MainThreadMarker, window: &Retained<WinitUIWindow>) {
let mut this = AppState::get_mut(mtm);
match this.state_mut() {
&mut AppStateImpl::Initial { ref mut queued_windows, .. } => {
return queued_windows.push(window.clone())
},
&mut AppStateImpl::ProcessingEvents { .. }
| &mut AppStateImpl::InUserCallback { .. }
| &mut AppStateImpl::ProcessingRedraws { .. } => {},
s @ &mut AppStateImpl::Waiting { .. } | s @ &mut AppStateImpl::PollFinished { .. } => {
bug!("unexpected state {:?}", s)
},
&mut AppStateImpl::Terminated => {
panic!("Attempt to create a `Window` after the app has terminated")
},
}
drop(this);
window.makeKeyAndVisible();
}

pub(crate) fn queue_gl_or_metal_redraw(mtm: MainThreadMarker, window: Retained<WinitUIWindow>) {
let mut this = AppState::get_mut(mtm);
match this.state_mut() {
Expand All @@ -436,39 +411,13 @@ pub(crate) fn launch(mtm: MainThreadMarker, app: &mut dyn ApplicationHandler, ru

pub fn did_finish_launching(mtm: MainThreadMarker) {
let mut this = AppState::get_mut(mtm);
let windows = match this.state_mut() {
AppStateImpl::Initial { queued_windows, .. } => mem::take(queued_windows),
s => bug!("unexpected state {:?}", s),
};

this.waker.start();

// have to drop RefMut because the window setup code below can trigger new events
drop(this);

for window in windows {
// Do a little screen dance here to account for windows being created before
// `UIApplicationMain` is called. This fixes visual issues such as being
// offcenter and sized incorrectly. Additionally, to fix orientation issues, we
// gotta reset the `rootViewController`.
//
// relevant iOS log:
// ```
// [ApplicationLifecycle] Windows were created before application initialization
// completed. This may result in incorrect visual appearance.
// ```
let screen = window.screen();
let _: () = unsafe { msg_send![&window, setScreen: ptr::null::<AnyObject>()] };
window.setScreen(&screen);

let controller = window.rootViewController();
window.setRootViewController(None);
window.setRootViewController(controller.as_deref());

window.makeKeyAndVisible();
}

let (windows, events) = AppState::get_mut(mtm).did_finish_launching_transition();
let events = AppState::get_mut(mtm).did_finish_launching_transition();

let events = [
EventWrapper::StaticEvent(Event::NewEvents(StartCause::Init)),
Expand All @@ -477,12 +426,6 @@ pub fn did_finish_launching(mtm: MainThreadMarker) {
.into_iter()
.chain(events);
handle_nonuser_events(mtm, events);

// the above window dance hack, could possibly trigger new windows to be created.
// we can just set those windows up normally, as they were created after didFinishLaunching
for window in windows {
window.makeKeyAndVisible();
}
}

// AppState::did_finish_launching handles the special transition `Init`
Expand Down
8 changes: 6 additions & 2 deletions src/platform_impl/apple/uikit/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,11 @@ impl WinitUIWindow {
frame: CGRect,
view_controller: &UIViewController,
) -> Retained<Self> {
// NOTE: This should only be created after the application has started launching,
// (`application:willFinishLaunchingWithOptions:` at the earliest), otherwise you'll run
// into very confusing issues with the window not being properly activated.
//
// Winit ensures this by not allowing access to `ActiveEventLoop` before handling events.
let this: Retained<Self> = unsafe { msg_send_id![mtm.alloc(), initWithFrame: frame] };

this.setRootViewController(Some(view_controller));
Expand Down Expand Up @@ -490,8 +495,7 @@ impl Window {

let view_controller = WinitViewController::new(mtm, &window_attributes, &view);
let window = WinitUIWindow::new(mtm, &window_attributes, frame, &view_controller);

app_state::set_key_window(mtm, &window);
window.makeKeyAndVisible();

// Like the Windows and macOS backends, we send a `ScaleFactorChanged` and `Resized`
// event on window creation if the DPI factor != 1.0
Expand Down

0 comments on commit 6c4da19

Please sign in to comment.