UI: Return to game settings after restart.

This is more convenient.  Also restarts the game if needed.

Added a way to pass args to Android here as well, and fixed displaying an
error if the shortcut file doesn't exist on Android (like if you deleted
it after creating the shortcut.)
This commit is contained in:
Unknown W. Brackets 2020-05-09 13:52:04 -07:00
parent dddaa57392
commit 03fc86a147
13 changed files with 183 additions and 65 deletions

View file

@ -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_;
}

View file

@ -48,6 +48,8 @@ protected:
bool UseVerticalLayout() const;
private:
void TriggerRestart(const char *why);
std::string gameID_;
bool lastVertical_;
UI::CheckBox *enableReportsCheckbox_;

View file

@ -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());

View file

@ -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 {

View file

@ -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> 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());

View file

@ -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);

View file

@ -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);
}

View file

@ -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<std::wstring> 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<std::wstring> 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) {

View file

@ -399,6 +399,60 @@ extern "C" jstring Java_org_ppsspp_ppsspp_NativeApp_queryConfig
return jresult;
}
static void parse_args(std::vector<std::string> &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<const char *> args;
std::vector<std::string> 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";

View file

@ -75,6 +75,7 @@ public abstract class NativeActivity extends Activity {
protected NativeRenderer nativeRenderer;
private String shortcutParam = "";
private static String overrideShortcutParam = null;
public static String runCommand;
public static String commandParameter;
@ -302,9 +303,11 @@ public abstract class NativeActivity extends Activity {
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"));
@ -1254,6 +1257,9 @@ public abstract class NativeActivity extends Activity {
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")) {

View file

@ -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("");

View file

@ -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<std::recursive_mutex> guard(inputLock_);
if (nextScreen_) {
if (!nextStack_.empty()) {
switchToNext();
}
@ -53,22 +52,25 @@ void ScreenManager::update() {
void ScreenManager::switchToNext() {
std::lock_guard<std::recursive_mutex> 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<std::recursive_mutex> 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<std::recursive_mutex> 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() {

View file

@ -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<Layer> stack_;
std::vector<Layer> nextStack_;
};