We are currently migrating Bugzilla to GitHub issues.
Any changes made to the bug tracker now will be lost, so please do not post new bugs or make changes to them.
When we're done, all bug URLs will redirect to their equivalent location on the new bug tracker.

Bug 4424

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
SDL 2.0.9 creates all windows without SDL_WINDOW_FULLSCREEN wrong, aren't properly windowed: instead, they're in this weird semi-immersive in between mode where the top bar and button bar will fade away, but it's not fully fullscreen - which is EXTREMELY annoying.

In 2.0.4 it works fine.

Also, to illustrate this is clearly a bug, the following statement has no effect:

SDL_SetWindowFullscreen(affected_window, 0)

... however, this suddenly makes the bars fully go away (proper fullscreen, onot the annoying bars-kinda-fade-away-sometimes mess):

SDL_SetWindowFullscreen(affected_window, SDL_WINDOW_FULLSCREEN)

... and this will actually go to PROPER windowed (bars are visible and STAY):

SDL_SetWindowFullscreen(affected_window, SDL_WINDOW_FULLSCREEN)
SDL_SetWindowFullscreen(affected_window, 0)

All on a window that never had SDL_WINDOW_FULLSCREEN in the flags of course, so it should be windowed.

I tested this on Android 7.1
Comment 1 Ellie 2018-12-15 00:51:29 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)
Comment 2 Ellie 2018-12-15 07:55:37 UTC
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?
Comment 3 Ellie 2018-12-17 01:17:17 UTC
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?
Comment 4 Ellie 2018-12-20 22:26:35 UTC
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
Comment 5 Ellie 2018-12-20 22:30:50 UTC
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
Comment 6 Ellie 2018-12-23 10:28:47 UTC
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...
Comment 7 Ellie 2019-01-02 09:40:21 UTC
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???
Comment 8 Ellie 2019-01-02 14:16:20 UTC
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!
Comment 9 Sylvain 2019-01-02 16:12:28 UTC
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
Comment 10 Sylvain 2019-01-02 16:13:15 UTC
Fixed!
Comment 11 Ellie 2019-01-02 16:19:04 UTC
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
Comment 12 Ellie 2019-01-02 16:20:15 UTC
Thanks for adding the fix! Looking forward to seeing this work properly in the next release
Comment 13 Sylvain 2019-01-02 16:30:01 UTC
Ok, thanks for the explanation I got it !