mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-10-01 14:58:14 +00:00
Instead of the PSP's web browser, a dialog shows the URL the game wants and opens it in the host's browser on X, or backs out on O. Either way the game sees the browser closed normally. Platforms that can't open a URL (the new SYSPROP_CAN_LAUNCH_URL) only offer to back out. Only plain printable-ASCII http(s) addresses are handed over, and on Linux without a shell. What the firmware does (sceUtility_Driver, 6.61, plus utility/dialog/htmlviewer): the HtmlViewer has its own state apart from the other dialogs, so they don't block each other, and its calls return WRONG_TYPE until one has started. The request size picks the 2.00 to 3.00 layout, and InitStart allocates 3.5MB of user memory (4.5MB with options bit 0x400 from 2.70 on), failing with 800200d9 without it. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
725 lines
21 KiB
C++
725 lines
21 KiB
C++
#include "pch.h"
|
|
#include "PPSSPP_UWPMain.h"
|
|
|
|
#include <mutex>
|
|
#include <list>
|
|
#include <memory>
|
|
#include <thread>
|
|
|
|
#include "Common/File/FileUtil.h"
|
|
#include "Common/Net/HTTPClient.h"
|
|
#include "Common/Net/Resolve.h"
|
|
#include "Common/GPU/thin3d_create.h"
|
|
|
|
#include "Common/Common.h"
|
|
#include "Common/OSVersion.h"
|
|
#include "Common/Audio/AudioBackend.h"
|
|
#include "Common/Input/InputState.h"
|
|
#include "Common/File/VFS/VFS.h"
|
|
#include "Common/Thread/ThreadUtil.h"
|
|
#include "Common/Data/Encoding/Utf8.h"
|
|
#include "Common/DirectXHelper.h"
|
|
#include "Common/Log.h"
|
|
#include "Common/Log/LogManager.h"
|
|
#include "Common/TimeUtil.h"
|
|
#include "Common/StringUtils.h"
|
|
#include "Common/System/Display.h"
|
|
#include "Common/System/NativeApp.h"
|
|
#include "Common/System/Request.h"
|
|
|
|
#include "Core/System.h"
|
|
#include "Core/Loaders.h"
|
|
#include "Core/Config.h"
|
|
#include "Core/CmdLine.h"
|
|
|
|
#include "Windows/InputDevice.h"
|
|
#include "Windows/XinputDevice.h"
|
|
#include "NKCodeFromWindowsSystem.h"
|
|
#include "XAudioSoundStream.h"
|
|
#include "UWPUtil.h"
|
|
#include "App.h"
|
|
|
|
// UWP Helpers includes
|
|
#include "UWPHelpers/StorageManager.h"
|
|
#include "UWPHelpers/StorageAsync.h"
|
|
#include "UWPHelpers/LaunchItem.h"
|
|
#include "UWPHelpers/InputHelpers.h"
|
|
#include "Windows/InputDevice.h"
|
|
|
|
using namespace UWP;
|
|
using namespace winrt;
|
|
using namespace winrt::Windows::Foundation;
|
|
using namespace winrt::Windows::Storage;
|
|
using namespace winrt::Windows::Storage::Streams;
|
|
using namespace winrt::Windows::System::Threading;
|
|
using namespace winrt::Windows::ApplicationModel::DataTransfer;
|
|
using namespace winrt::Windows::Devices::Enumeration;
|
|
|
|
// 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
|
|
// TODO: Low latency input: https://github.com/Microsoft/Windows-universal-samples/tree/master/Samples/LowLatencyInput/cpp
|
|
|
|
// Loads and initializes application assets when the application is loaded.
|
|
PPSSPP_UWPMain::PPSSPP_UWPMain(App *app, const std::shared_ptr<DX::DeviceResources>& deviceResources) :
|
|
app_(app),
|
|
m_deviceResources(deviceResources)
|
|
{
|
|
TimeInit();
|
|
|
|
// Register to be notified if the Device is lost or recreated
|
|
m_deviceResources->RegisterDeviceNotify(this);
|
|
|
|
ctx_.reset(new UWPGraphicsContext(deviceResources));
|
|
|
|
#if _DEBUG
|
|
g_logManager.SetAllLogLevels(LogLevel::LDEBUG);
|
|
|
|
if (g_Config.bEnableLogging) {
|
|
g_logManager.SetFileLogPath(Path(GetLogFile()));
|
|
}
|
|
#endif
|
|
|
|
// At this point we have main requirements initialized (Log, Config, NativeInit, Device)
|
|
NativeInitGraphics(ctx_.get());
|
|
NativeResized();
|
|
|
|
int width = m_deviceResources->GetScreenViewport().Width;
|
|
int height = m_deviceResources->GetScreenViewport().Height;
|
|
|
|
ctx_->GetDrawContext()->HandleEvent(Draw::Event::GOT_BACKBUFFER, width, height, m_deviceResources->GetBackBufferRenderTargetView());
|
|
|
|
// add first XInput device to respond
|
|
g_InputManager.AddDevice(new XinputDevice());
|
|
g_InputManager.BeginPolling();
|
|
|
|
// Prepare input pane (for Xbox & touch devices)
|
|
PrepareInputPane();
|
|
}
|
|
|
|
PPSSPP_UWPMain::~PPSSPP_UWPMain() {
|
|
g_InputManager.StopPolling();
|
|
g_InputManager.Shutdown();
|
|
|
|
ctx_->GetDrawContext()->HandleEvent(Draw::Event::LOST_BACKBUFFER, 0, 0, nullptr);
|
|
NativeShutdownGraphics(ctx_.get());
|
|
NativeShutdown();
|
|
g_VFS.Clear();
|
|
|
|
// Deregister device notification
|
|
m_deviceResources->RegisterDeviceNotify(nullptr);
|
|
net::Shutdown();
|
|
}
|
|
|
|
// Updates application state when the window size changes (e.g. device orientation change)
|
|
void PPSSPP_UWPMain::CreateWindowSizeDependentResources() {
|
|
ctx_->GetDrawContext()->HandleEvent(Draw::Event::LOST_BACKBUFFER, 0, 0, nullptr);
|
|
|
|
NativeResized();
|
|
|
|
int width = m_deviceResources->GetScreenViewport().Width;
|
|
int height = m_deviceResources->GetScreenViewport().Height;
|
|
ctx_->GetDrawContext()->HandleEvent(Draw::Event::GOT_BACKBUFFER, width, height, m_deviceResources->GetBackBufferRenderTargetView());
|
|
}
|
|
|
|
void PPSSPP_UWPMain::UpdateScreenState() {
|
|
// This code was included into the render loop directly
|
|
// based on my test I don't understand why it should be called each loop
|
|
// is it better to call it on demand only, like when screen state changed?
|
|
auto context = m_deviceResources->GetD3DDeviceContext();
|
|
|
|
switch (m_deviceResources->ComputeDisplayRotation()) {
|
|
case DXGI_MODE_ROTATION_IDENTITY: g_display.rotation = DisplayRotation::ROTATE_0; break;
|
|
case DXGI_MODE_ROTATION_ROTATE90: g_display.rotation = DisplayRotation::ROTATE_90; break;
|
|
case DXGI_MODE_ROTATION_ROTATE180: g_display.rotation = DisplayRotation::ROTATE_180; break;
|
|
case DXGI_MODE_ROTATION_ROTATE270: g_display.rotation = DisplayRotation::ROTATE_270; break;
|
|
}
|
|
// Not super elegant but hey.
|
|
auto orientMatrix = m_deviceResources->GetOrientationTransform3D();
|
|
memcpy(&g_display.rot_matrix, &orientMatrix, sizeof(float) * 16);
|
|
|
|
// Reset the viewport to target the whole screen.
|
|
auto viewport = m_deviceResources->GetScreenViewport();
|
|
|
|
g_display.pixel_xres = viewport.Width;
|
|
g_display.pixel_yres = viewport.Height;
|
|
|
|
// TODO: The below stuff is probably completely redundant since the UWP app elsewhere calls Native_UpdateScreenScale.
|
|
|
|
float dpi = m_deviceResources->GetActualDpi();
|
|
|
|
g_display.dpi_scale_real_x = 96.0f / dpi;
|
|
g_display.dpi_scale_real_y = 96.0f / dpi;
|
|
|
|
g_display.dpi_scale_x = g_display.dpi_scale_real_x;
|
|
g_display.dpi_scale_y = g_display.dpi_scale_real_y;
|
|
g_display.pixel_in_dps_x = 1.0f / g_display.dpi_scale_x;
|
|
g_display.pixel_in_dps_y = 1.0f / g_display.dpi_scale_y;
|
|
|
|
g_display.dp_xres = g_display.pixel_xres * g_display.dpi_scale_x;
|
|
g_display.dp_yres = g_display.pixel_yres * g_display.dpi_scale_y;
|
|
|
|
context->RSSetViewports(1, &viewport);
|
|
}
|
|
|
|
// 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() {
|
|
static bool hasSetThreadName = false;
|
|
if (!hasSetThreadName) {
|
|
SetCurrentThreadName("EmuThread");
|
|
hasSetThreadName = true;
|
|
}
|
|
|
|
UpdateScreenState();
|
|
|
|
NativeFrame(ctx_.get());
|
|
return true;
|
|
}
|
|
|
|
// Notifies renderers that device resources need to be released.
|
|
void PPSSPP_UWPMain::OnDeviceLost() {
|
|
ctx_->GetDrawContext()->HandleEvent(Draw::Event::LOST_DEVICE, 0, 0, nullptr);
|
|
}
|
|
|
|
// Notifies renderers that device resources may now be recreated.
|
|
void PPSSPP_UWPMain::OnDeviceRestored() {
|
|
CreateWindowSizeDependentResources();
|
|
|
|
ctx_->GetDrawContext()->HandleEvent(Draw::Event::GOT_DEVICE, 0, 0, nullptr);
|
|
}
|
|
|
|
void PPSSPP_UWPMain::OnKeyDown(int scanCode, winrt::Windows::System::VirtualKey virtualKey, int repeatCount) {
|
|
// TODO: Look like (Ctrl, Alt, Shift) don't trigger this event
|
|
bool isDPad = (int)virtualKey >= 195 && (int)virtualKey <= 218; // DPad buttons range
|
|
DPadInputState(isDPad);
|
|
|
|
auto iter = virtualKeyCodeToNKCode.find(virtualKey);
|
|
if (iter != virtualKeyCodeToNKCode.end()) {
|
|
KeyInput key{};
|
|
key.deviceId = DEVICE_ID_KEYBOARD;
|
|
key.keyCode = iter->second;
|
|
key.flags = KeyInputFlags::DOWN;
|
|
if (repeatCount > 1)
|
|
key.flags |= KeyInputFlags::IS_REPEAT;
|
|
NativeKey(key);
|
|
}
|
|
}
|
|
|
|
void PPSSPP_UWPMain::OnKeyUp(int scanCode, winrt::Windows::System::VirtualKey virtualKey) {
|
|
auto iter = virtualKeyCodeToNKCode.find(virtualKey);
|
|
if (iter != virtualKeyCodeToNKCode.end()) {
|
|
KeyInput key{};
|
|
key.deviceId = DEVICE_ID_KEYBOARD;
|
|
key.keyCode = iter->second;
|
|
key.flags = KeyInputFlags::UP;
|
|
NativeKey(key);
|
|
}
|
|
}
|
|
|
|
void PPSSPP_UWPMain::OnCharacterReceived(int scanCode, unsigned int keyCode) {
|
|
// This event triggered only in chars case, (Arrows, Delete..etc don't call it)
|
|
// TODO: Add ` && !IsCtrlOnHold()` once it's ready and implemented
|
|
if (isTextEditActive()) {
|
|
KeyInput key{};
|
|
key.deviceId = DEVICE_ID_KEYBOARD;
|
|
key.keyCode = (InputKeyCode)keyCode;
|
|
// After many tests turns out for char just add `KeyInputFlags::CHAR` for the flags
|
|
// any other flag like `KeyInputFlags::DOWN` will cause conflict and trigger something else
|
|
key.flags = KeyInputFlags::CHAR;
|
|
NativeKey(key);
|
|
}
|
|
}
|
|
|
|
void PPSSPP_UWPMain::OnMouseWheel(float delta) {
|
|
InputKeyCode key = NKCODE_EXT_MOUSEWHEEL_UP;
|
|
if (delta < 0) {
|
|
key = NKCODE_EXT_MOUSEWHEEL_DOWN;
|
|
} else if (delta == 0) {
|
|
return;
|
|
}
|
|
|
|
KeyInput keyInput{};
|
|
keyInput.keyCode = key;
|
|
keyInput.deviceId = DEVICE_ID_MOUSE;
|
|
keyInput.flags = KeyInputFlags::DOWN;
|
|
NativeKey(keyInput);
|
|
|
|
// KeyInputFlags::UP is now sent automatically afterwards for mouse wheel events, see NativeKey.
|
|
}
|
|
|
|
bool PPSSPP_UWPMain::OnBackRequested() {
|
|
if (NativeIsAtTopLevel())
|
|
return false;
|
|
|
|
KeyInput key{};
|
|
key.deviceId = DEVICE_ID_DEFAULT;
|
|
key.keyCode = NKCODE_BACK;
|
|
key.flags = KeyInputFlags::DOWN;
|
|
NativeKey(key);
|
|
key.flags = KeyInputFlags::UP;
|
|
NativeKey(key);
|
|
return true;
|
|
}
|
|
|
|
void PPSSPP_UWPMain::OnTouchEvent(TouchInputFlags flags, int touchId, float x, float y, double timestamp) {
|
|
// We get the coordinate in Windows' device independent pixels already. So let's undo that,
|
|
// and then apply our own "dpi".
|
|
float dpiFactor_x = m_deviceResources->GetActualDpi() / 96.0f;
|
|
float dpiFactor_y = dpiFactor_x;
|
|
dpiFactor_x /= g_display.pixel_in_dps_x;
|
|
dpiFactor_y /= g_display.pixel_in_dps_y;
|
|
|
|
TouchInput input{};
|
|
input.id = touchId;
|
|
input.x = x * dpiFactor_x;
|
|
input.y = y * dpiFactor_y;
|
|
input.flags = flags;
|
|
input.timestamp = timestamp;
|
|
NativeTouch(input);
|
|
|
|
KeyInput key{};
|
|
key.deviceId = DEVICE_ID_MOUSE;
|
|
if (flags & TouchInputFlags::DOWN) {
|
|
key.keyCode = NKCODE_EXT_MOUSEBUTTON_1;
|
|
key.flags = KeyInputFlags::DOWN;
|
|
NativeKey(key);
|
|
}
|
|
if (flags & TouchInputFlags::UP) {
|
|
key.keyCode = NKCODE_EXT_MOUSEBUTTON_1;
|
|
key.flags = KeyInputFlags::UP;
|
|
NativeKey(key);
|
|
}
|
|
}
|
|
|
|
void PPSSPP_UWPMain::OnSuspend() {
|
|
// TODO
|
|
}
|
|
|
|
|
|
UWPGraphicsContext::UWPGraphicsContext(std::shared_ptr<DX::DeviceResources> resources) {
|
|
std::vector<std::string> adapterNames = resources->GetAdapters();
|
|
|
|
draw_ = Draw::T3DCreateD3D11Context(
|
|
resources->GetD3DDevice(), resources->GetD3DDeviceContext(), resources->GetD3DDevice(), resources->GetD3DDeviceContext(), resources->GetSwapChain(), resources->GetDeviceFeatureLevel(), 0, adapterNames, g_Config.iInflightFrames);
|
|
bool success = draw_->CreatePresets();
|
|
_assert_(success);
|
|
}
|
|
|
|
void UWPGraphicsContext::ShutdownAPI() {
|
|
delete draw_;
|
|
}
|
|
|
|
bool IsXBox() {
|
|
auto deviceInfo = winrt::Windows::System::Profile::AnalyticsInfo::VersionInfo();
|
|
return deviceInfo.DeviceFamily() == L"Windows.Xbox";
|
|
}
|
|
|
|
void GetVersionInfo(uint32_t& major, uint32_t& minor, uint32_t& build, uint32_t& revision) {
|
|
major = minor = build = revision = 0;
|
|
winrt::hstring deviceFamilyVersion = winrt::Windows::System::Profile::AnalyticsInfo::VersionInfo().DeviceFamilyVersion();
|
|
uint64_t version = 0;
|
|
try {
|
|
version = std::stoull(std::wstring(deviceFamilyVersion));
|
|
} catch (...) {
|
|
return;
|
|
}
|
|
|
|
major = static_cast<uint32_t>((version & 0xFFFF000000000000L) >> 48);
|
|
minor = static_cast<uint32_t>((version & 0x0000FFFF00000000L) >> 32);
|
|
build = static_cast<uint32_t>((version & 0x00000000FFFF0000L) >> 16);
|
|
revision = static_cast<uint32_t>(version & 0x000000000000FFFFL);
|
|
}
|
|
|
|
std::string GetSystemName() {
|
|
std::string osName = "Microsoft Windows 10";
|
|
|
|
if (IsXBox()) {
|
|
osName = "Xbox OS";
|
|
} else {
|
|
uint32_t major = 0, minor = 0, build = 0, revision = 0;
|
|
GetVersionInfo(major, minor, build, revision);
|
|
|
|
if (build >= 22000) {
|
|
osName = "Microsoft Windows 11";
|
|
}
|
|
}
|
|
return osName + " " + GetWindowsSystemArchitecture();
|
|
}
|
|
|
|
std::string GetWindowsBuild() {
|
|
uint32_t major = 0, minor = 0, build = 0, revision = 0;
|
|
GetVersionInfo(major, minor, build, revision);
|
|
|
|
char buffer[50];
|
|
sprintf_s(buffer, sizeof(buffer), "%u.%u.%u (rev. %u)", major, minor, build, revision);
|
|
return std::string(buffer);
|
|
}
|
|
std::string System_GetProperty(SystemProperty prop) {
|
|
static bool hasCheckedGPUDriverVersion = false;
|
|
switch (prop) {
|
|
case SYSPROP_NAME:
|
|
return GetSystemName();
|
|
case SYSPROP_SYSTEMBUILD:
|
|
return GetWindowsBuild();
|
|
case SYSPROP_LANGREGION:
|
|
return GetLangRegion();
|
|
case SYSPROP_CLIPBOARD_TEXT:
|
|
/* TODO: Need to either change this API or do this on a thread in an ugly fashion.
|
|
auto view = winrt::Windows::ApplicationModel::DataTransfer::Clipboard::GetContent();
|
|
if (view) {
|
|
winrt::hstring text = co_await view.GetTextAsync();
|
|
}
|
|
*/
|
|
return "";
|
|
case SYSPROP_GPUDRIVER_VERSION:
|
|
return "";
|
|
case SYSPROP_BUILD_VERSION:
|
|
return PPSSPP_GIT_VERSION;
|
|
default:
|
|
return "";
|
|
}
|
|
}
|
|
|
|
std::vector<std::string> System_GetPropertyStringVec(SystemProperty prop) {
|
|
std::vector<std::string> result;
|
|
switch (prop) {
|
|
case SYSPROP_TEMP_DIRS:
|
|
{
|
|
std::wstring tempPath(MAX_PATH, '\0');
|
|
size_t sz = GetTempPath((DWORD)tempPath.size(), &tempPath[0]);
|
|
if (sz >= tempPath.size()) {
|
|
tempPath.resize(sz);
|
|
sz = GetTempPath((DWORD)tempPath.size(), &tempPath[0]);
|
|
}
|
|
// Need to resize off the null terminator either way.
|
|
tempPath.resize(sz);
|
|
result.push_back(ConvertWStringToUTF8(tempPath));
|
|
return result;
|
|
}
|
|
|
|
default:
|
|
return result;
|
|
}
|
|
}
|
|
|
|
extern AudioBackend *g_audioBackend;
|
|
|
|
int64_t System_GetPropertyInt(SystemProperty prop) {
|
|
switch (prop) {
|
|
case SYSPROP_AUDIO_SAMPLE_RATE:
|
|
return g_audioBackend ? g_audioBackend->SampleRate() : -1;
|
|
|
|
case SYSPROP_DEVICE_TYPE:
|
|
{
|
|
if (IsXBox()) {
|
|
return DEVICE_TYPE_TV;
|
|
} else {
|
|
return DEVICE_TYPE_DESKTOP;
|
|
}
|
|
}
|
|
case SYSPROP_DISPLAY_XRES:
|
|
{
|
|
winrt::Windows::UI::Core::CoreWindow corewindow = winrt::Windows::UI::Core::CoreWindow::GetForCurrentThread();
|
|
if (corewindow) {
|
|
return (int)corewindow.Bounds().Width;
|
|
}
|
|
return -1;
|
|
}
|
|
case SYSPROP_DISPLAY_YRES:
|
|
{
|
|
winrt::Windows::UI::Core::CoreWindow corewindow = winrt::Windows::UI::Core::CoreWindow::GetForCurrentThread();
|
|
if (corewindow) {
|
|
return (int)corewindow.Bounds().Height;
|
|
}
|
|
return -1;
|
|
}
|
|
default:
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
float System_GetPropertyFloat(SystemProperty prop) {
|
|
switch (prop) {
|
|
case SYSPROP_DISPLAY_REFRESH_RATE:
|
|
return 60.f;
|
|
case SYSPROP_DISPLAY_SAFE_INSET_LEFT:
|
|
case SYSPROP_DISPLAY_SAFE_INSET_RIGHT:
|
|
case SYSPROP_DISPLAY_SAFE_INSET_TOP:
|
|
case SYSPROP_DISPLAY_SAFE_INSET_BOTTOM:
|
|
return 0.0f;
|
|
default:
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
void System_Toast(std::string_view str) {}
|
|
|
|
bool System_GetPropertyBool(SystemProperty prop) {
|
|
switch (prop) {
|
|
case SYSPROP_HAS_TEXT_CLIPBOARD:
|
|
case SYSPROP_HAS_OPEN_DIRECTORY:
|
|
{
|
|
return !IsXBox();
|
|
}
|
|
case SYSPROP_HAS_FILE_BROWSER:
|
|
return true;
|
|
case SYSPROP_CAN_LAUNCH_URL:
|
|
return true;
|
|
case SYSPROP_HAS_FOLDER_BROWSER:
|
|
return true;
|
|
case SYSPROP_HAS_IMAGE_BROWSER:
|
|
return true; // we just use the file browser
|
|
case SYSPROP_HAS_BACK_BUTTON:
|
|
return false;
|
|
case SYSPROP_HAS_ACCELEROMETER:
|
|
return false;
|
|
case SYSPROP_APP_GOLD:
|
|
#ifdef GOLD
|
|
return true;
|
|
#else
|
|
return false;
|
|
#endif
|
|
case SYSPROP_CAN_JIT:
|
|
return true;
|
|
case SYSPROP_HAS_KEYBOARD:
|
|
{
|
|
// Do actual check
|
|
// touch devices has input pane, we need to depend on it
|
|
// I don't know any possible way to display input dialog in non-xaml apps
|
|
return isKeyboardAvailable() || isTouchAvailable();
|
|
}
|
|
case SYSPROP_KEYBOARD_IS_SOFT:
|
|
{
|
|
return IsXBox();
|
|
}
|
|
case SYSPROP_DEBUGGER_PRESENT:
|
|
return IsDebuggerPresent();
|
|
case SYSPROP_OK_BUTTON_LEFT:
|
|
return true;
|
|
case SYSPROP_CAN_GET_FREE_SPACE_FAST:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void System_Notify(SystemNotification notification) {}
|
|
|
|
bool System_MakeRequest(SystemRequestType type, int requestId, const std::string ¶m1, const std::string ¶m2, int64_t param3, int64_t param4) {
|
|
switch (type) {
|
|
|
|
case SystemRequestType::EXIT_APP:
|
|
{
|
|
bool state = false;
|
|
ExecuteTask(state, winrt::Windows::UI::ViewManagement::ApplicationView::GetForCurrentView().TryConsolidateAsync());
|
|
if (!state) {
|
|
// Notify the user?
|
|
}
|
|
return true;
|
|
}
|
|
case SystemRequestType::RESTART_APP:
|
|
{
|
|
winrt::Windows::ApplicationModel::Core::AppRestartFailureReason error;
|
|
ExecuteTask(error, winrt::Windows::ApplicationModel::Core::CoreApplication::RequestRestartAsync(L""));
|
|
if (error != winrt::Windows::ApplicationModel::Core::AppRestartFailureReason::RestartPending) {
|
|
// Shutdown
|
|
System_MakeRequest(SystemRequestType::EXIT_APP, requestId, param1, param2, param3, param4);
|
|
}
|
|
return true;
|
|
}
|
|
case SystemRequestType::BROWSE_FOR_IMAGE:
|
|
{
|
|
std::vector<std::string> supportedExtensions = { ".jpg", ".png" };
|
|
|
|
//Call file picker
|
|
std::string filePath = ChooseFile(supportedExtensions);
|
|
if (filePath.size() > 1) {
|
|
g_requestManager.PostSystemSuccess(requestId, filePath.c_str());
|
|
}
|
|
else {
|
|
g_requestManager.PostSystemFailure(requestId);
|
|
}
|
|
return true;
|
|
}
|
|
case SystemRequestType::BROWSE_FOR_FILE:
|
|
{
|
|
std::vector<std::string> supportedExtensions = {};
|
|
switch ((BrowseFileType)param3) {
|
|
case BrowseFileType::BOOTABLE:
|
|
supportedExtensions = { ".cso", ".iso", ".chd", ".elf", ".pbp", ".zip", ".prx", ".bin" }; // should .bin even be here?
|
|
break;
|
|
case BrowseFileType::SAVE_STATE:
|
|
supportedExtensions = { ".ppst" };
|
|
break;
|
|
case BrowseFileType::INI:
|
|
supportedExtensions = { ".ini" };
|
|
break;
|
|
case BrowseFileType::ZIP:
|
|
supportedExtensions = { ".zip" };
|
|
break;
|
|
case BrowseFileType::SYMBOL_MAP:
|
|
supportedExtensions = { ".ppmap" };
|
|
break;
|
|
case BrowseFileType::SYMBOL_MAP_NOCASH:
|
|
supportedExtensions = { ".sym" };
|
|
break;
|
|
case BrowseFileType::DB:
|
|
supportedExtensions = { ".db" };
|
|
break;
|
|
case BrowseFileType::SOUND_EFFECT:
|
|
supportedExtensions = { ".wav", ".mp3" };
|
|
break;
|
|
case BrowseFileType::ATRAC3:
|
|
supportedExtensions = { ".at3" };
|
|
break;
|
|
case BrowseFileType::ANY:
|
|
// 'ChooseFile' will added '*' by default when there are no extensions assigned
|
|
break;
|
|
default:
|
|
ERROR_LOG(Log::FileSystem, "Unexpected BrowseFileType: %d", param3);
|
|
return false;
|
|
}
|
|
|
|
//Call file picker
|
|
std::string filePath = ChooseFile(supportedExtensions);
|
|
if (filePath.size() > 1) {
|
|
g_requestManager.PostSystemSuccess(requestId, filePath.c_str());
|
|
}
|
|
else {
|
|
g_requestManager.PostSystemFailure(requestId);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
case SystemRequestType::BROWSE_FOR_FOLDER:
|
|
{
|
|
std::string folderPath = ChooseFolder();
|
|
if (folderPath.size() > 1) {
|
|
g_requestManager.PostSystemSuccess(requestId, folderPath.c_str());
|
|
}
|
|
else {
|
|
g_requestManager.PostSystemFailure(requestId);
|
|
}
|
|
return true;
|
|
}
|
|
case SystemRequestType::NOTIFY_UI_EVENT:
|
|
{
|
|
switch ((UIEventNotification)param3) {
|
|
case UIEventNotification::MENU_RETURN:
|
|
CloseLaunchItem();
|
|
break;
|
|
case UIEventNotification::POPUP_CLOSED:
|
|
DeactivateTextEditInput();
|
|
break;
|
|
case UIEventNotification::TEXT_GOTFOCUS:
|
|
ActivateTextEditInput(true);
|
|
break;
|
|
case UIEventNotification::TEXT_LOSTFOCUS:
|
|
DeactivateTextEditInput(true);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return true;
|
|
}
|
|
case SystemRequestType::COPY_TO_CLIPBOARD:
|
|
{
|
|
winrt::Windows::ApplicationModel::DataTransfer::DataPackage dataPackage;
|
|
dataPackage.RequestedOperation(winrt::Windows::ApplicationModel::DataTransfer::DataPackageOperation::Copy);
|
|
dataPackage.SetText(ToHString(param1));
|
|
winrt::Windows::ApplicationModel::DataTransfer::Clipboard::SetContent(dataPackage);
|
|
return true;
|
|
}
|
|
case SystemRequestType::APPLY_FULLSCREEN_STATE:
|
|
{
|
|
auto view = winrt::Windows::UI::ViewManagement::ApplicationView::GetForCurrentView();
|
|
bool flag = !view.IsFullScreenMode();
|
|
if (param1 == "0") {
|
|
flag = false;
|
|
} else if (param1 == "1"){
|
|
flag = true;
|
|
}
|
|
if (flag && g_Config.bFullScreen) {
|
|
view.TryEnterFullScreenMode();
|
|
} else if (!g_Config.bFullScreen){
|
|
view.ExitFullScreenMode();
|
|
}
|
|
return true;
|
|
}
|
|
case SystemRequestType::SHOW_FILE_IN_FOLDER:
|
|
OpenFolder(param1);
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void System_LaunchUrl(LaunchUrlType urlType, std::string_view url) {
|
|
try {
|
|
auto uri = winrt::Windows::Foundation::Uri(ToHString(url));
|
|
winrt::Windows::System::Launcher::LaunchUriAsync(uri);
|
|
} catch (const winrt::hresult_error &e) {
|
|
ERROR_LOG(Log::System, "System_LaunchUrl: invalid URI: %s", winrt::to_string(e.message()).c_str());
|
|
}
|
|
}
|
|
|
|
// Stub
|
|
std::vector<std::string> System_GetCameraDeviceList() {
|
|
return std::vector<std::string>();
|
|
}
|
|
|
|
void System_Vibrate(int length_ms) {
|
|
}
|
|
|
|
void System_AskForPermission(SystemPermission permission) {
|
|
}
|
|
|
|
PermissionStatus System_GetPermissionStatus(SystemPermission permission) {
|
|
return PERMISSION_STATUS_GRANTED;
|
|
}
|
|
|
|
std::string GetCPUBrandString() {
|
|
winrt::hstring cpu_id;
|
|
winrt::hstring cpu_name;
|
|
|
|
// GUID_DEVICE_PROCESSOR: {97FADB10-4E33-40AE-359C-8BEF029DBDD0}
|
|
winrt::hstring if_filter = L"System.Devices.InterfaceClassGuid:=\"{97FADB10-4E33-40AE-359C-8BEF029DBDD0}\"";
|
|
|
|
// Enumerate all CPU DeviceInterfaces, and get DeviceInstanceID of the first one.
|
|
try {
|
|
auto collection = winrt::Windows::Devices::Enumeration::DeviceInformation::FindAllAsync(if_filter).get();
|
|
if (collection.Size() > 0) {
|
|
auto cpu = collection.GetAt(0);
|
|
auto id = cpu.Properties().Lookup(L"System.Devices.DeviceInstanceID");
|
|
cpu_id = winrt::unbox_value<winrt::hstring>(id);
|
|
}
|
|
}
|
|
catch (const winrt::hresult_error& e) {
|
|
INFO_LOG(Log::System, "%s", winrt::to_string(e.message()).c_str());
|
|
}
|
|
|
|
if (!cpu_id.empty()) {
|
|
// Get the Device with the same ID as the DeviceInterface
|
|
// Then get the name (description) of that Device
|
|
// We have to do this because the DeviceInterface we get doesn't have a proper description.
|
|
winrt::hstring dev_filter = L"System.Devices.DeviceInstanceID:=\"" + cpu_id + L"\"";
|
|
|
|
try {
|
|
auto collection = winrt::Windows::Devices::Enumeration::DeviceInformation::FindAllAsync(dev_filter, {},
|
|
winrt::Windows::Devices::Enumeration::DeviceInformationKind::Device).get();
|
|
if (collection.Size() > 0) {
|
|
cpu_name = collection.GetAt(0).Name();
|
|
}
|
|
}
|
|
catch (const winrt::hresult_error& e) {
|
|
INFO_LOG(Log::System, "%s", winrt::to_string(e.message()).c_str());
|
|
}
|
|
}
|
|
|
|
if (!cpu_name.empty()) {
|
|
return FromHString(cpu_name);
|
|
} else {
|
|
return "Unknown";
|
|
}
|
|
}
|