diff --git a/UI/ControlMappingScreen.cpp b/UI/ControlMappingScreen.cpp index 8dac4ccae6..2008c19f50 100644 --- a/UI/ControlMappingScreen.cpp +++ b/UI/ControlMappingScreen.cpp @@ -636,6 +636,25 @@ void TouchTestScreen::CreateViews() { root_ = new LinearLayout(ORIENT_VERTICAL); LinearLayout *theTwo = new LinearLayout(ORIENT_HORIZONTAL, new LinearLayoutParams(1.0f)); root_->Add(theTwo); + +#if !PPSSPP_PLATFORM(UWP) + static const char *renderingBackend[] = { "OpenGL", "Direct3D 9", "Direct3D 11", "Vulkan" }; + PopupMultiChoice *renderingBackendChoice = root_->Add(new PopupMultiChoice(&g_Config.iGPUBackend, gr->T("Backend"), renderingBackend, (int)GPUBackend::OPENGL, ARRAY_SIZE(renderingBackend), gr->GetName(), screenManager())); + renderingBackendChoice->OnChoice.Handle(this, &TouchTestScreen::OnRenderingBackend); + + if (!g_Config.IsBackendEnabled(GPUBackend::OPENGL)) + renderingBackendChoice->HideChoice((int)GPUBackend::OPENGL); + if (!g_Config.IsBackendEnabled(GPUBackend::DIRECT3D9)) + renderingBackendChoice->HideChoice((int)GPUBackend::DIRECT3D9); + if (!g_Config.IsBackendEnabled(GPUBackend::DIRECT3D11)) + renderingBackendChoice->HideChoice((int)GPUBackend::DIRECT3D11); + if (!g_Config.IsBackendEnabled(GPUBackend::VULKAN)) + renderingBackendChoice->HideChoice((int)GPUBackend::VULKAN); +#endif + +#if PPSSPP_PLATFORM(ANDROID) + root_->Add(new Choice(gr->T("Recreate Activity")))->OnClick.Handle(this, &TouchTestScreen::OnRecreateActivity); +#endif root_->Add(new CheckBox(&g_Config.bImmersiveMode, gr->T("FullScreen", "Full Screen")))->OnClick.Handle(this, &TouchTestScreen::OnImmersiveModeChange); root_->Add(new Button(di->T("Back")))->OnClick.Handle(this, &UIScreen::OnBack); } @@ -687,7 +706,7 @@ void TouchTestScreen::render() { g_dpi_scale_x, g_dpi_scale_y, g_dpi_scale_real_x, g_dpi_scale_real_y); - ui_context->DrawTextShadow(buffer, bounds.x + 20.0f, dp_yres / 2.0f, 0xFFFFFFFF, ALIGN_VCENTER | FLAG_DYNAMIC_ASCII); + ui_context->DrawTextShadow(buffer, bounds.x + 20.0f, bounds.y + 20.0f, 0xFFFFFFFF, FLAG_DYNAMIC_ASCII); ui_context->Flush(); } @@ -700,3 +719,14 @@ UI::EventReturn TouchTestScreen::OnImmersiveModeChange(UI::EventParams &e) { } return UI::EVENT_DONE; } + +UI::EventReturn TouchTestScreen::OnRenderingBackend(UI::EventParams &e) { + g_Config.Save("GameSettingsScreen::RenderingBackend"); + System_SendMessage("graphics_restart", "--touchscreentest"); + return UI::EVENT_DONE; +} + +UI::EventReturn TouchTestScreen::OnRecreateActivity(UI::EventParams &e) { + RecreateActivity(); + return UI::EVENT_DONE; +} diff --git a/UI/ControlMappingScreen.h b/UI/ControlMappingScreen.h index 130596ee05..3d7b6879e5 100644 --- a/UI/ControlMappingScreen.h +++ b/UI/ControlMappingScreen.h @@ -132,6 +132,9 @@ protected: }; TrackedTouch touches_[MAX_TOUCH_POINTS]{}; - virtual void CreateViews() override; + void CreateViews() override; + UI::EventReturn OnImmersiveModeChange(UI::EventParams &e); + UI::EventReturn OnRenderingBackend(UI::EventParams &e); + UI::EventReturn OnRecreateActivity(UI::EventParams &e); }; diff --git a/UI/GameSettingsScreen.cpp b/UI/GameSettingsScreen.cpp index c597a68394..f11be70585 100644 --- a/UI/GameSettingsScreen.cpp +++ b/UI/GameSettingsScreen.cpp @@ -1195,14 +1195,25 @@ void GameSettingsScreen::CallbackMemstickFolder(bool yes) { } #endif +void GameSettingsScreen::TriggerRestart(const char *why) { + // Extra save here to make sure the choice really gets saved even if there are shutdown bugs in + // the GPU backend code. + g_Config.Save(why); + std::string param = "--gamesettings"; + if (editThenRestore_) { + // We won't pass the gameID, so don't resume back into settings. + param = ""; + } else if (!gamePath_.empty()) { + param += " \"" + ReplaceAll(ReplaceAll(gamePath_, "\\", "\\\\"), "\"", "\\\"") + "\""; + } + System_SendMessage("graphics_restart", param.c_str()); +} + void GameSettingsScreen::CallbackRenderingBackend(bool yes) { // If the user ends up deciding not to restart, set the config back to the current backend // so it doesn't get switched by accident. if (yes) { - // Extra save here to make sure the choice really gets saved even if there are shutdown bugs in - // the GPU backend code. - g_Config.Save("GameSettingsScreen::RenderingBackendYes"); - System_SendMessage("graphics_restart", ""); + TriggerRestart("GameSettingsScreen::RenderingBackendYes"); } else { g_Config.iGPUBackend = (int)GetGPUBackend(); } @@ -1212,10 +1223,7 @@ void GameSettingsScreen::CallbackRenderingDevice(bool yes) { // If the user ends up deciding not to restart, set the config back to the current backend // so it doesn't get switched by accident. if (yes) { - // Extra save here to make sure the choice really gets saved even if there are shutdown bugs in - // the GPU backend code. - g_Config.Save("GameSettingsScreen::RenderingDeviceYes"); - System_SendMessage("graphics_restart", ""); + TriggerRestart("GameSettingsScreen::RenderingDeviceYes"); } else { std::string *deviceNameSetting = GPUDeviceNameSetting(); if (deviceNameSetting) @@ -1227,8 +1235,7 @@ void GameSettingsScreen::CallbackRenderingDevice(bool yes) { void GameSettingsScreen::CallbackInflightFrames(bool yes) { if (yes) { - g_Config.Save("GameSettingsScreen::InflightFramesYes"); - System_SendMessage("graphics_restart", ""); + TriggerRestart("GameSettingsScreen::InflightFramesYes"); } else { g_Config.iInflightFrames = prevInflightFrames_; } diff --git a/UI/GameSettingsScreen.h b/UI/GameSettingsScreen.h index 2cf38a3a36..cfcd7fae27 100644 --- a/UI/GameSettingsScreen.h +++ b/UI/GameSettingsScreen.h @@ -48,6 +48,8 @@ protected: bool UseVerticalLayout() const; private: + void TriggerRestart(const char *why); + std::string gameID_; bool lastVertical_; UI::CheckBox *enableReportsCheckbox_; diff --git a/UI/MiscScreens.cpp b/UI/MiscScreens.cpp index 8888df4b55..27fadc85fd 100644 --- a/UI/MiscScreens.cpp +++ b/UI/MiscScreens.cpp @@ -420,7 +420,14 @@ void NewLanguageScreen::OnCompleted(DialogResult result) { void LogoScreen::Next() { if (!switched_) { switched_ = true; - if (boot_filename.size()) { + if (gotoGameSettings_) { + if (boot_filename.size()) { + screenManager()->switchScreen(new EmuScreen(boot_filename)); + } else { + screenManager()->switchScreen(new MainScreen()); + } + screenManager()->push(new GameSettingsScreen(boot_filename)); + } else if (boot_filename.size()) { screenManager()->switchScreen(new EmuScreen(boot_filename)); } else { screenManager()->switchScreen(new MainScreen()); diff --git a/UI/MiscScreens.h b/UI/MiscScreens.h index 34b0e0f40b..459974de48 100644 --- a/UI/MiscScreens.h +++ b/UI/MiscScreens.h @@ -114,8 +114,8 @@ private: class LogoScreen : public UIScreen { public: - LogoScreen() - : frames_(0), switched_(false) {} + LogoScreen(bool gotoGameSettings = false) + : gotoGameSettings_(gotoGameSettings) {} bool key(const KeyInput &key) override; bool touch(const TouchInput &touch) override; void update() override; @@ -125,8 +125,9 @@ public: private: void Next(); - int frames_; - bool switched_; + int frames_ = 0; + bool switched_ = false; + bool gotoGameSettings_ = false; }; class CreditsScreen : public UIDialogScreenWithBackground { diff --git a/UI/NativeApp.cpp b/UI/NativeApp.cpp index b71f30a90f..3c29f900b2 100644 --- a/UI/NativeApp.cpp +++ b/UI/NativeApp.cpp @@ -92,17 +92,18 @@ #include "Core/WebServer.h" #include "GPU/GPUInterface.h" +#include "UI/BackgroundAudio.h" +#include "UI/ControlMappingScreen.h" +#include "UI/DiscordIntegration.h" #include "UI/EmuScreen.h" #include "UI/GameInfoCache.h" +#include "UI/GPUDriverTestScreen.h" #include "UI/HostTypes.h" -#include "UI/OnScreenDisplay.h" #include "UI/MiscScreens.h" +#include "UI/OnScreenDisplay.h" #include "UI/RemoteISOScreen.h" #include "UI/TiltEventProcessor.h" -#include "UI/BackgroundAudio.h" #include "UI/TextureUtil.h" -#include "UI/DiscordIntegration.h" -#include "UI/GPUDriverTestScreen.h" #if !defined(MOBILE_DEVICE) #include "Common/KeyMap.h" @@ -551,6 +552,8 @@ void NativeInit(int argc, const char *argv[], const char *savegame_dir, const ch const char *stateToLoad = 0; bool gotBootFilename = false; + bool gotoGameSettings = false; + bool gotoTouchScreenTest = false; boot_filename = ""; // Parse command line @@ -593,6 +596,10 @@ void NativeInit(int argc, const char *argv[], const char *savegame_dir, const ch g_Config.bPauseMenuExitsEmulator = true; if (!strcmp(argv[i], "--fullscreen")) g_Config.bFullScreen = true; + if (!strcmp(argv[i], "--touchscreentest")) + gotoTouchScreenTest = true; + if (!strcmp(argv[i], "--gamesettings")) + gotoGameSettings = true; break; } } else { @@ -628,7 +635,7 @@ void NativeInit(int argc, const char *argv[], const char *savegame_dir, const ch std::unique_ptr fileLoader(ConstructFileLoader(boot_filename)); if (!fileLoader->Exists()) { fprintf(stderr, "File not found: %s\n", boot_filename.c_str()); -#ifdef _WIN32 +#if defined(_WIN32) || defined(__ANDROID__) // Ignore and proceed. #else // Bail. @@ -638,7 +645,7 @@ void NativeInit(int argc, const char *argv[], const char *savegame_dir, const ch } } else { fprintf(stderr, "Can only boot one file"); -#ifdef _WIN32 +#if defined(_WIN32) || defined(__ANDROID__) // Ignore and proceed. #else // Bail. @@ -711,7 +718,12 @@ void NativeInit(int argc, const char *argv[], const char *savegame_dir, const ch } screenManager = new ScreenManager(); - if (skipLogo) { + if (gotoGameSettings) { + screenManager->switchScreen(new LogoScreen(true)); + } else if (gotoTouchScreenTest) { + screenManager->switchScreen(new MainScreen()); + screenManager->push(new TouchTestScreen()); + } else if (skipLogo) { screenManager->switchScreen(new EmuScreen(boot_filename)); } else { screenManager->switchScreen(new LogoScreen()); diff --git a/Windows/GPU/WindowsGLContext.cpp b/Windows/GPU/WindowsGLContext.cpp index f0aa0ba45a..71e215c0bc 100644 --- a/Windows/GPU/WindowsGLContext.cpp +++ b/Windows/GPU/WindowsGLContext.cpp @@ -290,6 +290,9 @@ bool WindowsGLContext::InitFromRenderThread(std::string *error_message) { // Unfortunately, glew will generate an invalid enum error, ignore. glGetError(); + // Reset in case we're in a backend switch. + ResetGLExtensions(); + int contextFlags = g_Config.bGfxDebugOutput ? WGL_CONTEXT_DEBUG_BIT_ARB : 0; HGLRC m_hrc = nullptr; diff --git a/Windows/W32Util/Misc.cpp b/Windows/W32Util/Misc.cpp index ca26ad7a4b..3bb86f6bf6 100644 --- a/Windows/W32Util/Misc.cpp +++ b/Windows/W32Util/Misc.cpp @@ -153,13 +153,20 @@ namespace W32Util moduleFilename.resize(sz); } - void ExitAndRestart() { + void ExitAndRestart(bool overrideArgs, const std::string &args) { // This preserves arguments (for example, config file) and working directory. std::wstring workingDirectory; std::wstring moduleFilename; GetSelfExecuteParams(workingDirectory, moduleFilename); - const wchar_t *cmdline = RemoveExecutableFromCommandLine(GetCommandLineW()); + const wchar_t *cmdline; + std::wstring wargs; + if (overrideArgs) { + wargs = ConvertUTF8ToWString(args); + cmdline = wargs.c_str(); + } else { + cmdline = RemoveExecutableFromCommandLine(GetCommandLineW()); + } ShellExecute(nullptr, nullptr, moduleFilename.c_str(), cmdline, workingDirectory.c_str(), SW_SHOW); ExitProcess(0); diff --git a/Windows/W32Util/Misc.h b/Windows/W32Util/Misc.h index 680a48c62e..623de73233 100644 --- a/Windows/W32Util/Misc.h +++ b/Windows/W32Util/Misc.h @@ -11,7 +11,7 @@ namespace W32Util BOOL CopyTextToClipboard(HWND hwnd, const char *text); BOOL CopyTextToClipboard(HWND hwnd, const std::wstring &wtext); void MakeTopMost(HWND hwnd, bool topMost); - void ExitAndRestart(); + void ExitAndRestart(bool overrideArgs = false, const std::string &args = ""); void GetSelfExecuteParams(std::wstring &workingDirectory, std::wstring &moduleFilename); } diff --git a/Windows/main.cpp b/Windows/main.cpp index 73938e6093..3795597e26 100644 --- a/Windows/main.cpp +++ b/Windows/main.cpp @@ -96,6 +96,8 @@ static std::string langRegion; static std::string osName; static std::string gpuDriverVersion; +static std::string restartArgs; + HMENU g_hPopupMenus; int g_activeWindow = 0; @@ -283,6 +285,7 @@ void System_SendMessage(const char *command, const char *parameter) { PostMessage(MainWindow::GetHWND(), WM_CLOSE, 0, 0); } } else if (!strcmp(command, "graphics_restart")) { + restartArgs = parameter == nullptr ? "" : parameter; if (IsDebuggerPresent()) { PostMessage(MainWindow::GetHWND(), MainWindow::WM_USER_RESTART_EMUTHREAD, 0, 0); } else { @@ -415,7 +418,14 @@ static bool DetectVulkanInExternalProcess() { std::vector GetWideCmdLine() { wchar_t **wargv; int wargc = -1; - wargv = CommandLineToArgvW(GetCommandLineW(), &wargc); + // This is used for the WM_USER_RESTART_EMUTHREAD path. + if (!restartArgs.empty()) { + std::wstring wargs = ConvertUTF8ToWString("PPSSPP " + restartArgs); + wargv = CommandLineToArgvW(wargs.c_str(), &wargc); + restartArgs.clear(); + } else { + wargv = CommandLineToArgvW(GetCommandLineW(), &wargc); + } std::vector wideArgs(wargv, wargv + wargc); LocalFree(wargv); @@ -447,12 +457,12 @@ static void WinMainInit() { } static void WinMainCleanup() { - if (g_Config.bRestartRequired) { - W32Util::ExitAndRestart(); - } - net::Shutdown(); CoUninitialize(); + + if (g_Config.bRestartRequired) { + W32Util::ExitAndRestart(!restartArgs.empty(), restartArgs); + } } int WINAPI WinMain(HINSTANCE _hInstance, HINSTANCE hPrevInstance, LPSTR szCmdLine, int iCmdShow) { diff --git a/android/jni/app-android.cpp b/android/jni/app-android.cpp index 88b66a00e9..649ecade8a 100644 --- a/android/jni/app-android.cpp +++ b/android/jni/app-android.cpp @@ -399,6 +399,60 @@ extern "C" jstring Java_org_ppsspp_ppsspp_NativeApp_queryConfig return jresult; } +static void parse_args(std::vector &args, const std::string value) { + // Simple argument parser so we can take args from extra params. + const char *p = value.c_str(); + + while (*p != '\0') { + while (isspace(*p)) { + p++; + } + if (*p == '\0') { + break; + } + + bool done = false; + bool quote = false; + std::string arg; + + while (!done) { + size_t sz = strcspn(p, "\"\\ \r\n\t"); + arg += std::string(p, sz); + p += sz; + + switch (*p) { + case '"': + quote = !quote; + p++; + break; + + case '\\': + p++; + arg += std::string(p, 1); + p++; + break; + + case '\0': + done = true; + break; + + default: + // If it's not the above, it's whitespace. + if (!quote) { + done = true; + } + break; + } + } + + args.push_back(arg); + + while (isspace(*p)) { + p++; + } + } +} + extern "C" void Java_org_ppsspp_ppsspp_NativeApp_init (JNIEnv *env, jclass, jstring jmodel, jint jdeviceType, jstring jlangRegion, jstring japkpath, jstring jdataDir, jstring jexternalDir, jstring jlibraryDir, jstring jcacheDir, jstring jshortcutParam, @@ -455,18 +509,22 @@ extern "C" void Java_org_ppsspp_ppsspp_NativeApp_init NativeGetAppInfo(&app_name, &app_nice_name, &landscape, &version); - // If shortcut_param is not empty, pass it as additional varargs argument to NativeInit() method. + // If shortcut_param is not empty, pass it as additional arguments to the NativeInit() method. // NativeInit() is expected to treat extra argument as boot_filename, which in turn will start game immediately. // NOTE: Will only work if ppsspp started from Activity.onCreate(). Won't work if ppsspp app start from onResume(). - if (shortcut_param.empty()) { - const char *argv[2] = {app_name.c_str(), 0}; - NativeInit(1, argv, user_data_path.c_str(), externalDir.c_str(), cacheDir.c_str()); - } else { - const char *argv[3] = {app_name.c_str(), shortcut_param.c_str(), 0}; - NativeInit(2, argv, user_data_path.c_str(), externalDir.c_str(), cacheDir.c_str()); + std::vector args; + std::vector temp; + args.push_back(app_name.c_str()); + if (!shortcut_param.empty()) { + parse_args(temp, shortcut_param); + for (const auto &arg : temp) { + args.push_back(arg.c_str()); + } } + NativeInit((int)args.size(), &args[0], user_data_path.c_str(), externalDir.c_str(), cacheDir.c_str()); + retry: // Now that we've loaded config, set javaGL. javaGL = NativeQueryConfig("androidJavaGL") == "true"; @@ -968,14 +1026,21 @@ void getDesiredBackbufferSize(int &sz_x, int &sz_y) { extern "C" void JNICALL Java_org_ppsspp_ppsspp_NativeApp_setDisplayParameters(JNIEnv *, jclass, jint xres, jint yres, jint dpi, jfloat refreshRate) { ILOG("NativeApp.setDisplayParameters(%d x %d, dpi=%d, refresh=%0.2f)", xres, yres, dpi, refreshRate); - display_xres = xres; - display_yres = yres; - display_dpi_x = dpi; - display_dpi_y = dpi; - display_hz = refreshRate; + bool changed = false; + changed = changed || display_xres != xres || display_yres != yres; + changed = changed || display_dpi_x != dpi || display_dpi_y != dpi; + changed = changed || display_hz != refreshRate; - recalculateDpi(); - NativeResized(); + if (changed) { + display_xres = xres; + display_yres = yres; + display_dpi_x = dpi; + display_dpi_y = dpi; + display_hz = refreshRate; + + recalculateDpi(); + NativeResized(); + } } extern "C" void JNICALL Java_org_ppsspp_ppsspp_NativeApp_computeDesiredBackbufferDimensions() { diff --git a/android/src/org/ppsspp/ppsspp/NativeActivity.java b/android/src/org/ppsspp/ppsspp/NativeActivity.java index 2a2b986702..2b6f2d4890 100644 --- a/android/src/org/ppsspp/ppsspp/NativeActivity.java +++ b/android/src/org/ppsspp/ppsspp/NativeActivity.java @@ -28,25 +28,17 @@ import android.os.PowerManager; import android.os.Vibrator; import android.provider.MediaStore; import android.text.InputType; -import android.util.DisplayMetrics; import android.util.Log; -import android.view.Display; -import android.view.DisplayCutout; import android.view.Gravity; import android.view.HapticFeedbackConstants; import android.view.InputDevice; import android.view.InputEvent; import android.view.KeyEvent; import android.view.MotionEvent; -import android.view.OrientationEventListener; import android.view.Surface; -import android.view.SurfaceHolder; import android.view.SurfaceView; import android.view.View; -import android.view.View.OnSystemUiVisibilityChangeListener; -import android.view.ViewGroup; import android.view.Window; -import android.view.WindowInsets; import android.view.WindowManager; import android.view.inputmethod.InputMethodManager; import android.widget.EditText; @@ -59,7 +51,7 @@ import java.util.Locale; import java.util.regex.Matcher; import java.util.regex.Pattern; -public abstract class NativeActivity extends Activity implements SurfaceHolder.Callback { +public abstract class NativeActivity extends Activity { // Remember to loadLibrary your JNI .so in a static {} block // Adjust these as necessary @@ -81,6 +73,7 @@ public abstract class NativeActivity extends Activity implements SurfaceHolder.C protected NativeRenderer nativeRenderer; private String shortcutParam = ""; + private static String overrideShortcutParam = null; public static String runCommand; public static String commandParameter; @@ -91,7 +84,6 @@ public abstract class NativeActivity extends Activity implements SurfaceHolder.C private boolean sustainedPerfSupported; - private boolean navigationHidden; private View navigationCallbackView = null; // audioFocusChangeListener to listen to changes in audio state @@ -116,20 +108,10 @@ public abstract class NativeActivity extends Activity implements SurfaceHolder.C private String inputPlayerADesc; private PowerSaveModeReceiver mPowerSaveModeReceiver = null; - + private SizeManager sizeManager = null; private static LocationHelper mLocationHelper; private static CameraHelper mCameraHelper; - private float densityDpi; - private float refreshRate; - private int pixelWidth; - private int pixelHeight; - - private int safeInsetLeft = 0; - private int safeInsetRight = 0; - private int safeInsetTop = 0; - private int safeInsetBottom = 0; - private static final String[] permissionsForStorage = { Manifest.permission.WRITE_EXTERNAL_STORAGE, }; @@ -319,9 +301,11 @@ public abstract class NativeActivity extends Activity implements SurfaceHolder.C String model = Build.MANUFACTURER + ":" + Build.MODEL; String languageRegion = Locale.getDefault().getLanguage() + "_" + Locale.getDefault().getCountry(); + String shortcut = overrideShortcutParam == null ? shortcutParam : overrideShortcutParam; + overrideShortcutParam = null; NativeApp.audioConfig(optimalFramesPerBuffer, optimalSampleRate); - NativeApp.init(model, deviceType, languageRegion, apkFilePath, dataDir, externalStorageDir, libraryDir, cacheDir, shortcutParam, Build.VERSION.SDK_INT, Build.BOARD); + NativeApp.init(model, deviceType, languageRegion, apkFilePath, dataDir, externalStorageDir, libraryDir, cacheDir, shortcut, Build.VERSION.SDK_INT, Build.BOARD); // Allow C++ to tell us to use JavaGL or not. javaGL = "true".equalsIgnoreCase(NativeApp.queryConfig("androidJavaGL")); @@ -437,7 +421,7 @@ public abstract class NativeActivity extends Activity implements SurfaceHolder.C } else { Log.e(TAG, "updateSystemUiVisibility: decor view not yet created, ignoring for now"); } - updateDisplayMeasurements(); + sizeManager.checkDisplayMeasurements(); } // Need API 11 to check for existence of a vibrator? Zany. @@ -467,48 +451,10 @@ public abstract class NativeActivity extends Activity implements SurfaceHolder.C // Tells the render loop thread to exit, so we can restart it. public native void exitEGLRenderLoop(); - public void getDesiredBackbufferSize(Point sz) { - NativeApp.computeDesiredBackbufferDimensions(); - sz.x = NativeApp.getDesiredBackbufferWidth(); - sz.y = NativeApp.getDesiredBackbufferHeight(); - } - - private SurfaceView getSurfaceView() { - if (mGLSurfaceView != null) { - return mGLSurfaceView; - } else { - return mSurfaceView; - } - } - - @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1) - public void updateDisplayMeasurements() { - Display display = getWindowManager().getDefaultDisplay(); - - // Early in startup, we don't have a view to query. Do our best to get some kind of size - // that can be used by config default heuristics, and so on. - DisplayMetrics metrics = new DisplayMetrics(); - if (navigationHidden && Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { - display.getRealMetrics(metrics); - } else { - display.getMetrics(metrics); - } - - // Later on, we have the exact pixel size so let's just use it. - SurfaceView view = getSurfaceView(); - if (view != null) { - metrics.widthPixels = view.getWidth(); - metrics.heightPixels = view.getHeight(); - } - densityDpi = metrics.densityDpi; - refreshRate = display.getRefreshRate(); - - NativeApp.setDisplayParameters(metrics.widthPixels, metrics.heightPixels, (int) densityDpi, refreshRate); - } - @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + sizeManager = new SizeManager(this); TextRenderer.init(this); shuttingDown = false; registerCallbacks(); @@ -516,7 +462,7 @@ public abstract class NativeActivity extends Activity implements SurfaceHolder.C // This calls NativeApp.setDisplayParameters. Make sure that's done early in order // to be able to set defaults when loading config for the first time. Like figuring out // whether to start at 1x or 2x. - updateDisplayMeasurements(); + sizeManager.updateDisplayMeasurements(); if (!initialized) { Initialize(); @@ -540,7 +486,7 @@ public abstract class NativeActivity extends Activity implements SurfaceHolder.C mGLSurfaceView = new NativeGLView(this); nativeRenderer = new NativeRenderer(this); mGLSurfaceView.setEGLContextClientVersion(2); - mGLSurfaceView.getHolder().addCallback(NativeActivity.this); + sizeManager.setSurfaceView(mGLSurfaceView); // Setup the GLSurface and ask android for the correct // Number of bits for r, g, b, a, depth and stencil components @@ -572,93 +518,28 @@ public abstract class NativeActivity extends Activity implements SurfaceHolder.C } mGLSurfaceView.setRenderer(nativeRenderer); setContentView(mGLSurfaceView); - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { - mGLSurfaceView.setOnApplyWindowInsetsListener(new View.OnApplyWindowInsetsListener() { - @Override - public WindowInsets onApplyWindowInsets(View view, WindowInsets windowInsets) { - checkInsets(windowInsets); - return windowInsets; - } - }); - } } else { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { updateSystemUiVisibility(); } mSurfaceView = new NativeSurfaceView(NativeActivity.this); - mSurfaceView.getHolder().addCallback(NativeActivity.this); + sizeManager.setSurfaceView(mSurfaceView); Log.i(TAG, "setcontentview before"); setContentView(mSurfaceView); Log.i(TAG, "setcontentview after"); ensureRenderLoop(); - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { - mSurfaceView.setOnApplyWindowInsetsListener(new View.OnApplyWindowInsetsListener() { - @Override - public WindowInsets onApplyWindowInsets(View view, WindowInsets windowInsets) { - checkInsets(windowInsets); - return windowInsets; - } - }); - } } } - private Point desiredSize = new Point(); - private int badOrientationCount = 0; - - @Override - public void surfaceCreated(SurfaceHolder holder) { - pixelWidth = holder.getSurfaceFrame().width(); - pixelHeight = holder.getSurfaceFrame().height(); - - // Workaround for terrible bug when locking and unlocking the screen in landscape mode on Nexus 5X. - int requestedOr = getRequestedOrientation(); - boolean requestedPortrait = requestedOr == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT || requestedOr == ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT; - boolean detectedPortrait = pixelHeight > pixelWidth; - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT && badOrientationCount < 3 && requestedPortrait != detectedPortrait && requestedOr != ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED) { - Log.e(TAG, "Bad orientation detected (w=" + pixelWidth + " h=" + pixelHeight + "! Recreating activity."); - badOrientationCount++; - recreate(); - return; - } else if (requestedPortrait == detectedPortrait) { - Log.i(TAG, "Correct orientation detected, resetting orientation counter."); - badOrientationCount = 0; - } else { - Log.i(TAG, "Bad orientation detected but ignored" + (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT ? " (sdk version)" : "")); - } - - Log.d(TAG, "Surface created. pixelWidth=" + pixelWidth + ", pixelHeight=" + pixelHeight + " holder: " + holder.toString() + " or: " + requestedOr); - NativeApp.setDisplayParameters(pixelWidth, pixelHeight, (int) densityDpi, refreshRate); - getDesiredBackbufferSize(desiredSize); - - // Note that desiredSize might be 0,0 here - but that's fine when calling setFixedSize! It means auto. - Log.d(TAG, "Setting fixed size " + desiredSize.x + " x " + desiredSize.y); - holder.setFixedSize(desiredSize.x, desiredSize.y); - } - @Override public void onWindowFocusChanged(boolean hasFocus) { super.onWindowFocusChanged(hasFocus); updateSustainedPerformanceMode(); } - @Override - public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { - Log.v(TAG, "surfaceChanged: isCreating:" + holder.isCreating() + " holder: " + holder.toString()); - if (holder.isCreating() && desiredSize.x > 0 && desiredSize.y > 0) { - // We have called setFixedSize which will trigger another surfaceChanged after the initial - // one. This one is the original one and we don't care about it. - Log.w(TAG, "holder.isCreating = true, ignoring. width=" + width + " height=" + height + " desWidth=" + desiredSize.x + " desHeight=" + desiredSize.y); - return; - } - Log.w(TAG, "Surface changed. Resolution: " + width + "x" + height + " Format: " + format); - // The window size might have changed (immersive mode, native fullscreen on some devices) - NativeApp.backbufferResize(width, height, format); - updateDisplayMeasurements(); - mSurface = holder.getSurface(); + public void notifySurface(Surface surface) { + mSurface = surface; if (!javaGL) { // If we got a surface, this starts the thread. If not, it doesn't. if (mSurface == null) { @@ -670,17 +551,6 @@ public abstract class NativeActivity extends Activity implements SurfaceHolder.C updateSustainedPerformanceMode(); } - @Override - public void surfaceDestroyed(SurfaceHolder holder) { - mSurface = null; - Log.w(TAG, "Surface destroyed."); - if (!javaGL) { - joinRenderLoopThread(); - } - // Autosize the next created surface. - holder.setSizeFromLayout(); - } - // Invariants: After this, mRenderLoopThread will be set, and the thread will be running. protected synchronized void ensureRenderLoop() { if (javaGL) { @@ -728,20 +598,7 @@ public abstract class NativeActivity extends Activity implements SurfaceHolder.C return; } - decorView.setOnSystemUiVisibilityChangeListener(new OnSystemUiVisibilityChangeListener() { - @Override - public void onSystemUiVisibilityChange(int visibility) { - // Called when the system UI's visibility changes, regardless of - // whether it's because of our or system actions. - // We will try to force it to follow our preference but will not stupidly - // act as if it's visible if it's not. - navigationHidden = ((visibility & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) != 0); - // TODO: Check here if it's the state we want. - Log.i(TAG, "SystemUiVisibilityChange! visibility=" + visibility + " navigationHidden: " + navigationHidden); - Log.i(TAG, "decorView: " + decorView.getWidth() + "x" + decorView.getHeight()); - updateDisplayMeasurements(); - } - }); + sizeManager.setupSystemUiCallback(decorView); navigationCallbackView = decorView; } @@ -776,6 +633,7 @@ public abstract class NativeActivity extends Activity implements SurfaceHolder.C mSurfaceView.onDestroy(); mSurfaceView = null; } + sizeManager.setSurfaceView(null); if (mPowerSaveModeReceiver != null) { mPowerSaveModeReceiver.destroy(this); mPowerSaveModeReceiver = null; @@ -864,29 +722,6 @@ public abstract class NativeActivity extends Activity implements SurfaceHolder.C } } - private void checkInsets(WindowInsets insets) { - if (insets == null) { - return; - } - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { - DisplayCutout cutout = insets.getDisplayCutout(); - if (cutout != null) { - safeInsetLeft = cutout.getSafeInsetLeft(); - safeInsetRight = cutout.getSafeInsetRight(); - safeInsetTop = cutout.getSafeInsetTop(); - safeInsetBottom = cutout.getSafeInsetBottom(); - Log.i(TAG, "Safe insets: left: " + safeInsetLeft + " right: " + safeInsetRight + " top: " + safeInsetTop + " bottom: " + safeInsetBottom); - } else { - Log.i(TAG, "Cutout was null"); - safeInsetLeft = 0; - safeInsetRight = 0; - safeInsetTop = 0; - safeInsetBottom = 0; - } - NativeApp.sendMessage("safe_insets", safeInsetLeft + ":" + safeInsetRight + ":" + safeInsetTop + ":" + safeInsetBottom); - } - } - @Override public void onAttachedToWindow() { Log.i(TAG, "onAttachedToWindow"); @@ -905,7 +740,7 @@ public abstract class NativeActivity extends Activity implements SurfaceHolder.C updateSystemUiVisibility(); } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { - densityDpi = (float)newConfig.densityDpi; + sizeManager.updateDpi((float)newConfig.densityDpi); } } @@ -914,7 +749,7 @@ public abstract class NativeActivity extends Activity implements SurfaceHolder.C // onConfigurationChanged not called on multi-window change Log.i(TAG, "onMultiWindowModeChanged: isInMultiWindowMode = " + isInMultiWindowMode); super.onMultiWindowModeChanged(isInMultiWindowMode, newConfig); - updateDisplayMeasurements(); + sizeManager.checkDisplayMeasurements(); } // keep this static so we can call this even if we don't @@ -1420,6 +1255,9 @@ public abstract class NativeActivity extends Activity implements SurfaceHolder.C recreate(); } else if (command.equals("graphics_restart")) { Log.i(TAG, "graphics_restart"); + if (params != null && !params.equals("")) { + overrideShortcutParam = params; + } shuttingDown = true; recreate(); } else if (command.equals("ask_permission") && params.equals("storage")) { @@ -1456,6 +1294,7 @@ public abstract class NativeActivity extends Activity implements SurfaceHolder.C if (params.equals("ingame")) { // Keep the screen bright - very annoying if it goes dark when tilting away window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + updateSustainedPerformanceMode(); } else { // Only keep the screen bright ingame. window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); diff --git a/android/src/org/ppsspp/ppsspp/PpssppActivity.java b/android/src/org/ppsspp/ppsspp/PpssppActivity.java index f6496168b8..4269f7f98b 100644 --- a/android/src/org/ppsspp/ppsspp/PpssppActivity.java +++ b/android/src/org/ppsspp/ppsspp/PpssppActivity.java @@ -12,6 +12,8 @@ public class PpssppActivity extends NativeActivity { private static final String TAG = "PpssppActivity"; // Key used by shortcut. public static final String SHORTCUT_EXTRA_KEY = "org.ppsspp.ppsspp.Shortcuts"; + // Key used for debugging. + public static final String ARGS_EXTRA_KEY = "org.ppsspp.ppsspp.Args"; private static boolean m_hasUnsupportedABI = false; private static boolean m_hasNoNativeBinary = false; @@ -78,14 +80,18 @@ public class PpssppActivity extends NativeActivity { if (data != null) { String path = intent.getData().getPath(); Log.i(TAG, "Found Shortcut Parameter in data: " + path); - super.setShortcutParam(path); + super.setShortcutParam("\"" + path.replace("\\", "\\\\").replace("\"", "\\\"") + "\""); // Toast.makeText(getApplicationContext(), path, Toast.LENGTH_SHORT).show(); } else { String param = getIntent().getStringExtra(SHORTCUT_EXTRA_KEY); + String args = getIntent().getStringExtra(ARGS_EXTRA_KEY); Log.e(TAG, "Got ACTION_VIEW without a valid uri, trying param"); if (param != null) { Log.i(TAG, "Found Shortcut Parameter in extra-data: " + param); - super.setShortcutParam(getIntent().getStringExtra(SHORTCUT_EXTRA_KEY)); + super.setShortcutParam("\"" + param.replace("\\", "\\\\").replace("\"", "\\\"") + "\""); + } else if (args != null) { + Log.i(TAG, "Found args parameter in extra-data: " + args); + super.setShortcutParam(args); } else { Log.e(TAG, "Shortcut missing parameter!"); super.setShortcutParam(""); diff --git a/android/src/org/ppsspp/ppsspp/SizeManager.java b/android/src/org/ppsspp/ppsspp/SizeManager.java new file mode 100644 index 0000000000..d5977e974a --- /dev/null +++ b/android/src/org/ppsspp/ppsspp/SizeManager.java @@ -0,0 +1,214 @@ +package org.ppsspp.ppsspp; + +import android.annotation.TargetApi; +import android.app.Activity; +import android.content.pm.ActivityInfo; +import android.graphics.Point; +import android.os.Build; +import android.os.Handler; +import android.os.Looper; +import android.util.DisplayMetrics; +import android.util.Log; +import android.view.Display; +import android.view.DisplayCutout; +import android.view.SurfaceHolder; +import android.view.SurfaceView; +import android.view.View; +import android.view.WindowInsets; +import java.lang.Runnable; + +public class SizeManager implements SurfaceHolder.Callback { + private static String TAG = "PPSSPPSizeManager"; + + final NativeActivity activity; + SurfaceView surfaceView = null; + + private int safeInsetLeft = 0; + private int safeInsetRight = 0; + private int safeInsetTop = 0; + private int safeInsetBottom = 0; + + private float densityDpi; + private float refreshRate; + private int pixelWidth; + private int pixelHeight; + + private boolean navigationHidden = false; + private boolean displayUpdatePending = false; + + private Point desiredSize = new Point(); + private int badOrientationCount = 0; + + public SizeManager(final NativeActivity a) { + activity = a; + } + + @TargetApi(Build.VERSION_CODES.P) + public void setSurfaceView(SurfaceView view) { + surfaceView = view; + if (surfaceView == null) + return; + + surfaceView.getHolder().addCallback(this); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + surfaceView.setOnApplyWindowInsetsListener(new View.OnApplyWindowInsetsListener() { + @Override + public WindowInsets onApplyWindowInsets(View view, WindowInsets windowInsets) { + updateInsets(windowInsets); + return windowInsets; + } + }); + } + } + + @Override + public void surfaceCreated(SurfaceHolder holder) { + pixelWidth = holder.getSurfaceFrame().width(); + pixelHeight = holder.getSurfaceFrame().height(); + + // Workaround for terrible bug when locking and unlocking the screen in landscape mode on Nexus 5X. + int requestedOr = activity.getRequestedOrientation(); + boolean requestedPortrait = requestedOr == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT || requestedOr == ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT; + boolean detectedPortrait = pixelHeight > pixelWidth; + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT && badOrientationCount < 3 && requestedPortrait != detectedPortrait && requestedOr != ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED) { + Log.e(TAG, "Bad orientation detected (w=" + pixelWidth + " h=" + pixelHeight + "! Recreating activity."); + badOrientationCount++; + activity.recreate(); + return; + } else if (requestedPortrait == detectedPortrait) { + Log.i(TAG, "Correct orientation detected, resetting orientation counter."); + badOrientationCount = 0; + } else { + Log.i(TAG, "Bad orientation detected but ignored" + (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT ? " (sdk version)" : "")); + } + + Log.d(TAG, "Surface created. pixelWidth=" + pixelWidth + ", pixelHeight=" + pixelHeight + " holder: " + holder.toString() + " or: " + requestedOr); + NativeApp.setDisplayParameters(pixelWidth, pixelHeight, (int)densityDpi, refreshRate); + getDesiredBackbufferSize(desiredSize); + + // Note that desiredSize might be 0,0 here - but that's fine when calling setFixedSize! It means auto. + Log.d(TAG, "Setting fixed size " + desiredSize.x + " x " + desiredSize.y); + holder.setFixedSize(desiredSize.x, desiredSize.y); + } + + @Override + public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { + Log.v(TAG, "surfaceChanged: isCreating:" + holder.isCreating() + " holder: " + holder.toString()); + if (holder.isCreating() && desiredSize.x > 0 && desiredSize.y > 0) { + // We have called setFixedSize which will trigger another surfaceChanged after the initial + // one. This one is the original one and we don't care about it. + Log.w(TAG, "holder.isCreating = true, ignoring. width=" + width + " height=" + height + " desWidth=" + desiredSize.x + " desHeight=" + desiredSize.y); + return; + } + + Log.w(TAG, "Surface changed. Resolution: " + width + "x" + height + " Format: " + format); + // The window size might have changed (immersive mode, native fullscreen on some devices) + NativeApp.backbufferResize(width, height, format); + updateDisplayMeasurements(); + + activity.notifySurface(holder.getSurface()); + } + + @Override + public void surfaceDestroyed(SurfaceHolder holder) { + activity.notifySurface(null); + + // Autosize the next created surface. + holder.setSizeFromLayout(); + } + + public void checkDisplayMeasurements() { + if (displayUpdatePending) { + return; + } + displayUpdatePending = true; + + final Runnable updater = new Runnable() { + public void run() { + Log.d(TAG, "checkDisplayMeasurements: checking now"); + updateDisplayMeasurements(); + } + }; + + final Handler handler = new Handler(Looper.getMainLooper()); + handler.postDelayed(updater, 10); + } + + @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1) + public void updateDisplayMeasurements() { + displayUpdatePending = false; + + Display display = activity.getWindowManager().getDefaultDisplay(); + // Early in startup, we don't have a view to query. Do our best to get some kind of size + // that can be used by config default heuristics, and so on. + DisplayMetrics metrics = new DisplayMetrics(); + if (navigationHidden && Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { + display.getRealMetrics(metrics); + } else { + display.getMetrics(metrics); + } + + // Later on, we have the exact pixel size so let's just use it. + if (surfaceView != null) { + metrics.widthPixels = surfaceView.getWidth(); + metrics.heightPixels = surfaceView.getHeight(); + } + densityDpi = metrics.densityDpi; + refreshRate = display.getRefreshRate(); + + NativeApp.setDisplayParameters(metrics.widthPixels, metrics.heightPixels, (int)densityDpi, refreshRate); + } + + @TargetApi(Build.VERSION_CODES.KITKAT) + public void setupSystemUiCallback(final View view) { + view.setOnSystemUiVisibilityChangeListener(new View.OnSystemUiVisibilityChangeListener() { + @Override + public void onSystemUiVisibilityChange(int visibility) { + // Called when the system UI's visibility changes, regardless of + // whether it's because of our or system actions. + // We will try to force it to follow our preference but will not stupidly + // act as if it's visible if it's not. + navigationHidden = ((visibility & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) != 0); + // TODO: Check here if it's the state we want. + Log.i(TAG, "SystemUiVisibilityChange! visibility=" + visibility + " navigationHidden: " + navigationHidden); + Log.i(TAG, "decorView: " + view.getWidth() + "x" + view.getHeight()); + checkDisplayMeasurements(); + } + }); + } + + public void updateDpi(float dpi) { + densityDpi = dpi; + } + + private void getDesiredBackbufferSize(Point sz) { + NativeApp.computeDesiredBackbufferDimensions(); + sz.x = NativeApp.getDesiredBackbufferWidth(); + sz.y = NativeApp.getDesiredBackbufferHeight(); + } + + @TargetApi(Build.VERSION_CODES.P) + private void updateInsets(WindowInsets insets) { + if (insets == null) { + return; + } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + DisplayCutout cutout = insets.getDisplayCutout(); + if (cutout != null) { + safeInsetLeft = cutout.getSafeInsetLeft(); + safeInsetRight = cutout.getSafeInsetRight(); + safeInsetTop = cutout.getSafeInsetTop(); + safeInsetBottom = cutout.getSafeInsetBottom(); + Log.i(TAG, "Safe insets: left: " + safeInsetLeft + " right: " + safeInsetRight + " top: " + safeInsetTop + " bottom: " + safeInsetBottom); + } else { + Log.i(TAG, "Safe insets: Cutout was null"); + safeInsetLeft = 0; + safeInsetRight = 0; + safeInsetTop = 0; + safeInsetBottom = 0; + } + NativeApp.sendMessage("safe_insets", safeInsetLeft + ":" + safeInsetRight + ":" + safeInsetTop + ":" + safeInsetBottom); + } + } +} diff --git a/ext/native/gfx_es2/gpu_features.cpp b/ext/native/gfx_es2/gpu_features.cpp index 757ccb43f3..ca4df8c26a 100644 --- a/ext/native/gfx_es2/gpu_features.cpp +++ b/ext/native/gfx_es2/gpu_features.cpp @@ -38,9 +38,9 @@ PFNGLISVERTEXARRAYOESPROC glIsVertexArrayOES; GLExtensions gl_extensions; std::string g_all_gl_extensions; -std::set g_set_gl_extensions; +static std::set g_set_gl_extensions; std::string g_all_egl_extensions; -std::set g_set_egl_extensions; +static std::set g_set_egl_extensions; static bool extensionsDone = false; static bool useCoreContext = false; @@ -564,6 +564,15 @@ void SetGLCoreContext(bool flag) { gl_extensions.IsCoreContext = useCoreContext; } +void ResetGLExtensions() { + extensionsDone = false; + + gl_extensions = {}; + gl_extensions.IsCoreContext = useCoreContext; + g_all_gl_extensions.clear(); + g_all_egl_extensions.clear(); +} + static const char *glsl_fragment_prelude = "#ifdef GL_ES\n" "precision mediump float;\n" diff --git a/ext/native/gfx_es2/gpu_features.h b/ext/native/gfx_es2/gpu_features.h index 95a57f095b..4617e544a4 100644 --- a/ext/native/gfx_es2/gpu_features.h +++ b/ext/native/gfx_es2/gpu_features.h @@ -120,5 +120,6 @@ extern std::string g_all_egl_extensions; void CheckGLExtensions(); void SetGLCoreContext(bool flag); +void ResetGLExtensions(); std::string ApplyGLSLPrelude(const std::string &source, uint32_t stage); diff --git a/ext/native/ui/screen.cpp b/ext/native/ui/screen.cpp index a5e105aa56..958c1d70c3 100644 --- a/ext/native/ui/screen.cpp +++ b/ext/native/ui/screen.cpp @@ -8,7 +8,6 @@ #include "ui/view.h" ScreenManager::ScreenManager() { - nextScreen_ = 0; uiContext_ = 0; dialogFinished_ = 0; } @@ -18,7 +17,7 @@ ScreenManager::~ScreenManager() { } void ScreenManager::switchScreen(Screen *screen) { - if (screen == nextScreen_) { + if (!nextStack_.empty() && screen == nextStack_.front().screen) { ELOG("Already switching to this screen"); return; } @@ -26,23 +25,23 @@ void ScreenManager::switchScreen(Screen *screen) { // will only become apparent if the dialog is closed. The previous screen will stick around // until that switch. // TODO: is this still true? - if (nextScreen_ != 0) { - ELOG("Already had a nextScreen_! Asynchronous open while doing something? Deleting the new screen."); + if (!nextStack_.empty()) { + ELOG("Already had a nextStack_! Asynchronous open while doing something? Deleting the new screen."); delete screen; return; } - if (screen == 0) { + if (screen == nullptr) { WLOG("Swiching to a zero screen, this can't be good"); } if (stack_.empty() || screen != stack_.back().screen) { - nextScreen_ = screen; - nextScreen_->setScreenManager(this); + screen->setScreenManager(this); + nextStack_.push_back({ screen, 0 }); } } void ScreenManager::update() { std::lock_guard guard(inputLock_); - if (nextScreen_) { + if (!nextStack_.empty()) { switchToNext(); } @@ -53,22 +52,25 @@ void ScreenManager::update() { void ScreenManager::switchToNext() { std::lock_guard guard(inputLock_); - if (!nextScreen_) { - ELOG("switchToNext: No nextScreen_!"); + if (nextStack_.empty()) { + ELOG("switchToNext: No nextStack_!"); } - Layer temp = {0, 0}; + Layer temp = {nullptr, 0}; if (!stack_.empty()) { temp = stack_.back(); stack_.pop_back(); } - Layer newLayer = {nextScreen_, 0}; - stack_.push_back(newLayer); + stack_.push_back(nextStack_.front()); if (temp.screen) { delete temp.screen; } - nextScreen_ = 0; - UI::SetFocusedView(0); + UI::SetFocusedView(nullptr); + + for (size_t i = 1; i < nextStack_.size(); ++i) { + stack_.push_back(nextStack_[i]); + } + nextStack_.clear(); } bool ScreenManager::touch(const TouchInput &touch) { @@ -197,19 +199,16 @@ Screen *ScreenManager::topScreen() const { void ScreenManager::shutdown() { std::lock_guard guard(inputLock_); - for (auto x = stack_.begin(); x != stack_.end(); x++) - delete x->screen; + for (auto layer : stack_) + delete layer.screen; stack_.clear(); - delete nextScreen_; - nextScreen_ = nullptr; + for (auto layer : nextStack_) + delete layer.screen; + nextStack_.clear(); } void ScreenManager::push(Screen *screen, int layerFlags) { std::lock_guard guard(inputLock_); - if (nextScreen_ && stack_.empty()) { - // we're during init, this is OK - switchToNext(); - } screen->setScreenManager(this); if (screen->isTransparent()) { layerFlags |= LAYER_TRANSPARENT; @@ -224,7 +223,10 @@ void ScreenManager::push(Screen *screen, int layerFlags) { touch(input); Layer layer = {screen, layerFlags}; - stack_.push_back(layer); + if (nextStack_.empty()) + stack_.push_back(layer); + else + nextStack_.push_back(layer); } void ScreenManager::pop() { diff --git a/ext/native/ui/screen.h b/ext/native/ui/screen.h index abca7f9769..0b97108afd 100644 --- a/ext/native/ui/screen.h +++ b/ext/native/ui/screen.h @@ -147,7 +147,6 @@ private: void switchToNext(); void processFinishDialog(); - Screen *nextScreen_; UIContext *uiContext_; Draw::DrawContext *thin3DContext_; @@ -166,4 +165,5 @@ private: // Dialog stack. These are shown "on top" of base screens and the Android back button works as expected. // Used for options, in-game menus and other things you expect to be able to back out from onto something. std::vector stack_; + std::vector nextStack_; };