MonoGame / MonoGame

One framework for creating powerful cross-platform games.
http://www.monogame.net
Other
11.46k stars 2.91k forks source link

The GamePad.Back may remain set true on Android after the application calls the Exit() #8522

Open lanosgnatiuc opened 2 weeks ago

lanosgnatiuc commented 2 weeks ago

Prerequisites

MonoGame Version

MonoGame 3.8.x

Which MonoGame platform are you using?

MonoGame Android Application (mgandroid)

Operating System

Windows

Description

The GamePad.Back is set true in the OnKeyDown, and it should be reset to false in the OnKeyUp. But it seems there's a race condition and sometimes the OnKeyUp is not received. That makes the GamePad.Back = true permanently.

Steps to Reproduce

That is reproduced on the default android template. You just have to click back a few times.

Minimal Example Repo

No response

Expected Behavior

The GamePad.Back should be set to false after the application comes to foreground again.

Resulting Behavior

The GamePad.Back remains set to true after the application comes to foreground again.

Files

No response

CartBlanche commented 2 weeks ago

@dellis1972 Is this something you can look into?

lanosgnatiuc commented 2 weeks ago

I've set logs at the Back = true and Back = false, and here it is - the first time it was called, the second wasn't. It's like 50/50 chance.

[Keycode.Back] set true [Keycode.Back] set false [OpenGLRenderer] Unable to match the desired swap behavior. [MonoGame] GraphicsDeviceManager.ResetClientBounds: newClientBounds={X:53 Y:0 Width:1334 Height:2892} [MonoGame] GraphicsDeviceManager.ResetClientBounds: newClientBounds={X:53 Y:0 Width:1334 Height:2892} [Keycode.Back] set true

dellis1972 commented 2 weeks ago

@lanosgnatiuc do you have a simple sample code showing the issue?

lanosgnatiuc commented 2 weeks ago

@dellis1972 The default android project

lanosgnatiuc commented 2 weeks ago

@dellis1972 But if you can tell me where else I can look, I can help debug it.

mrhelmut commented 2 weeks ago

The issue title seems misleading in regard to the description. Are we talking about suspending/resuming the app or about calling Game.Exit()? Because if we're really talking about Game.Exit(), we should assume that anything happening after that is unpredictable and therefore isn't an issue.

lanosgnatiuc commented 2 weeks ago

That's what the Exit() does - moves the app to background, which effectively suspends the app.

dellis1972 commented 2 weeks ago

Calling Exec calls MoveTaskToBack. See https://github.com/MonoGame/MonoGame/blob/develop/MonoGame.Framework/Platform/Android/AndroidGamePlatform.cs#L44C27-L44C41

At this point it will stop receiving Input calls.

That said there might be a bug in this area https://github.com/MonoGame/MonoGame/blob/af25f421e45f1e20391d89a3182cfead275727b0/MonoGame.Framework/Platform/Android/MonoGameAndroidGameView.cs#L1168

mrhelmut commented 2 weeks ago

Game.Exit() is not meant to suspend the app and should be prohibited on Android and iOS because app lifecycle is managed by the system and we shouldn't be able to even call Game.Exit() in that regard.

IMO it should throw an Exception on those platforms.

dellis1972 commented 2 weeks ago

It does not work on iOS at all. Infact the Exit method does not exist. The problem is we have had this call in Android for a long time now. Changing the behaviour will break existing applications, the best thing would be to make it a No-Op on Android and put an Obsolete attribute on it.

lanosgnatiuc commented 2 weeks ago

The problem is that you keep that static "Keycode Back" variable. It shouldn't exist at all.

lanosgnatiuc commented 2 weeks ago

@dellis1972 patch.txt

That fixes the problem, but the real problem is that a static variable keeps a volatile state.

lanosgnatiuc commented 2 weeks ago

Screenshot 2024-10-08 125118

Here is the commit which made this issue evident.

lanosgnatiuc commented 2 weeks ago

@dellis1972

This should be put back, because the code was intended to generate just a one time GamePadState, which the if condition suggests.

            if (index == 0 && Back)
            {
                // Consume state
                Back = false;
                state = new GamePadState(new GamePadThumbSticks(), new GamePadTriggers(), new GamePadButtons(Buttons.Back), new GamePadDPad());
                state.IsConnected = false;
            }