(UWP) - Implemented activation/starting of retroarch using URI activa… (#13563)

* (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 <domagoj@morpholineinteractive.com>
This commit is contained in:
dbaronica 2022-02-05 12:46:42 +01:00 committed by GitHub
parent 5bc2d42cdc
commit 2febc16e2e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 160 additions and 46 deletions

View File

@ -20,6 +20,13 @@
</uap:DefaultTile> </uap:DefaultTile>
<uap:SplashScreen Image="Assets\SplashScreen.png" BackgroundColor="#000000" /> <uap:SplashScreen Image="Assets\SplashScreen.png" BackgroundColor="#000000" />
</uap:VisualElements> </uap:VisualElements>
<Extensions>
<uap:Extension Category="windows.protocol">
<uap:Protocol Name="retroarch">
<uap:DisplayName>retroarch</uap:DisplayName>
</uap:Protocol>
</uap:Extension>
</Extensions>
</Application> </Application>
</Applications> </Applications>
<Capabilities> <Capabilities>

View File

@ -20,6 +20,13 @@
</uap:DefaultTile> </uap:DefaultTile>
<uap:SplashScreen Image="Assets\SplashScreen.png" BackgroundColor="#000000"/> <uap:SplashScreen Image="Assets\SplashScreen.png" BackgroundColor="#000000"/>
</uap:VisualElements> </uap:VisualElements>
<Extensions>
<uap:Extension Category="windows.protocol">
<uap:Protocol Name="retroarch">
<uap:DisplayName>retroarch</uap:DisplayName>
</uap:Protocol>
</uap:Extension>
</Extensions>
</Application> </Application>
</Applications> </Applications>
<Capabilities> <Capabilities>

View File

@ -22,6 +22,8 @@
#include <lists/string_list.h> #include <lists/string_list.h>
#include <queues/task_queue.h> #include <queues/task_queue.h>
#include <retro_timers.h> #include <retro_timers.h>
#include <sstream>
#include <iomanip>
#include "configuration.h" #include "configuration.h"
#include "paths.h" #include "paths.h"
@ -274,6 +276,9 @@ void App::Initialize(CoreApplicationView^ applicationView)
CoreApplication::Resuming += CoreApplication::Resuming +=
ref new EventHandler<Platform::Object^>(this, &App::OnResuming); ref new EventHandler<Platform::Object^>(this, &App::OnResuming);
CoreApplication::EnteredBackground +=
ref new EventHandler<EnteredBackgroundEventArgs^>(this, &App::OnEnteredBackground);
} }
/* Called when the CoreWindow object is created (or re-created). */ /* 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. */ /* Initializes scene resources, or loads a previously saved app state. */
void App::Load(Platform::String^ entryPoint) 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(); auto catalog = Windows::ApplicationModel::PackageCatalog::OpenForCurrentPackage();
catalog->PackageInstalling += catalog->PackageInstalling +=
@ -421,12 +380,90 @@ void App::Run()
void App::Uninitialize() void App::Uninitialize()
{ {
main_exit(NULL); 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<bool>([&]() {
return create_task(Launcher::LaunchUriAsync(ref new Uri(m_launchOnExit)));
}, false);
}
catch (Platform::InvalidArgumentException^ e)
{
}
}
} }
/* Application lifecycle event handlers. */ /* Application lifecycle event handlers. */
void App::OnActivated(CoreApplicationView^ applicationView, IActivatedEventArgs^ args) 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<char*> argv;
std::vector<std::string> 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. */ /* Run() won't start until the CoreWindow is activated. */
CoreWindow::GetForCurrentThread()->Activate(); 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) void App::OnBackRequested(Platform::Object^ sender, Windows::UI::Core::BackRequestedEventArgs^ args)
{ {
/* Prevent the B controller button on Xbox One from quitting the app */ /* 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<char*> *argv, std::vector<std::string> *argvTmp)
{
argvTmp->clear();
argv->clear();
// If the app is activated using protocol, it is expected to be in this format:
// "retroarch:?cmd=<RetroArch CLI arguments>&launchOnExit=<app to launch on exit>"
// 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<Windows::ApplicationModel::Activation::ProtocolActivatedEventArgs^>(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 */ /* Implement UWP equivalents of various win32_* functions */
extern "C" { extern "C" {

View File

@ -37,6 +37,7 @@ namespace RetroArchUWP
void OnActivated(Windows::ApplicationModel::Core::CoreApplicationView^ applicationView, Windows::ApplicationModel::Activation::IActivatedEventArgs^ args); void OnActivated(Windows::ApplicationModel::Core::CoreApplicationView^ applicationView, Windows::ApplicationModel::Activation::IActivatedEventArgs^ args);
void OnSuspending(Platform::Object^ sender, Windows::ApplicationModel::SuspendingEventArgs^ args); void OnSuspending(Platform::Object^ sender, Windows::ApplicationModel::SuspendingEventArgs^ args);
void OnResuming(Platform::Object^ sender, Platform::Object^ 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); void OnBackRequested(Platform::Object^ sender, Windows::UI::Core::BackRequestedEventArgs^ args);
@ -70,6 +71,9 @@ namespace RetroArchUWP
bool m_windowVisible; bool m_windowVisible;
bool m_windowFocused; bool m_windowFocused;
bool m_windowResized; bool m_windowResized;
Platform::String^ m_launchOnExit;
bool m_launchOnExitShutdown;
void ParseProtocolArgs(Windows::ApplicationModel::Activation::IActivatedEventArgs^ args, int *argc, std::vector<char*> *argv, std::vector<std::string> *argvTmp);
static App^ m_instance; static App^ m_instance;
}; };
} }