diff --git a/UWP/App.cpp b/UWP/App.cpp index 639650eae7..711230591e 100644 --- a/UWP/App.cpp +++ b/UWP/App.cpp @@ -216,7 +216,6 @@ void App::Uninitialize() { } // Application lifecycle event handlers. -extern LaunchItem launchItem; void App::OnActivated(CoreApplicationView^ applicationView, IActivatedEventArgs^ args) { // Run() won't start until the CoreWindow is activated. CoreWindow::GetForCurrentThread()->Activate(); @@ -227,15 +226,8 @@ void App::OnActivated(CoreApplicationView^ applicationView, IActivatedEventArgs^ if (g_Config.UseFullScreen()) Windows::UI::ViewManagement::ApplicationView::GetForCurrentView()->TryEnterFullScreenMode(); - if (args->Kind == ActivationKind::File) { - FileActivatedEventArgs^ protocolArgs = dynamic_cast(args); - launchItem = LaunchItem((StorageFile^)protocolArgs->Files->GetAt(0)); - } - if (args->Kind == ActivationKind::Protocol) - { - ProtocolActivatedEventArgs^ protocolArgs = dynamic_cast(args); - launchItem = LaunchItem(protocolArgs); - } + //Detect if app started or activated by launch item (file, uri) + DetectLaunchItem(args); } void App::OnSuspending(Platform::Object^ sender, SuspendingEventArgs^ args) { diff --git a/UWP/PPSSPP_UWPMain.cpp b/UWP/PPSSPP_UWPMain.cpp index 858b1edbf5..bf9d2236d7 100644 --- a/UWP/PPSSPP_UWPMain.cpp +++ b/UWP/PPSSPP_UWPMain.cpp @@ -57,9 +57,6 @@ PPSSPP_UWPMain *g_main; extern WindowsAudioBackend *winAudioBackend; std::string langRegion; std::list> g_input; - -// This item will be assigned by 'App.cpp' at 'App::OnActivated' -LaunchItem launchItem; // TODO: Use Microsoft::WRL::ComPtr<> for D3D11 objects? // TODO: See https://github.com/Microsoft/Windows-universal-samples/tree/master/Samples/WindowsAudioSession for WASAPI with UWP @@ -126,22 +123,29 @@ PPSSPP_UWPMain::PPSSPP_UWPMain(App ^app, const std::shared_ptrSetAllLogLevels(LogTypes::LDEBUG); } - const char *argv[2] = { "fake", nullptr }; - - std::string cacheFolder = ConvertWStringToUTF8(ApplicationData::Current->TemporaryFolder->Path->Data()); - - NativeInit(1, argv, "", "", cacheFolder.c_str()); - // Set log file location if (g_Config.bEnableLogging) { LogManager::GetInstance()->ChangeFileLog(GetLogFile().c_str()); } + const char *argv[2] = { "fake", nullptr }; + int argc = 1; + + std::string cacheFolder = ConvertWStringToUTF8(ApplicationData::Current->TemporaryFolder->Path->Data()); + + // 'PPSSPP_UWPMain' is getting called before 'OnActivated' + // this make detecting launch items on startup hard + // I think 'Init' process must be moved out and invoked from 'OnActivated' one time only + // currently launchItem will work fine but we cannot skip logo screen + // we should pass file path to 'argv' using 'GetLaunchItemPath(args)' + // instead of depending on 'boot_filename' (LaunchItem.cpp) + NativeInit(argc, argv, "", "", cacheFolder.c_str()); + NativeInitGraphics(ctx_.get()); NativeResized(); @@ -179,15 +183,6 @@ void PPSSPP_UWPMain::CreateWindowSizeDependentResources() { ctx_->GetDrawContext()->HandleEvent(Draw::Event::GOT_BACKBUFFER, width, height, m_deviceResources->GetBackBufferRenderTargetView()); } -void PPSSPP_UWPMain::BootToLaunchFile() { - if (launchItem.IsValid() && !launchItem.IsHandled()) { - launchItem.SetState(true); - std::string path = launchItem.GetFilePath(); - if (!path.empty()) { - System_PostUIMessage("boot", path.c_str()); - } - } -} // Renders the current frame according to the current application state. // Returns true if the frame was rendered and is ready to be displayed. bool PPSSPP_UWPMain::Render() { @@ -577,9 +572,7 @@ bool System_MakeRequest(SystemRequestType type, int requestId, const std::string case SystemRequestType::NOTIFY_UI_STATE: { if (!param1.empty() && !strcmp(param1.c_str(), "menu")) { - if (launchItem.IsValid() && launchItem.IsHandled()) { - launchItem.Close(); - } + CloseLaunchItem(); } return true; } diff --git a/UWP/PPSSPP_UWPMain.h b/UWP/PPSSPP_UWPMain.h index f144d0cb51..e2cc2edefc 100644 --- a/UWP/PPSSPP_UWPMain.h +++ b/UWP/PPSSPP_UWPMain.h @@ -35,7 +35,6 @@ public: PPSSPP_UWPMain(App ^app, const std::shared_ptr& deviceResources); ~PPSSPP_UWPMain(); void CreateWindowSizeDependentResources(); - void BootToLaunchFile(); bool Render(); // IDeviceNotify diff --git a/UWP/UWP.vcxproj b/UWP/UWP.vcxproj index 2edb5f9f5d..40ae2ad8cc 100644 --- a/UWP/UWP.vcxproj +++ b/UWP/UWP.vcxproj @@ -89,7 +89,7 @@ True False Never - C8DEB388B9BC89D1DC61324E4E9D9FE6A796B7AA + C8DEB388B9BC89D1DC61324E4E9D9FE6A796B7AA False 0 OnApplicationRun @@ -468,6 +468,7 @@ + @@ -479,11 +480,11 @@ - + Create @@ -980,4 +981,4 @@ - + \ No newline at end of file diff --git a/UWP/UWP.vcxproj.filters b/UWP/UWP.vcxproj.filters index de4e5d9ed0..b0703d24c6 100644 --- a/UWP/UWP.vcxproj.filters +++ b/UWP/UWP.vcxproj.filters @@ -120,7 +120,10 @@ Input - + + UWPHelpers + + UWPHelpers @@ -613,4 +616,4 @@ Content - + \ No newline at end of file diff --git a/UWP/UWPHelpers/LaunchItem.cpp b/UWP/UWPHelpers/LaunchItem.cpp new file mode 100644 index 0000000000..ffa3aba460 --- /dev/null +++ b/UWP/UWPHelpers/LaunchItem.cpp @@ -0,0 +1,185 @@ +// Copyright (c) 2023- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#pragma once + +#include "pch.h" +#include +#include +#include + +#include "LaunchItem.h" + +#include "Common/Log.h" +#include +#include +#include "UWPUtil.h" + + +extern void AddItemToFutureList(IStorageItem^ item); + +// Temporary to handle startup items +// See remarks at 'PPSSPP_UWPMain.cpp', above 'NativeInit(..)' +extern Path boot_filename; + +#pragma region LaunchItemClass +class LaunchItem { +public: + LaunchItem() { + } + + ~LaunchItem() { + delete storageFile; + } + + void Activate(IStorageFile^ file) { + storageFile = file; + AddItemToFutureList(storageFile); + launchPath = std::string(); + launchOnExit = std::string(); + } + + void Activate(ProtocolActivatedEventArgs^ args) { + try { + unsigned i; + Windows::Foundation::WwwFormUrlDecoder^ query = args->Uri->QueryParsed; + + for (i = 0; i < query->Size; i++) + { + IWwwFormUrlDecoderEntry^ arg = query->GetAt(i); + + if (arg->Name == "cmd") + { + auto command = FromPlatformString(arg->Value); + DEBUG_LOG(FILESYS, "Launch command %s", command.c_str()); + + std::regex rgx("\"(.+[^\\/]+)\""); + std::smatch match; + + if (std::regex_search(command, match, rgx)) { + try + { + launchPath = match[1]; + } + catch (...) { + launchPath = match[0]; + } + DEBUG_LOG(FILESYS, "Launch target %s", launchPath.c_str()); + } + } + else if (arg->Name == "launchOnExit") { + launchOnExit = FromPlatformString(arg->Value); + DEBUG_LOG(FILESYS, "On exit URI %s", launchOnExit.c_str()); + } + } + } + catch (...) { + + } + storageFile = nullptr; + } + + void Start() { + if (IsValid()) { + SetState(true); + std::string path = GetFilePath(); + System_PostUIMessage("boot", path.c_str()); + boot_filename = Path(path); // Temporary to handle startup items + } + } + + bool IsHandled() { + return handled; + } + void SetState(bool fileHandled) { + handled = fileHandled; + } + + bool IsValid() { + return storageFile != nullptr || !launchPath.empty(); + } + + std::string GetFilePath() { + std::string path = launchPath; + if (storageFile != nullptr) { + path = FromPlatformString(storageFile->Path); + } + return path; + } + + void Close(bool callLaunchOnExit) { + storageFile = nullptr; + launchPath = std::string(); + handled = false; + + if (!launchOnExit.empty()) { + if (callLaunchOnExit) { + DEBUG_LOG(FILESYS, "Calling back %s", launchOnExit.c_str()); + auto uri = ref new Windows::Foundation::Uri(ToPlatformString(launchOnExit)); + Windows::System::Launcher::LaunchUriAsync(uri); + } + else { + DEBUG_LOG(FILESYS, "Ignoring callback %s, due to callLaunchOnExit is false", launchOnExit.c_str()); + } + } + launchOnExit = std::string(); + boot_filename.clear(); // Temporary to handle startup items + } + +private: + IStorageFile^ storageFile; + std::string launchPath; + std::string launchOnExit; + bool handled = false; +}; +#pragma endregion + +LaunchItem launchItemHandler; +void DetectLaunchItem(IActivatedEventArgs^ activateArgs, bool onlyActivate) { + if (activateArgs != nullptr) { + if (!launchItemHandler.IsHandled()) { + if (activateArgs->Kind == ActivationKind::File) { + FileActivatedEventArgs^ fileArgs = dynamic_cast(activateArgs); + launchItemHandler.Activate((StorageFile^)fileArgs->Files->GetAt(0)); + } + else if (activateArgs->Kind == ActivationKind::Protocol) + { + ProtocolActivatedEventArgs^ protocolArgs = dynamic_cast(activateArgs); + launchItemHandler.Activate(protocolArgs); + } + if (!onlyActivate) { + launchItemHandler.Start(); + } + } + } +} + +std::string GetLaunchItemPath(IActivatedEventArgs^ activateArgs) { + DetectLaunchItem(activateArgs, true); // Just activate + if (launchItemHandler.IsValid()) { + // Expected that 'GetLaunchItemPath' called to handle startup item + // it should be marked as handled by default + launchItemHandler.SetState(true); + } + return launchItemHandler.GetFilePath(); +} + +void CloseLaunchItem(bool launchOnExit) { + if (launchItemHandler.IsValid() && launchItemHandler.IsHandled()) { + launchItemHandler.Close(launchOnExit); + } +} diff --git a/UWP/UWPHelpers/LaunchItem.h b/UWP/UWPHelpers/LaunchItem.h index 22084fdc41..36979728c2 100644 --- a/UWP/UWPHelpers/LaunchItem.h +++ b/UWP/UWPHelpers/LaunchItem.h @@ -15,115 +15,28 @@ // Official git repository and contact information can be found at // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. -#pragma once - -#include "pch.h" -#include -#include - -#include "Common/Log.h" -#include "UWPUtil.h" -#include - - using namespace Platform; using namespace Windows::Storage; using namespace Windows::Foundation; +using namespace Windows::UI::Core; +using namespace Windows::ApplicationModel; using namespace Windows::ApplicationModel::Activation; -extern void AddItemToFutureList(IStorageItem^ item); -class LaunchItem { -public: - LaunchItem() { - } +// LaunchItem can detect launch items in two cases +// 1- StorageFile +// 2- URI [ppsspp:?cmd="fullpath"&launchOnExit=customURI] - LaunchItem(IStorageFile^ file) { - storageFile = file; - AddItemToFutureList(storageFile); - launchPath = std::string(); - launchOnExit = std::string(); - } +// Detect if activate args has launch item +// it will auto start the item unless 'onlyActivate' set to 'true' +void DetectLaunchItem(IActivatedEventArgs^ activateArgs, bool onlyActivate = false); - LaunchItem(ProtocolActivatedEventArgs^ args) { - try { - unsigned i; - Windows::Foundation::WwwFormUrlDecoder^ query = args->Uri->QueryParsed; +// Get current launch item path (same as 'DetectLaunchItem' but it doesn't start) +// this function made to handle item on startup +// it will mark the item as 'Handled' by default +// consider to close it if you want to use it for other purposes +std::string GetLaunchItemPath(IActivatedEventArgs^ activateArgs); - for (i = 0; i < query->Size; i++) - { - IWwwFormUrlDecoderEntry^ arg = query->GetAt(i); - - if (arg->Name == "cmd") - { - auto command = FromPlatformString(arg->Value); - DEBUG_LOG(FILESYS, "Launch command %s", command.c_str()); - - std::regex rgx("\"(.+\[^\/]+)\""); - std::smatch match; - - if (std::regex_search(command, match, rgx)) { - try - { - launchPath = match[1]; - } - catch (...) { - launchPath = match[0]; - } - DEBUG_LOG(FILESYS, "Launch target %s", launchPath.c_str()); - } - } - else if (arg->Name == "launchOnExit") { - launchOnExit = FromPlatformString(arg->Value); - DEBUG_LOG(FILESYS, "On exit URI %s", launchOnExit.c_str()); - } - } - } - catch (...) { - - } - storageFile = nullptr; - } - - ~LaunchItem() { - delete storageFile; - } - - - bool IsHandled() { - return handled; - } - void SetState(bool fileHandled) { - handled = fileHandled; - } - - bool IsValid() { - return storageFile != nullptr || !launchPath.empty(); - } - - std::string GetFilePath() { - std::string path = launchPath; - if (storageFile != nullptr) { - path = FromPlatformString(storageFile->Path); - } - return path; - } - - void Close() { - storageFile = nullptr; - launchPath = std::string(); - handled = false; - - if (!launchOnExit.empty()) { - DEBUG_LOG(FILESYS, "Calling back %s", launchOnExit.c_str()); - auto uri = ref new Windows::Foundation::Uri(ToPlatformString(launchOnExit)); - Windows::System::Launcher::LaunchUriAsync(uri); - } - launchOnExit = std::string(); - } - -private: - IStorageFile^ storageFile; - std::string launchPath; - std::string launchOnExit; - bool handled = false; -}; +// Close current launch item +// it will launch back 'launchOnExit' if passed with URI 'cmd' +// if you want to ignore 'launchOnExit' call set it to 'false' +void CloseLaunchItem(bool launchOnExit = true);