| Summary: | [Regression] When using Theme.NoTitleBar (instead of Theme.NoTitleBar.Fullscreen), SDL2 messes up the windowed mode at launch | ||
|---|---|---|---|
| Product: | SDL | Reporter: | Ellie <etc0de> |
| Component: | *don't know* | Assignee: | Ryan C. Gordon <icculus> |
| Status: | RESOLVED FIXED | QA Contact: | Sam Lantinga <slouken> |
| Severity: | normal | ||
| Priority: | P2 | CC: | someuniquename, sylvain.becker |
| Version: | 2.0.9 | ||
| Hardware: | x86_64 | ||
| OS: | Android (All) | ||
| Attachments: | Illustration showing the different states I get (see comment) | ||
|
Description
Ellie
2018-12-15 00:48:07 UTC
It might also be related to API level, maybe. I built 2.0.9 with NDK API/minimum API 19 and target API 26, and 2.0.4 with NDK API/minimum API 14 and target API 19. Nevertheless, the windowed mode isn't working right with 2.0.9 (without the make-temporarily-fullscreen-and-make-windowed-again hack) I also just noticed that the workaround with SDL_SetWindowFullscreen() only seems to work with an actual delay, and/or after the window has been focused, or some other sort of event. That complicates things... Is anyone aware of a SIMPLE approach to work around this that doesn't involve delays/timing magic? Created attachment 3550 [details]
Illustration showing the different states I get (see comment)
The phonediff.png image illustrates the issue:
When I start the windowed app (no SDL_WINDOW_FULLSCREEN flag or similar for SDL_CreateWindow), I get Variant 3(!) - not Variant 1 as expected.
When I use SDL_SetWindowFullscren to switch to fullscreen I get Variant 2, after switching back to windowed I finally get Variant 1 which is what I want - but only if these switches have some delay, I can NOT just do them instantly after SDL_CreateWindow in a row, sadly.
And that's kinda bad, since I really want Variant 1, and that is also what older SDL2 at a lower API level gave me. So why was this changed, and how can I opt out of it/are you going to fix it?
I did some more research here into what might be going on: https://discourse.libsdl.org/t/search-for-workaround-for-broken-windowed-mode-on-android/25467/6 It seems like a bug in the native code making incorrect assumptions about the surface size, and always resizing it to the full device screen size, even if 1.) the app uses ""@android:style/Theme.NoTitleBar" (no "Fullscreen!"), and 2.) the app doesn't use SDL_WINDOW_FULLSCREEN with SDL_CreateWindow - both things appear to just be ignored. Since the native code directly resizes the surface without calling the SDLActivity's setWindowStyle(), the top bar & menu/action bar aren't properly hidden and this broken in-between mode appears to be entered. To reproduce: 1. Change the AndroidManifest.xml of the example project such that android:theme="@android:style/Theme.NoTitleBar" (note the lacking Fullscreen!) 2. Write a simple app that uses SDL_CreateWindow() without SDL_WINDOW_FULLSCREEN I haven't tested with a minimal app yet but I'm fairly certain this should be enough to see the issue Addition: I only dug around in SDLActivity.java with additional debug logging to see what is called, and observed what was going on from the outside - so I haven't checked the native code itself yet. But I hope I was able to provide some interesting pointers to someone familiar with it to possibly find out what's going on Ok this is kind infuriating!!
I hacked in this into SDLActivity.java's setOrientationBis() with a runOnUIThread(...) runnable to run on the first call of it, since I observed this usually happens right after SDL2 breaks the surface size and ruins everything:
Context context = SDL.getContext();
if (context != null && context instanceof Activity) {
Window window = ((Activity) context).getWindow();
if (window != null) {
Log.v(TAG, "Setting flags to ensure proper windowed mode");
int flags = View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
View.SYSTEM_UI_FLAG_VISIBLE;
window.getDecorView().setSystemUiVisibility(flags);
window.addFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
window.clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
SDLActivity.mFullscreenModeActive = false;
}
}
... because I thought well, might as well undo that nonsense and maybe that fixes it?
And this is what happens:
12-23 11:20:15.252 18588 18588 V SDL : surfaceChanged()
12-23 11:20:15.252 18588 18588 V SDL : pixel format RGB_565
12-23 11:20:15.259 18588 18588 V SDL : Window size: 2160x1080
12-23 11:20:15.259 18588 18588 V SDL : Device size: 2160x1080
<SDL2 native code launches now, breaks things>
<my hack runs in first setOrientationBis() with above code>
12-23 11:20:27.324 18588 18588 V SDL : surfaceChanged()
12-23 11:20:27.324 18588 18588 V SDL : pixel format RGB_565
12-23 11:20:27.327 18588 18588 V SDL : Window size: 2016x1008
12-23 11:20:27.327 18588 18588 V SDL : Device size: 2160x1080
12-23 11:20:27.327 18588 18621 V SDL : setOrientation() orientation=-1 width=900 height=800 resizable=true hint=
<SDL2 native code thinks it's being smarter than me......>
12-23 11:20:29.436 18588 18588 V SDL : surfaceChanged()
12-23 11:20:29.436 18588 18588 V SDL : pixel format RGB_565
12-23 11:20:29.437 18588 18588 V SDL : Window size: 2160x1080
12-23 11:20:29.437 18588 18588 V SDL : Device size: 2160x1080
So yeah. The native code just appears to be always resetting this BACK to the wrong size. Jeez.
So unless I do SDL_SetWindowFullscreen() twice with a delay in the native code (which in python-for-android I can't MAKE the app do from the outside wrapper, so applying this hack from outside is almost impossible) I have no chance of avoiding this issue, and I'm now 99% certain the SDL2 native code is at fault.
So could someone take a look at this maybe? Because this is really a major problem for windowed mode...
Ok so I changed SDLActivity.java's corde like this to make it enforce proper windowed mode if fullscreen isn't active:
private final Runnable rehideSystemUi = new Runnable() {
@Override
public void run() {
int flags = View.SYSTEM_UI_FLAG_FULLSCREEN |
View.SYSTEM_UI_FLAG_HIDE_NAVIGATION |
View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY |
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.INVISIBLE;
SDLActivity.this.getWindow().getDecorView().setSystemUiVisibility(flags);
}
};
private final Runnable reshowSystemUi = new Runnable() {
@Override
public void run() {
int flags = View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_VISIBLE;
SDLActivity.this.getWindow().getDecorView().setSystemUiVisibility(flags);
SDLActivity.this.getWindow().addFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
SDLActivity.this.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
}
};
public void onSystemUiVisibilityChange(int visibility) {
Log.v(TAG, "onSystemUiVisibilityChange(" + visibility + ")");
if (SDLActivity.mFullscreenModeActive && (visibility & View.SYSTEM_UI_FLAG_FULLSCREEN) == 0 || (visibility & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0) {
Log.v(TAG, "onSystemUiVisibilityChange(): re-hiding navigation");
Handler handler = getWindow().getDecorView().getHandler();
if (handler != null) {
handler.removeCallbacks(rehideSystemUi); // Prevent a hide loop.
handler.postDelayed(rehideSystemUi, 2000);
}
} else if (!SDLActivity.mFullscreenModeActive && ((visibility & View.SYSTEM_UI_FLAG_LAYOUT_STABLE) == 0 ||
(visibility & View.SYSTEM_UI_FLAG_VISIBLE) == 0 ||
(visibility & View.SYSTEM_UI_FLAG_FULLSCREEN) != 0)) {
Log.v(TAG, "onSystemUiVisibilityChange(): re-showing navigation");
Handler handler = getWindow().getDecorView().getHandler();
if (handler != null) {
handler.removeCallbacks(reshowSystemUi); // Prevent a hide loop.
handler.postDelayed(reshowSystemUi, 2000);
}
}
}
The result is that I get stuck in a cycle of 1 sec proper windowed mode, 1 sec broken sliding-away-bars-mode constantly changing back and forth, as if some component is fighting me over it.
I would be really happy if someone had an educated guess on what component that is. I already searched through the native code, and outside of the GL stuff, I couldn't find anything that actively enforces a certain surface or window size. Where is this call coming from???
FOUND THE BUG!!! It's in SDLActivity.java:
- if (SDLActivity.mFullscreenModeActive && (visibility & View.SYSTEM_UI_FLAG_FULLSCREEN) == 0 || (visibility & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0) {
+ if (SDLActivity.mFullscreenModeActive && ((visibility & View.SYSTEM_UI_FLAG_FULLSCREEN) == 0 || (visibility & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0)) {
Since this issue is so annoying, could this go into a some-time-soon patch release? Windowed mode is pretty much unusable without this fix!
Great ! I added your patch, it sounds goods. Seems a typo. https://hg.libsdl.org/SDL/rev/3b1f484500f0 This initially was here https://hg.libsdl.org/SDL/rev/842dd960769e Maybe this mFullscreenModeActive is not even needed ? I am closing the issue. but re-open, if it needs more improvements Fixed! For what it's worth, mFullscreenModeActive is definitely needed! This is what the code does: 1. It triggers when anything about status bar/action bar changes 2. It checks if SYSTEM_UI_FLAG_FULLSCREEN is not set, or anything else about the fullscreen layout is broken 3. It restores the proper layout by setting SYSTEM_UI_FLAG_FULLSCREEN This is necessary because Android likes to re-show the bars if the user does certain swipes, so this code is supposed to hide them again. However, hiding those bars in WINDOWED mode just breaks everything entirely, which was the bug. mFullscreenModeActive is changed through setWindowStyle(), which is called by the native code depending on whether the SDL2 window is supposed to be fullscreened or not. So yes, evaluating that is vital for this code part Thanks for adding the fix! Looking forward to seeing this work properly in the next release Ok, thanks for the explanation I got it ! |