From 2febc16e2e65d440e28e06f856729c9edc95dca9 Mon Sep 17 00:00:00 2001 From: dbaronica Date: Sat, 5 Feb 2022 12:46:42 +0100 Subject: [PATCH] =?UTF-8?q?(UWP)=20-=20Implemented=20activation/starting?= =?UTF-8?q?=20of=20retroarch=20using=20URI=20activa=E2=80=A6=20(#13563)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * (UWP) - Implemented activation/starting of retroarch using URI activation. * Removed try-catch blocks, refactored argument parsing, ignore driver reset if content starting directly from arguments Co-authored-by: Domagoj Baronica --- .../Package.appxmanifest | 7 + .../Package.appxmanifest | 7 + uwp/uwp_main.cpp | 188 +++++++++++++----- uwp/uwp_main.h | 4 + 4 files changed, 160 insertions(+), 46 deletions(-) diff --git a/pkg/msvc-uwp/RetroArch-msvc2017-UWP/Package.appxmanifest b/pkg/msvc-uwp/RetroArch-msvc2017-UWP/Package.appxmanifest index 53f23c6dab..9a3d243f16 100644 --- a/pkg/msvc-uwp/RetroArch-msvc2017-UWP/Package.appxmanifest +++ b/pkg/msvc-uwp/RetroArch-msvc2017-UWP/Package.appxmanifest @@ -20,6 +20,13 @@ + + + + retroarch + + + diff --git a/pkg/msvc-uwp/RetroArch-msvc2019-UWP/Package.appxmanifest b/pkg/msvc-uwp/RetroArch-msvc2019-UWP/Package.appxmanifest index 5bc58943a2..68bbcfa256 100755 --- a/pkg/msvc-uwp/RetroArch-msvc2019-UWP/Package.appxmanifest +++ b/pkg/msvc-uwp/RetroArch-msvc2019-UWP/Package.appxmanifest @@ -20,6 +20,13 @@ + + + + retroarch + + + diff --git a/uwp/uwp_main.cpp b/uwp/uwp_main.cpp index 71cfa26dce..29919b6164 100644 --- a/uwp/uwp_main.cpp +++ b/uwp/uwp_main.cpp @@ -22,6 +22,8 @@ #include #include #include +#include +#include #include "configuration.h" #include "paths.h" @@ -274,6 +276,9 @@ void App::Initialize(CoreApplicationView^ applicationView) CoreApplication::Resuming += ref new EventHandler(this, &App::OnResuming); + + CoreApplication::EnteredBackground += + ref new EventHandler(this, &App::OnEnteredBackground); } /* Called when the CoreWindow object is created (or re-created). */ @@ -327,52 +332,6 @@ void App::SetWindow(CoreWindow^ window) /* Initializes scene resources, or loads a previously saved app state. */ void App::Load(Platform::String^ entryPoint) { - int ret = rarch_main(NULL, NULL, NULL); - if (ret != 0) - { - RARCH_ERR("Init failed\n"); - CoreApplication::Exit(); - return; - } - m_initialized = true; - - if (is_running_on_xbox()) - { - bool reset = false; - int width = uwp_get_width(); - int height = uwp_get_height(); - //reset driver to d3d11 if set to opengl on boot as cores can just set to gl when needed and there is no good reason to use gl for the menus - settings_t* settings = config_get_ptr(); - char* currentdriver = settings->arrays.video_driver; - if (strcmpi(currentdriver, "gl")==0) - { - //set driver to default - configuration_set_string(settings, - settings->arrays.video_driver, - config_get_default_video()); - //reset needed - reset = true; - } - if ((settings->uints.video_fullscreen_x != width) || (settings->uints.video_fullscreen_y != height)) - { - //get width and height from display again - configuration_set_int(settings, - settings->uints.video_fullscreen_x, - width); - configuration_set_int(settings, - settings->uints.video_fullscreen_y, - height); - //reset needed - reset = true; - } - if (reset) - { - //restart driver - command_event(CMD_EVENT_REINIT, NULL); - } - - } - auto catalog = Windows::ApplicationModel::PackageCatalog::OpenForCurrentPackage(); catalog->PackageInstalling += @@ -421,12 +380,90 @@ void App::Run() void App::Uninitialize() { main_exit(NULL); + + //if this instance of RetroArch was started from another app/frontend and the frontend passed "launchOnExit" parameter: + //1. launch the app specified in "launchOnExit", most likely the same app that started RetroArch + //2. RetroArch goes to background and RunAsyncAndCatchErrors doesn't return, because the target app is immediately started. + //3. explicitly exit in App::OnEnteredBackground if m_launchOnExitShutdown is set. Otherwise, RetroArch doesn't properly shutdown. + if (m_launchOnExit != nullptr && m_launchOnExit->IsEmpty() == false) + { + try + { + //launch the target app + m_launchOnExitShutdown = true; + auto ret = RunAsyncAndCatchErrors([&]() { + return create_task(Launcher::LaunchUriAsync(ref new Uri(m_launchOnExit))); + }, false); + } + catch (Platform::InvalidArgumentException^ e) + { + } + } } /* Application lifecycle event handlers. */ void App::OnActivated(CoreApplicationView^ applicationView, IActivatedEventArgs^ args) { + //start only if not already initialized. If there is a game in progress, just return + if (m_initialized == true) + { + return; + } + + int argc = NULL; + std::vector argv; + std::vector argvTmp; //using std::string as temp buf instead of char* array to avoid manual char allocations + ParseProtocolArgs(args, &argc, &argv, &argvTmp); + + int ret = rarch_main(argc, argv.data(), NULL); + if (ret != 0) + { + RARCH_ERR("Init failed\n"); + CoreApplication::Exit(); + return; + } + m_initialized = true; + + if (is_running_on_xbox()) + { + bool reset = false; + int width = uwp_get_width(); + int height = uwp_get_height(); + //reset driver to d3d11 if set to opengl on boot as cores can just set to gl when needed and there is no good reason to use gl for the menus + //do not change the default driver if the content is already initialized through arguments as this would crash RA for cores that use only ANGLE + settings_t* settings = config_get_ptr(); + content_state_t* p_content = content_state_get_ptr(); + char* currentdriver = settings->arrays.video_driver; + if (strcmpi(currentdriver, "gl") == 0 && p_content->is_inited == false) + { + //set driver to default + configuration_set_string(settings, + settings->arrays.video_driver, + config_get_default_video()); + //reset needed + reset = true; + } + if ((settings->uints.video_fullscreen_x != width) || (settings->uints.video_fullscreen_y != height)) + { + //get width and height from display again + configuration_set_int(settings, + settings->uints.video_fullscreen_x, + width); + configuration_set_int(settings, + settings->uints.video_fullscreen_y, + height); + //reset needed + reset = true; + } + if (reset) + { + //restart driver + command_event(CMD_EVENT_REINIT, NULL); + } + + } + /* Run() won't start until the CoreWindow is activated. */ CoreWindow::GetForCurrentThread()->Activate(); } @@ -485,6 +522,15 @@ void App::OnResuming(Platform::Object^ sender, Platform::Object^ args) */ } +void App::OnEnteredBackground(Platform::Object^ sender, EnteredBackgroundEventArgs^ args) +{ + //RetroArch entered background because another app/frontend was launched on exit, so properly quit + if (m_launchOnExitShutdown == true) + { + CoreApplication::Exit(); + } +} + void App::OnBackRequested(Platform::Object^ sender, Windows::UI::Core::BackRequestedEventArgs^ args) { /* Prevent the B controller button on Xbox One from quitting the app */ @@ -638,6 +684,56 @@ void App::OnPackageInstalling(PackageCatalog^ sender, PackageInstallingEventArgs } } +void App::ParseProtocolArgs(Windows::ApplicationModel::Activation::IActivatedEventArgs^ args, int *argc, std::vector *argv, std::vector *argvTmp) +{ + argvTmp->clear(); + argv->clear(); + + // If the app is activated using protocol, it is expected to be in this format: + // "retroarch:?cmd=&launchOnExit=" + // For example: + // retroarch:?cmd=retroarch -L cores\core_libretro.dll "c:\mypath\path with spaces\game.rom"&launchOnExit=LaunchApp: + // "cmd" and "launchOnExit" are optional. If none specified, it will normally launch into menu + if (args->Kind == ActivationKind::Protocol) + { + ProtocolActivatedEventArgs^ protocolArgs = dynamic_cast(args); + Windows::Foundation::WwwFormUrlDecoder^ query = protocolArgs->Uri->QueryParsed; + + for (int i = 0; i < query->Size; i++) + { + IWwwFormUrlDecoderEntry^ arg = query->GetAt(i); + + //parse RetroArch command line string + if (arg->Name == "cmd") + { + std::wstring wsValue(arg->Value->ToString()->Data()); + std::string strValue(wsValue.begin(), wsValue.end()); + std::istringstream iss(strValue); + std::string s; + + //set escape character to null char to preserve backslashes in paths which are inside quotes, they get stripped by default + while (iss >> std::quoted(s, '"', (char)0)) { + argvTmp->push_back(s); + } + } + else if (arg->Name == "launchOnExit") + { + //if RetroArch UWP app is started using protocol with argument "launchOnExit", this gives an option to launch another app on RA exit, + //making it easy to integrate RA with other UWP frontends + m_launchOnExit = arg->Value; + } + } + } + + (*argc) = argvTmp->size(); + //convert to char* array compatible with argv + for (int i = 0; i < argvTmp->size(); i++) + { + argv->push_back((char*)(argvTmp->at(i)).c_str()); + } + argv->push_back(nullptr); +} + /* Implement UWP equivalents of various win32_* functions */ extern "C" { diff --git a/uwp/uwp_main.h b/uwp/uwp_main.h index a2f2595d45..8be6f321e1 100644 --- a/uwp/uwp_main.h +++ b/uwp/uwp_main.h @@ -37,6 +37,7 @@ namespace RetroArchUWP void OnActivated(Windows::ApplicationModel::Core::CoreApplicationView^ applicationView, Windows::ApplicationModel::Activation::IActivatedEventArgs^ args); void OnSuspending(Platform::Object^ sender, Windows::ApplicationModel::SuspendingEventArgs^ args); void OnResuming(Platform::Object^ sender, Platform::Object^ args); + void OnEnteredBackground(Platform::Object^ sender, Windows::ApplicationModel::EnteredBackgroundEventArgs^ args); void OnBackRequested(Platform::Object^ sender, Windows::UI::Core::BackRequestedEventArgs^ args); @@ -70,6 +71,9 @@ namespace RetroArchUWP bool m_windowVisible; bool m_windowFocused; bool m_windowResized; + Platform::String^ m_launchOnExit; + bool m_launchOnExitShutdown; + void ParseProtocolArgs(Windows::ApplicationModel::Activation::IActivatedEventArgs^ args, int *argc, std::vector *argv, std::vector *argvTmp); static App^ m_instance; }; }