mirror of
https://github.com/libretro/RetroArch
synced 2025-02-06 00:39:53 +00:00
(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:
parent
5bc2d42cdc
commit
2febc16e2e
@ -20,6 +20,13 @@
|
||||
</uap:DefaultTile>
|
||||
<uap:SplashScreen Image="Assets\SplashScreen.png" BackgroundColor="#000000" />
|
||||
</uap:VisualElements>
|
||||
<Extensions>
|
||||
<uap:Extension Category="windows.protocol">
|
||||
<uap:Protocol Name="retroarch">
|
||||
<uap:DisplayName>retroarch</uap:DisplayName>
|
||||
</uap:Protocol>
|
||||
</uap:Extension>
|
||||
</Extensions>
|
||||
</Application>
|
||||
</Applications>
|
||||
<Capabilities>
|
||||
|
@ -20,6 +20,13 @@
|
||||
</uap:DefaultTile>
|
||||
<uap:SplashScreen Image="Assets\SplashScreen.png" BackgroundColor="#000000"/>
|
||||
</uap:VisualElements>
|
||||
<Extensions>
|
||||
<uap:Extension Category="windows.protocol">
|
||||
<uap:Protocol Name="retroarch">
|
||||
<uap:DisplayName>retroarch</uap:DisplayName>
|
||||
</uap:Protocol>
|
||||
</uap:Extension>
|
||||
</Extensions>
|
||||
</Application>
|
||||
</Applications>
|
||||
<Capabilities>
|
||||
|
188
uwp/uwp_main.cpp
188
uwp/uwp_main.cpp
@ -22,6 +22,8 @@
|
||||
#include <lists/string_list.h>
|
||||
#include <queues/task_queue.h>
|
||||
#include <retro_timers.h>
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
|
||||
#include "configuration.h"
|
||||
#include "paths.h"
|
||||
@ -274,6 +276,9 @@ void App::Initialize(CoreApplicationView^ applicationView)
|
||||
|
||||
CoreApplication::Resuming +=
|
||||
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). */
|
||||
@ -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<bool>([&]() {
|
||||
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<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. */
|
||||
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<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 */
|
||||
extern "C" {
|
||||
|
||||
|
@ -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<char*> *argv, std::vector<std::string> *argvTmp);
|
||||
static App^ m_instance;
|
||||
};
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user