| Summary: | Android terminates SDL2 application when using the power button while it's active and related power management problems? | ||
|---|---|---|---|
| Product: | SDL | Reporter: | superfury |
| Component: | events | Assignee: | Sam Lantinga <slouken> |
| Status: | RESOLVED INVALID | QA Contact: | Sam Lantinga <slouken> |
| Severity: | normal | ||
| Priority: | P1 | CC: | sylvain.becker |
| Version: | 2.0.5 | ||
| Hardware: | x86 | ||
| OS: | Windows 10 | ||
| Attachments: | Input of my emulator, which crashes when it's turned to standby(or during resume after standby). | ||
your app stop ? does it crash ? or is destroyed normally ? you could add to you AndroidManifest.xml, for your activity : android:alwaysRetainTaskState="true" and also android:launchMode="singleInstance" (In reply to Sylvain from comment #1) > your app stop ? does it crash ? or is destroyed normally ? > > you could add to you AndroidManifest.xml, for your activity : > android:alwaysRetainTaskState="true" > and also android:launchMode="singleInstance" Just added those to the activity in the androidmanifest.xml, but it still stops the application when the device is put in standby while the application is running. I now notice that, even when the device is put to turn off the screen while locked using the power button, the application continues to produce sound, so it's still running the emulation, instead of silencing it(because it's put to the background that disables all those and pauses emulation(the SDL_APP_WILLENTERBACKGROUND event))? (In reply to superfury from comment #2) > (In reply to Sylvain from comment #1) > > your app stop ? does it crash ? or is destroyed normally ? > > > > you could add to you AndroidManifest.xml, for your activity : > > android:alwaysRetainTaskState="true" > > and also android:launchMode="singleInstance" > > Just added those to the activity in the androidmanifest.xml, but it still > stops the application when the device is put in standby while the > application is running. > > I now notice that, even when the device is put to turn off the screen while > locked using the power button, the application continues to produce sound, > so it's still running the emulation, instead of silencing it(because it's > put to the background that disables all those and pauses emulation(the > SDL_APP_WILLENTERBACKGROUND event))? Those don't help. The old behaviour is still there: - When the application is paused by returning to the main Android screen(by pressing the Home button) will pause the application and mute audio output. It can then be put in standby by using the Power button and turned on and clicking the application makes it resume the emulation where it left off. - When the application isn't paused by returning to the main Android screen(not pressing the Home button) and using the Power button to put it in standby, the application keeps running and doesn't mute audio(thus the WILLENTERBACKGROUND event never fires). The application keeps running and generate sound while it's in Standby. When the application is resumed by clicking the Power button again, it seems to crash? I'll add some logging of the background states to my those events to see what's happening... Seems strange, do you have the latest SDL2 version, and also the latest SDLActivity.java ? I also have a S7 and when I press the power button, while the application is running, I get: onPause() nativePause() SDL_APP_WILLENTERBACKGROUND SDL_APP_DIDENTERBACKGROUND I get those events by using SDL_PollEvent ( but you can also get them via SDL_AddEventWatch()) These are the state changes (foreground/background/close states) that happen during my various testcases: Standby,Poweron(returns to main Android screen): *** START *** 00:00:04:34.06077: willenterbackground 00:00:04:34.06502: willenterbackground 00:00:04:34.06786: didenterbackground 00:00:04:34.07144: willenterbackground 00:00:04:34.07274: didenterbackground 00:00:04:34.07435: willenterbackground 00:00:04:36.04639: quitting 00:00:04:36.04906: quitting 00:00:04:36.05040: quitting *** END *** Background, Standby, Poweron, Foreground, Background, Close *** START *** 00:00:08:74.07397: willenterbackground 00:00:08:74.08608: didenterbackground 00:00:08:74.09104: willenterbackground 00:00:08:74.09654: willenterbackground 00:00:08:74.09880: didenterbackground 00:00:08:74.09989: willenterbackground 00:00:21:07.02784: willenterforeground 00:00:21:07.03552: didenterforeground 00:00:21:07.05816: willenterforeground 00:00:21:07.06040: didenterforeground 00:00:28:06.01736: willenterbackground 00:00:28:06.01996: didenterbackground 00:00:28:06.02256: willenterbackground 00:00:28:06.07744: willenterbackground 00:00:28:06.07936: didenterbackground 00:00:28:06.08060: willenterbackground *** END *** Background, Foreground, Background, Close *** START *** 00:00:08:37.09773: willenterbackground 00:00:08:38.00234: didenterbackground 00:00:08:38.00348: willenterbackground 00:00:08:38.00995: willenterbackground 00:00:08:38.01170: didenterbackground 00:00:08:38.01295: willenterbackground 00:00:11:55.02128: willenterforeground 00:00:11:55.02741: didenterforeground 00:00:11:55.04264: willenterforeground 00:00:11:55.04453: didenterforeground 00:00:14:89.09511: willenterbackground 00:00:14:89.09700: didenterbackground 00:00:14:89.09815: willenterbackground 00:00:14:89.09986: willenterbackground 00:00:14:90.00132: didenterbackground 00:00:14:90.00233: willenterbackground *** END *** Background/Foreground is done by Minimizing/Restoring, Close is closing the App from Android main menu(task manager), Standby is pressing the power button to turn screen off, Poweron is the reverse of Standby(pressing the power button to restore Android to poweron mode). So which portion of log represents the state "foreground" + transition "pressing the power button" ? You seemed to say that you did not have the SDL_APP_WILLENTERBACKGROUND ? Can you put a SDL_Log in your "myEventFilter" ? Maybe your app is multi threaded and blocked by your lock() / unlock() ? All those logs start out with the application being started normally(thus on the foreground). The big time gaps of at least 0.5 second are the seperators for each action. The only locking on the logging itself is the logging of a single entry(or timed entry). So the locks that are shown in the code given are the locks that are used, while the logging happens right at the start of the case statement(e.g. directly below "case SDL_APP_DIDENTERBACKGROUND: //Are we pushed to the background?" ). Otherwise, the statements are unmodified. The logging function that's called(dolog) has it's own lock to prevent the same logging file from being used by multiple threads at once, which will be corrupting the logging file if not locked. Yes, the application is multi-threaded, but SDL events are only handled in the main thread(and the event filter's thread for those specific Android cases). Also, the version I'm using is the SDL 2.0.5 version from libsdl.org (https://libsdl.org/release/SDL2-2.0.5.zip ). So according to your log, you correctly receive the event WILL_ENTERBACKGROUND
but immediately after, you are also receiving SDL_APP_TERMINATING (state=quitting).
On android, this event only happens when onDestroy() is called.
in addition to
android:alwaysRetainTaskState="true"
android:launchMode="singleInstance"
I use in AndroidManifest.xml, something like this:
android:configChanges="mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|fontScale|uiMode|screenSize|smallestScreenSize">
If that does not solve your issue, I would:
- update to latest version of SDL2
- try one of the SDL test app
I've replaced the configchanges with the line you've provided. It now properly resumes after going to standby and returning from it. But for some unknown reason, it still continues to produce sound and run the emulation when it's put in standby(as it did with those two task state/launch mode settings)? I've removed those two settings, while leaving your version of the configChanges intact. It now won't crash anymore with any of those situations, but it won't mute the sound or pause the application(through haswindowactive) when it's put in standby while the application is still activated(so essentially, it's stuck in the foreground unless the user minimizes it using the home button)? You should mute and stop using the renderer as soon as you have the ENTERBACKGROUND event. So, this is within your app now. can you identify which of the "configChange" parameter is mandatory ? I originally only had two of them in my config: "keyboardHidden|orientation", which are required for the on-screen keyboard and orientation(which is always landscape(will crash on portrait due to unused handling(though I'll never use that, since the screen would be much too small and problems with reallocating many emulation structures). One or more of those configuration changes you've given fixes the problem with the standby mode, but I don't know which one. I've reduced the config to at least(while keeping it running without crashing): "touchscreen|keyboard|keyboardHidden|orientation|screenLayout|uiMode|screenSize|smallestScreenSize". I don't know how many more I can safely remove without making the application crash. Although orientation changes isn't supported(it's locked to landscape(portrait would make it unusable, due to the very small width, which is required for readability of on-screen text(both the application itself and the emulated system's display))). and if only put : android:configChanges="keyboardHidden|orientation|screenSize" like in the repository ? Just changed the config. Everything still works without crashing. It continues to run in standby still(not backgrounded at all; using vanilla SDL2.0.5 from libsdl.org)? And you still got events : SDL_APP_WILLENTERBACKGROUND / SDL_APP_DIDENTERBACKGROUND ? Put logs into your code to check that you mute the audio ? Receiving either of those two events WILL cause the application to mute audio that's rendering and pause the processing within the app(because of the resulting bits in the haswindowactive). So it's clearly either receiving those and receiving an foreground message right after that(which will resume the application and audio output) or not receiving it at all. This is the log it's producing: 00:00:10:44.01183: willenterbackground(or didenterbackground ^ passthrough) 00:00:10:44.02597: didenterbackground 00:00:10:44.02732: willenterbackground(or didenterbackground ^ passthrough) 00:00:10:44.02874: willenterbackground(or didenterbackground ^ passthrough) 00:00:10:44.03026: didenterbackground 00:00:10:44.03154: willenterbackground(or didenterbackground ^ passthrough) 00:00:10:89.01034: willenterforeground 00:00:10:89.01518: didenterforeground 00:00:10:89.02244: willenterforeground 00:00:10:89.02466: didenterforeground 00:00:22:88.09172: willenterbackground(or didenterbackground ^ passthrough) 00:00:22:89.00360: didenterbackground 00:00:22:89.00824: willenterbackground(or didenterbackground ^ passthrough) 00:00:22:89.01182: willenterbackground(or didenterbackground ^ passthrough) 00:00:22:89.01372: didenterbackground 00:00:22:89.01500: willenterbackground(or didenterbackground ^ passthrough) 00:00:25:58.00428: quitting I've just opened the application, waited for the main execution loop to start, then put it in standby, wait a little bit of time, turn it on, minimized and closed. It handles pausing properly when it's minimized(deactivated) normally using the home button etc. Only during the standby it keeps running for some unknown reason?
The pause is handled at various points in execution:
- Main execution loop:
*** START CODE ***
lock(LOCK_INPUT);
if (unlikely((haswindowactive&0x1C)==0xC)) {getnspassed(&CPU_timing); haswindowactive|=0x10;} //Pending to finish Soundblaster!
currenttiming += likely(haswindowactive&2)?getnspassed(&CPU_timing):0; //Check for any time that has passed to emulate! Don't emulate when not allowed to run, keeping emulation paused!
unlock(LOCK_INPUT);
*** END CODE ***
Video resolution update check:
*** START CODE ***
if (needvideoupdate && ((haswindowactive&3)==3)) //We need to update the screen resolution and we're not hidden (We can't update the Window resolution correctly when we're hidden)?
*** END CODE ***
Frame rendering:
*** START CODE ***
if ((haswindowactive&3)==3) //Are we even visible and allowed to update?
{
safeFlip(rendersurface); //Set the new resized screen to use, if possible!
}
*** END CODE ***
Sound sample rendering:
*** START CODE ***
if ((haswindowactive&4)==0) //Not to sound audio?
{
result_l = result_r = 0; //Mute audio!
}
*** END CODE ***
Handling at the end of the main loop to continue properly:
*** START CODE ***
if (unlikely((haswindowactive&0x38)==0x38)) {haswindowactive &= ~0x38;} //Fully active again?
*** END CODE ***
Sound Blaster sample recording(recording ):
*** START CODE ***
soundblaster_recordedpassed = getnspassed(&SOUNDBLASTER.recordingtimer); //Tick the recording timer real-time!
if (unlikely(haswindowactive&0x10)) {soundblaster_recordedpassed = 0.0; haswindowactive |= ~0x20;} //Fully active again?
*** END CODE ***
That's all the code that has anything directly to do with the minimized/maximized/pause/mute status.
According to your log you have multiple time the "willenterbackground" event and this is strange. I suggest you to update to the latest SDL2 code and try again. I've updated SDL2 to the latest code from hg.libsdl.org/SDL, but the problem is still there?
*** START OF LOG ***
00:00:07:62.01398: willenterbackground
00:00:07:62.01806: didenterbackground
00:00:07:62.02168: willenterbackground
00:00:07:62.02330: didenterbackground
00:00:16:28.04180: willenterforeground
00:00:16:28.04823: didenterforeground
00:00:16:28.06510: willenterforeground
00:00:16:28.06901: didenterforeground
00:00:17:76.01956: willenterbackground
00:00:17:76.02138: didenterbackground
00:00:17:76.02364: willenterbackground
00:00:17:76.02524: didenterbackground
00:00:19:66.01476: quitting
*** END OF LOG ***
This is opening the app, at 7.62 seconds standby, wait some seconds, turn the device on again, minimizing the app, close the app.
I've modified the logging a bit to skip the duplicate willenterbackground after the didenterbackground by jumping around it in the case above it.
*** START CODE ***
//Misc system events
case SDL_QUIT: //Quit?
#ifdef SDL2
#ifdef ANDROID
case SDL_APP_TERMINATING: //Terminating the application by the OS?
//case SDL_APP_LOWMEMORY: //Low on memory?
#ifdef NDK_PROFILE
monpendingcleanup(); //Process any pending cleanup when needed!
#endif
#endif
#endif
quitting:
dolog("Android","quitting");
lock(LOCK_INPUT);
if (joystick) //Gotten a joystick connected?
{
SDL_JoystickClose(joystick); //Finish our joystick: we're not using it anymore!
joystick = NULL; //No joystick connected anymore!
}
EMU_Shutdown(1); //Request a shutdown!
unlock(LOCK_INPUT);
break;
#ifndef SDL2
case SDL_ACTIVEEVENT: //Window event?
lock(LOCK_INPUT);
if (event->active.state&SDL_APPMOUSEFOCUS)
{
hasmousefocus = event->active.gain; //Do we have mouse focus?
}
if (event->active.state&SDL_APPINPUTFOCUS) //Gain/lose keyboard focus?
{
hasinputfocus = event->active.gain; //Do we have input focus?
}
if (event->active.state&SDL_APPACTIVE) //Iconified/Restored?
{
haswindowactive = (haswindowactive&~1)|(event->active.gain?1:0); //0=Iconified, 1=Restored.
}
unlock(LOCK_INPUT);
break;
#else
case SDL_WINDOWEVENT: //SDL2 window event!
switch (event->window.event) //What event?
{
case SDL_WINDOWEVENT_MINIMIZED:
lock(LOCK_INPUT);
haswindowactive &= ~1; //Iconified!
unlock(LOCK_INPUT);
break;
case SDL_WINDOWEVENT_RESTORED:
lock(LOCK_INPUT);
haswindowactive |= 1; //Restored!
unlock(LOCK_INPUT);
break;
case SDL_WINDOWEVENT_FOCUS_GAINED:
lock(LOCK_INPUT);
hasinputfocus = 1; //Input focus!
unlock(LOCK_INPUT);
break;
case SDL_WINDOWEVENT_FOCUS_LOST:
lock(LOCK_INPUT);
hasinputfocus = 0; //Lost input focus!
unlock(LOCK_INPUT);
break;
case SDL_WINDOWEVENT_ENTER:
lock(LOCK_INPUT);
hasmousefocus = 1; //Mouse focus!
unlock(LOCK_INPUT);
break;
case SDL_WINDOWEVENT_LEAVE:
lock(LOCK_INPUT);
hasmousefocus = 0; //Lost mouse focus!
unlock(LOCK_INPUT);
break;
case SDL_WINDOWEVENT_CLOSE:
goto quitting; //We're redirecting to the SDL_QUIT event!
break;
default: //Unknown event?
break;
}
break;
case SDL_APP_DIDENTERBACKGROUND: //Are we pushed to the background?
dolog("Android","didenterbackground");
goto willenterbackground;
case SDL_APP_WILLENTERBACKGROUND: //Are we pushing to the background?
dolog("Android","willenterbackground");
willenterbackground:
lock(LOCK_INPUT);
haswindowactive &= ~6; //We're iconified! This also prevents drawing and audio output! This is critical!
haswindowactive |= 0x8; //Discard any future time!
unlock(LOCK_INPUT);
break;
case SDL_APP_WILLENTERFOREGROUND: //Are we pushing to the foreground?
dolog("Android","willenterforeground");
break; //Unhandled!
case SDL_APP_DIDENTERFOREGROUND: //Are we pushed to the foreground?
dolog("Android","didenterforeground");
lock(LOCK_INPUT);
haswindowactive |= 6; //We're not iconified! This also enables drawing and audio output!
lock(LOCK_GPU);
request_render = 1; //Requesting for rendering once!
unlock(LOCK_GPU);
unlock(LOCK_INPUT);
break;
#ifdef ANDROID
case SDL_WINDOWEVENT_SIZE_CHANGED: //Orientation changed?
lock(LOCK_GPU); //Lock the GPU!
lock(LOCK_VIDEO); //Lock the video output!
window_xres = window_yres = 0; //We're autodetecting the new resolution!
GPU.forceRedraw = 1; //We're forcing a full redraw next frame to make sure the screen is always updated nicely!
needvideoupdate = 1; //We need a video update!
unlock(LOCK_VIDEO); //We're done with video!
unlock(LOCK_GPU); //We're finshed with the GPU!
break;
#endif
*** END CODE ***
Ok! So there first 4 lines : 00:00:07:62.01398: willenterbackground 00:00:07:62.01806: didenterbackground 00:00:07:62.02168: willenterbackground 00:00:07:62.02330: didenterbackground do you think you receive the event twice ? this is still strange. Maybe because you have an EventWatch + you are polling the events ? Anyway, you don't receive any put to foreground. So nothing re-activate the audio ! Could it be that, when you receive an enter_background event, you forward it to another thread, which is not fast enough to process it ? so the audio remains activated ? Also, there is a android compilation flag in SDL2 "BLOCK_ON_PAUSE". Do you have the compilation by default or do you modify this flag ? I haven't added any other flags, except some runtime flags: *** START CODE *** #ifdef SDL2 SDL_AddEventWatch(myEventFilter, NULL); //For applying critical updates! #ifdef SDL_HINT_WINDOWS_NO_CLOSE_ON_ALT_F4 SDL_SetHintWithPriority(SDL_HINT_WINDOWS_NO_CLOSE_ON_ALT_F4,"1",SDL_HINT_OVERRIDE); //We're forcing the window not to quit on ALT-F4! #endif #ifdef SDL_HINT_ANDROID_SEPARATE_MOUSE_AND_TOUCH SDL_SetHintWithPriority(SDL_HINT_ANDROID_SEPARATE_MOUSE_AND_TOUCH,"1",SDL_HINT_OVERRIDE); //We're forcing us to use seperate mouse and touch events! #endif #endif *** END CODE *** So those hints aren't set up at all in my code. The events are supposed to be handled by updateInput, which (in the case of those specific Android inputs) should be handled in the myEventFilter function(which redirects to the updateInput function in the same thread). It also returns 1, which should prevent the event from bubbling up to the main thread's updateInput handling(thus be handled twice)? Wait a sec.... *looks at the filter and SDL documentation again* ... Whoops. The filter has it's filtering backwards indeed: it returns 1 for the Android filters(which means it adds it to the queue for the main thread to process again). Othersise, it returns 0(which will drop the event). https://wiki.libsdl.org/SDL_SetEventFilter That literally says: If filter returns 1, then the event will be added to the internal queue. If it returns 0, then the event will be dropped from the queue, but the internal state will still be updated. This allows selective filtering of dynamically arriving events. So the filter result was backwards(it needs to return 0 for the Android events, while 1 for all other events). Updated filter function: *** START CODE *** int SDLCALL myEventFilter(void *userdata, SDL_Event * event) { //Emergency calls! Immediately update! switch (event->type) //Emergency event? { #ifdef ANDROID case SDL_APP_TERMINATING: //Terminating the application by the OS? case SDL_APP_LOWMEMORY: //Low on memory? case SDL_APP_DIDENTERBACKGROUND: //Are we pushed to the background? case SDL_APP_WILLENTERBACKGROUND: //Are we pushing to the background? case SDL_APP_WILLENTERFOREGROUND: //Are we pushing to the foreground? case SDL_APP_DIDENTERFOREGROUND: //Are we pushed to the foreground? updateInput(event); //Handle this immediately! return 0; //Drop the event, as this is handled already! #endif default: break; //Handle normally! } // etc return 1; //Handle normally, as a normal event! } *** END CODE *** I've improved the SDL to support window events better (for standby) on Android as well(redirecting focus gain/lost to foreground/background):
*** START CODE ***
//Misc system events
case SDL_QUIT: //Quit?
#ifdef SDL2
#ifdef ANDROID
case SDL_APP_TERMINATING: //Terminating the application by the OS?
//case SDL_APP_LOWMEMORY: //Low on memory?
#ifdef NDK_PROFILE
monpendingcleanup(); //Process any pending cleanup when needed!
#endif
#endif
#endif
quitting:
lock(LOCK_INPUT);
if (joystick) //Gotten a joystick connected?
{
SDL_JoystickClose(joystick); //Finish our joystick: we're not using it anymore!
joystick = NULL; //No joystick connected anymore!
}
EMU_Shutdown(1); //Request a shutdown!
unlock(LOCK_INPUT);
break;
#ifndef SDL2
case SDL_ACTIVEEVENT: //Window event?
lock(LOCK_INPUT);
if (event->active.state&SDL_APPMOUSEFOCUS)
{
hasmousefocus = event->active.gain; //Do we have mouse focus?
}
if (event->active.state&SDL_APPINPUTFOCUS) //Gain/lose keyboard focus?
{
hasinputfocus = event->active.gain; //Do we have input focus?
}
if (event->active.state&SDL_APPACTIVE) //Iconified/Restored?
{
haswindowactive = (haswindowactive&~1)|(event->active.gain?1:0); //0=Iconified, 1=Restored.
}
unlock(LOCK_INPUT);
break;
#else
case SDL_WINDOWEVENT: //SDL2 window event!
switch (event->window.event) //What event?
{
case SDL_WINDOWEVENT_MINIMIZED:
lock(LOCK_INPUT);
haswindowactive &= ~1; //Iconified!
unlock(LOCK_INPUT);
break;
case SDL_WINDOWEVENT_RESTORED:
lock(LOCK_INPUT);
haswindowactive |= 1; //Restored!
unlock(LOCK_INPUT);
break;
case SDL_WINDOWEVENT_FOCUS_GAINED:
lock(LOCK_INPUT);
hasinputfocus = 1; //Input focus!
unlock(LOCK_INPUT);
#ifdef ANDROID
goto didenterforeground; //Simulate foreground!
#endif
break;
case SDL_WINDOWEVENT_FOCUS_LOST:
lock(LOCK_INPUT);
hasinputfocus = 0; //Lost input focus!
unlock(LOCK_INPUT);
#ifdef ANDROID
goto didenterbackground; //Simulate background!
#endif
break;
case SDL_WINDOWEVENT_ENTER:
lock(LOCK_INPUT);
hasmousefocus = 1; //Mouse focus!
unlock(LOCK_INPUT);
break;
case SDL_WINDOWEVENT_LEAVE:
lock(LOCK_INPUT);
hasmousefocus = 0; //Lost mouse focus!
unlock(LOCK_INPUT);
break;
case SDL_WINDOWEVENT_CLOSE:
goto quitting; //We're redirecting to the SDL_QUIT event!
break;
default: //Unknown event?
break;
}
break;
case SDL_APP_DIDENTERBACKGROUND: //Are we pushed to the background?
case SDL_APP_WILLENTERBACKGROUND: //Are we pushing to the background?
didenterbackground: //For focus gain/lost!
lock(LOCK_INPUT);
haswindowactive &= ~6; //We're iconified! This also prevents drawing and audio output! This is critical!
haswindowactive |= 0x8; //Discard any future time!
unlock(LOCK_INPUT);
break;
case SDL_APP_WILLENTERFOREGROUND: //Are we pushing to the foreground?
break; //Unhandled!
case SDL_APP_DIDENTERFOREGROUND: //Are we pushed to the foreground?
didenterforeground: //For focus gain/lost!
lock(LOCK_INPUT);
haswindowactive |= 6; //We're not iconified! This also enables drawing and audio output!
lock(LOCK_GPU);
request_render = 1; //Requesting for rendering once!
unlock(LOCK_GPU);
unlock(LOCK_INPUT);
break;
#ifdef ANDROID
case SDL_WINDOWEVENT_SIZE_CHANGED: //Orientation changed?
lock(LOCK_GPU); //Lock the GPU!
lock(LOCK_VIDEO); //Lock the video output!
window_xres = window_yres = 0; //We're autodetecting the new resolution!
GPU.forceRedraw = 1; //We're forcing a full redraw next frame to make sure the screen is always updated nicely!
needvideoupdate = 1; //We need a video update!
unlock(LOCK_VIDEO); //We're done with video!
unlock(LOCK_GPU); //We're finshed with the GPU!
break;
#endif
*** END CODE ***
Is that correct? So this should prevent the continuing and incorrect behaviour on Android?
can really say. but you indeed has identified why the ENTERBACKGROUND event where processed twice. I believe no event should be processed twice (even if it breaks nothing). So what about your audio now ? do you have some trace to make sure the stop request is processed ? how do you play audio and which format ? I´ve just checked on Android again with the lastest code(it now correctly seems to mute audio output(thus pausing the application effectively as well due to the other bits' effects causing running time(nanosecond counter) to be discarded)). The new additions on the focus gain/lost being redirected, after processing, to the didenterforeground/didenterbackground events (on Android only) seems to make the minimizing/restoring and standby behave as they're supposed to. The application is properly 'paused'(the code is still running, but the main execution loop is semi-blocked due to all time gotten from the high resolution clock (delta time) being discarded. So the pausing and resuming in all those cases seems to work properly now:D I haven't modified the BLOCK_ON_PAUSE flag on SDL2. Even when I enable it, it would cause problems with my application, because the delta timing wouldn't be discarded properly(haswindowactive bits 2,3 and 4), causing timing inconsistencies which will cause it to process the entire paused time(after reactivating the app) in one big chunk(that's the way the app is updated: it first retrieves the delta time, then updates the app's state by that time(it's a cycle-accurate x86 emulator). Although the security check should truncate that huge time to a small amount(to prevent running huge loops due to inconsistent CPU speed(or other applications on the used system taking CPU time away from the app)). Looking at the core handling, it seems it truncates the elapsed time to 1ms amounts. Audio is played in the format used by the emulator only(although I use my own resampling algorithm due to the many channels(24 MIDI channels, various emulated hardware channels)).
*** START OF CODE ***
//dolog("soundservice","Setting desired audio device...");
/* Open the audio device. The sound driver will try to give us
the requested format, but it might not succeed. The 'obtained'
structure will be filled in with the actual format data. */
audiospecs.freq = HW_SAMPLERATE; /* desired output sample rate */
audiospecs.format = AUDIO_S16SYS; /* request signed 16-bit samples */
audiospecs.channels = 2; /* ask for stereo */
audiospecs.samples = SAMPLESIZE; /* this is more or less discretionary */
audiospecs.size = audiospecs.samples * audiospecs.channels * sizeof(sample_t);
#ifdef SDL_QUEUEAUDIO
audiospecs.callback = NULL; //We're queueing audio!
#else
audiospecs.callback = &Sound_AudioCallback; //We're not queueing audio! Use the callback instead!
#endif
audiospecs.userdata = NULL; /* we don't need this */
//dolog("soundservice","Opening audio device...");
*** END OF CODE ***
ok, great ! so I'm closing the issue has there's not bug ! (Btw SDL_ANDROID_BLOCK_ON_PAUSE is by default enabled in SDL_androidevents.c) |
Created attachment 2946 [details] Input of my emulator, which crashes when it's turned to standby(or during resume after standby). When I use the power button on my Samsung Galaxy S7 while my SDL2 app is running(which turns off the screen) and click it again(entering the screen lock protection, if required), then my SDL2 application is terminated? Is some required processing missing(it's an emulator that's running), or is there a bug in SDL2? An event filter is set up that redirects all specific events to my main thread(locking it temporarily): *** START OF CODE *** int SDLCALL myEventFilter(void *userdata, SDL_Event * event) { //Emergency calls! Immediately update! switch (event->type) //Emergency event? { case SDL_APP_TERMINATING: //Terminating the application by the OS? case SDL_APP_LOWMEMORY: //Low on memory? case SDL_APP_DIDENTERBACKGROUND: //Are we pushed to the background? case SDL_APP_WILLENTERBACKGROUND: //Are we pushing to the background? case SDL_APP_WILLENTERFOREGROUND: //Are we pushing to the foreground? case SDL_APP_DIDENTERFOREGROUND: //Are we pushed to the foreground? updateInput(event); //Handle this immediately! return 1; //Drop the event, as this is handled already! } // etc return 0; //Handle normally, as a normal event! } *** END OF CODE *** The main input code(which runs from the main thread, as well as from the event handler directly(the updateInput function call)) handles the event, when it's received(ignoring the SDL_APP_LOWMEMORY event, as nothing can be cleaned up on emulated objects(which would screw up the emulator)): *** START OF CODE *** //Misc system events case SDL_QUIT: //Quit? #ifdef SDL2 #ifdef ANDROID case SDL_APP_TERMINATING: //Terminating the application by the OS? //case SDL_APP_LOWMEMORY: //Low on memory? #ifdef NDK_PROFILE monpendingcleanup(); //Process any pending cleanup when needed! #endif #endif #endif quitting: lock(LOCK_INPUT); if (joystick) //Gotten a joystick connected? { SDL_JoystickClose(joystick); //Finish our joystick: we're not using it anymore! joystick = NULL; //No joystick connected anymore! } EMU_Shutdown(1); //Request a shutdown! unlock(LOCK_INPUT); break; #ifndef SDL2 case SDL_ACTIVEEVENT: //Window event? lock(LOCK_INPUT); if (event->active.state&SDL_APPMOUSEFOCUS) { hasmousefocus = event->active.gain; //Do we have mouse focus? } if (event->active.state&SDL_APPINPUTFOCUS) //Gain/lose keyboard focus? { hasinputfocus = event->active.gain; //Do we have input focus? } if (event->active.state&SDL_APPACTIVE) //Iconified/Restored? { haswindowactive = (haswindowactive&~1)|(event->active.gain?1:0); //0=Iconified, 1=Restored. } unlock(LOCK_INPUT); break; #else case SDL_WINDOWEVENT: //SDL2 window event! switch (event->window.event) //What event? { case SDL_WINDOWEVENT_MINIMIZED: lock(LOCK_INPUT); haswindowactive &= ~1; //Iconified! unlock(LOCK_INPUT); break; case SDL_WINDOWEVENT_RESTORED: lock(LOCK_INPUT); haswindowactive |= 1; //Restored! unlock(LOCK_INPUT); break; case SDL_WINDOWEVENT_FOCUS_GAINED: lock(LOCK_INPUT); hasinputfocus = 1; //Input focus! unlock(LOCK_INPUT); break; case SDL_WINDOWEVENT_FOCUS_LOST: lock(LOCK_INPUT); hasinputfocus = 0; //Lost input focus! unlock(LOCK_INPUT); break; case SDL_WINDOWEVENT_ENTER: lock(LOCK_INPUT); hasmousefocus = 1; //Mouse focus! unlock(LOCK_INPUT); break; case SDL_WINDOWEVENT_LEAVE: lock(LOCK_INPUT); hasmousefocus = 0; //Lost mouse focus! unlock(LOCK_INPUT); break; case SDL_WINDOWEVENT_CLOSE: goto quitting; //We're redirecting to the SDL_QUIT event! break; default: //Unknown event? break; } break; case SDL_APP_DIDENTERBACKGROUND: //Are we pushed to the background? case SDL_APP_WILLENTERBACKGROUND: //Are we pushing to the background? lock(LOCK_INPUT); haswindowactive &= ~6; //We're iconified! This also prevents drawing and audio output! This is critical! haswindowactive |= 0x8; //Discard any future time! unlock(LOCK_INPUT); break; case SDL_APP_WILLENTERFOREGROUND: //Are we pushing to the foreground? break; //Unhandled! case SDL_APP_DIDENTERFOREGROUND: //Are we pushed to the foreground? lock(LOCK_INPUT); haswindowactive |= 6; //We're not iconified! This also enables drawing and audio output! lock(LOCK_GPU); request_render = 1; //Requesting for rendering once! unlock(LOCK_GPU); unlock(LOCK_INPUT); break; #ifdef ANDROID case SDL_WINDOWEVENT_SIZE_CHANGED: //Orientation changed? lock(LOCK_GPU); //Lock the GPU! lock(LOCK_VIDEO); //Lock the video output! window_xres = window_yres = 0; //We're autodetecting the new resolution! GPU.forceRedraw = 1; //We're forcing a full redraw next frame to make sure the screen is always updated nicely! needvideoupdate = 1; //We need a video update! unlock(LOCK_VIDEO); //We're done with video! unlock(LOCK_GPU); //We're finshed with the GPU! break; #endif *** END OF CODE *** The haswindowactive has different flags, which prevent the app itself from doing various kinds of updates: - Basic emulation is paused when minimized/iconified(or put on pause when put on background on Android event). - Bits 0&1 need to be set to update video(prevents invalid video updates when not allowed). - Bit 2 mutes audio output(although the buffer is filled with silent samples(zeroes). - Bits 2/3/4(being pending resume/pause state) combined pauses emulation. - Recording being paused/resumed is handled through bit 4 only. Even with all this implemented, when I press the power button on my Android device(only when the app is active, but not when it's in the background) and click it again later(to turn the device on again and to resume emulation(after entering the screen lock, if required)), the application is terminated by the Android OS? Clicking it again restarts the app entirely?