rust-windowing / winit

Window handling library in pure Rust
https://docs.rs/winit/
Apache License 2.0
4.88k stars 912 forks source link

Cross-platform consistency for `ApplicationHandler::suspended/resumed()` #3779

Open daxpedda opened 4 months ago

daxpedda commented 4 months ago

Unfortunately the behavior between Android, iOS and Web doesn't match exactly, so this will require some research and figuring out exactly how we want to expose this to the user.

Web

Web currently implements this via pagehide and pageshow. Which for suspended() means the application is being navigated away from and is being stored in the B/F Cache and for resumed() means that the page has been restored.

Its important to note that between suspended() and restored() no user code can be executed and getting restored() isn't actually promised.

There is also the Page Lifecycle API, currently only implemented in Chrome, which lets us also detect if the application was frozen by the browser.

iOS

@madsmtm please feel free to work out this section and correct me.

applicationDidEnterBackground could be used to detect when iOS is telling the application to stop doing anything. This doesn't mean that any code execution will be suspended immediately but "probably soon".

This however is currently not implemented correctly and instead iOS will call ApplicationHandler::suspended() when the application is running in the background via applicationWillResignActive, which should probably be WindowEvent::Focused(false) instead.

Presumably its also guaranteed that the application will be woken up before being actually closed (?).

Android

@MarijnS95 your input would be greatly appreciated here.

Android does currently not implement ApplicationHandler::suspended/resumed()

MarijnS95 commented 4 months ago

@daxpedda just in case; you want context after #3765, i.e. knowing that we moved surface lifetime away from the suspended() and resumed() events that Android was previously relying on?

From the look and sound of it you'll want onStop() and onStart() from the activity lifecycle: https://developer.android.com/guide/components/activities/activity-lifecycle

Note that there are are differences between these and onPause()/onResume().

These lifecycle events are already available in winit:

https://github.com/rust-windowing/winit/blob/39a7d5b738c79687f3d493181378ad129702cbfc/src/platform_impl/android/mod.rs#L223-L243

daxpedda commented 4 months ago

From the documentation I can't seem to be able to definitely say that all code execution is halted after onStop, it seems more similar to iOS in that regard, that execution will stop "soon". Am I getting this right?

Note that there are are differences between these and onPause()/onResume().

Seems similar to iOS's applicationWillResignActive, which should be delegated to WindowEvent::Focused(false).

These lifecycle events are already available in winit: ...

Would you like to make a PR implementing those events? I can offer actually reviewing it and testing it locally.

MarijnS95 commented 4 months ago

Android doesn't have a concept of this "code stop" that other platforms seem to have. As we have a separate thread where we create our own looper, and configure how often it should wake via - for example - ControlFlow::{Poll, Wait}, the loop can keep running after both these events and producing code flow.

These lifecycle events more so tell you what is going on with the visible portion and user interaction of your app. However, I won't be surprised if Android has mechanisms in place to kill or throttle long-running app processes if no Activitys are visible for some time, and it doesn't have background "threads" that show a persistent notification of sorts. But for now you can minimize an Android app and observe that logging keeps going inside an event loop (rely on debug/verbose logging inside the android-activity crate to see this too).


Seems similar to iOS's applicationWillResignActive, which should be delegated to WindowEvent::Focused(false).

Android also has (un)focused events to make things extra complicated :/

These don't seem to be keyed off of the native surface though, confusingly.


Opened a PR at #3786 but my time is pretty much up so I'll leave documentation for some other time.

What's worse, we seem to be having some weird hypothetical self.running bool that is keyed off onResume/onSuspend while the app is still visible to block RedrawRequested etc?

kchibisov commented 4 months ago

If the application is simple hidden, but still running the Occluded event should be used instead, because it indicats that the window is hidden, but not paused. Resume/Suspended should really happen only if the application is not running at all.

MarijnS95 commented 4 months ago

This is where Android recommends you to save/restore state like the other platforms, though, but you're right that it seems weird.