diff --git a/Common/GPU/ShaderTranslation.cpp b/Common/GPU/ShaderTranslation.cpp index dc9492b31b..760d8acc4c 100644 --- a/Common/GPU/ShaderTranslation.cpp +++ b/Common/GPU/ShaderTranslation.cpp @@ -64,14 +64,10 @@ static EShLanguage GetShLanguageFromStage(const ShaderStage stage) { void ShaderTranslationInit() { // TODO: We have TLS issues on UWP -#if !PPSSPP_PLATFORM(UWP) glslang::InitializeProcess(); -#endif } void ShaderTranslationShutdown() { -#if !PPSSPP_PLATFORM(UWP) glslang::FinalizeProcess(); -#endif } struct Builtin { @@ -229,11 +225,6 @@ bool TranslateShader(std::string *dest, ShaderLanguage destLang, const ShaderLan return result; } -#if PPSSPP_PLATFORM(UWP) - *errorMessage = "No shader translation available (UWP)"; - return false; -#endif - errorMessage->clear(); glslang::TProgram program; diff --git a/UI/NativeApp.cpp b/UI/NativeApp.cpp index 9d5dfb802c..f316c3aea3 100644 --- a/UI/NativeApp.cpp +++ b/UI/NativeApp.cpp @@ -305,7 +305,7 @@ void PostLoadConfig() { else g_i18nrepo.LoadIni(g_Config.sLanguageIni, langOverridePath); -#if !PPSSPP_PLATFORM(WINDOWS) && !PPSSPP_PLATFORM(UWP) +#if !PPSSPP_PLATFORM(WINDOWS) || PPSSPP_PLATFORM(UWP) CreateSysDirectories(); #endif } diff --git a/UWP/App.cpp b/UWP/App.cpp index 711230591e..084a538c66 100644 --- a/UWP/App.cpp +++ b/UWP/App.cpp @@ -3,17 +3,20 @@ #include "pch.h" #include "App.h" +#include #include +#include "Common/Net/HTTPClient.h" +#include "Common/Net/Resolve.h" + +#include "Common/Data/Encoding/Utf8.h" #include "Common/Input/InputState.h" #include "Common/System/NativeApp.h" #include "Common/System/System.h" +#include "Common/LogManager.h" #include "Core/System.h" #include "Core/Config.h" #include "Core/Core.h" - -#include - #include "UWPHelpers/LaunchItem.h" using namespace UWP; @@ -27,7 +30,6 @@ using namespace Windows::UI::Input; using namespace Windows::System; using namespace Windows::Foundation; using namespace Windows::Graphics::Display; -using namespace Windows::Storage; // The main function is only used to initialize our IFrameworkView class. [Platform::MTAThread] @@ -47,6 +49,50 @@ App::App() : { } +void App::InitialPPSSPP() { + // Initial net + net::Init(); + + //Prepare for initialization + std::wstring internalDataFolderW = ApplicationData::Current->LocalFolder->Path->Data(); + g_Config.internalDataDirectory = Path(internalDataFolderW); + g_Config.memStickDirectory = g_Config.internalDataDirectory; + + // On Win32 it makes more sense to initialize the system directories here + // because the next place it was called was in the EmuThread, and it's too late by then. + CreateSysDirectories(); + + LogManager::Init(&g_Config.bEnableLogging); + + // Set the config path to local state by default + // it will be overrided by `NativeInit` if there is custom memStick + g_Config.SetSearchPath(GetSysDirectory(DIRECTORY_SYSTEM)); + g_Config.Load(); + + if (g_Config.bFirstRun) { + // Clear `memStickDirectory` to show memory stick screen on first start + g_Config.memStickDirectory.clear(); + } + + // Since we don't have any async operation in `NativeInit` + // it's better to call it here + const char* argv[2] = { "fake", nullptr }; + std::string cacheFolder = ConvertWStringToUTF8(ApplicationData::Current->TemporaryFolder->Path->Data()); + // Ee will not be able to use `argv` + // since launch parameters usually handled by `OnActivated` + // and `OnActivated` will be invoked later, even after `PPSSPP_UWPMain(..)` + // so we are handling launch cases using `LaunchItem` + NativeInit(1, argv, "", "", cacheFolder.c_str()); + + // Override backend, `DIRECT3D11` is the only way for UWP apps + g_Config.iGPUBackend = (int)GPUBackend::DIRECT3D11; + + // Calling `NativeInit` before will help us to deal with custom configs + // such as custom adapter, so it's better to initial render device here + m_deviceResources = std::make_shared(); + m_deviceResources->CreateWindowSizeDependentResources(); +} + // The first method called when the IFrameworkView is being created. void App::Initialize(CoreApplicationView^ applicationView) { // Register event handlers for app lifecycle. This example includes Activated, so that we @@ -59,10 +105,6 @@ void App::Initialize(CoreApplicationView^ applicationView) { CoreApplication::Resuming += ref new EventHandler(this, &App::OnResuming); - - // At this point we have access to the device. - // We can create the device-dependent resources. - m_deviceResources = std::make_shared(); } // Called when the CoreWindow object is created (or re-created). @@ -89,6 +131,7 @@ void App::SetWindow(CoreWindow^ window) { window->KeyDown += ref new TypedEventHandler(this, &App::OnKeyDown); window->KeyUp += ref new TypedEventHandler(this, &App::OnKeyUp); + window->CharacterReceived += ref new TypedEventHandler(this, &App::OnCharacterReceived); window->PointerMoved += ref new TypedEventHandler(this, &App::OnPointerMoved); window->PointerEntered += ref new TypedEventHandler(this, &App::OnPointerEntered); @@ -111,7 +154,7 @@ void App::SetWindow(CoreWindow^ window) { Windows::UI::Core::BackRequestedEventArgs^>( this, &App::App_BackRequested); - m_deviceResources->SetWindow(window); + InitialPPSSPP(); } bool App::HasBackButton() { @@ -137,6 +180,10 @@ void App::OnKeyUp(Windows::UI::Core::CoreWindow^ sender, Windows::UI::Core::KeyE m_main->OnKeyUp(args->KeyStatus.ScanCode, args->VirtualKey); } +void App::OnCharacterReceived(Windows::UI::Core::CoreWindow^ sender, Windows::UI::Core::CharacterReceivedEventArgs^ args) { + m_main->OnCharacterReceived(args->KeyStatus.ScanCode, args->KeyCode); +} + void App::OnPointerMoved(Windows::UI::Core::CoreWindow^ sender, Windows::UI::Core::PointerEventArgs^ args) { int pointerId = touchMap_.TouchId(args->CurrentPoint->PointerId); if (pointerId < 0) diff --git a/UWP/App.h b/UWP/App.h index bd1fd0b331..595b618fc4 100644 --- a/UWP/App.h +++ b/UWP/App.h @@ -84,6 +84,7 @@ namespace UWP { // Input void OnKeyDown(Windows::UI::Core::CoreWindow^ sender, Windows::UI::Core::KeyEventArgs^ args); void OnKeyUp(Windows::UI::Core::CoreWindow^ sender, Windows::UI::Core::KeyEventArgs^ args); + void OnCharacterReceived(Windows::UI::Core::CoreWindow^ sender, Windows::UI::Core::CharacterReceivedEventArgs^ args); void OnPointerMoved(Windows::UI::Core::CoreWindow^ sender, Windows::UI::Core::PointerEventArgs^ args); void OnPointerEntered(Windows::UI::Core::CoreWindow^ sender, Windows::UI::Core::PointerEventArgs^ args); @@ -94,6 +95,7 @@ namespace UWP { void OnPointerWheelChanged(Windows::UI::Core::CoreWindow^ sender, Windows::UI::Core::PointerEventArgs^ args); void App_BackRequested(Platform::Object^ sender, Windows::UI::Core::BackRequestedEventArgs^ e); + void InitialPPSSPP(); private: std::shared_ptr m_deviceResources; diff --git a/UWP/Common/DeviceResources.cpp b/UWP/Common/DeviceResources.cpp index ab1bbcbf40..2f527c7096 100644 --- a/UWP/Common/DeviceResources.cpp +++ b/UWP/Common/DeviceResources.cpp @@ -3,6 +3,10 @@ #include "DeviceResources.h" #include "DirectXHelper.h" +#include "Core/Config.h" +#include "Common/StringUtils.h" +#include "Common/Data/Encoding/Utf8.h" + using namespace D2D1; using namespace DirectX; using namespace Microsoft::WRL; @@ -125,7 +129,7 @@ void DX::DeviceResources::CreateDeviceIndependentResources() } // Configures the Direct3D device, and stores handles to it and the device context. -void DX::DeviceResources::CreateDeviceResources() +void DX::DeviceResources::CreateDeviceResources(IDXGIAdapter* vAdapter) { // This flag adds support for surfaces with a different color channel ordering // than the API default. It is required for compatibility with Direct2D. @@ -159,10 +163,10 @@ void DX::DeviceResources::CreateDeviceResources() // Create the Direct3D 11 API device object and a corresponding context. ComPtr device; ComPtr context; - + auto hardwareType = (vAdapter != nullptr ? D3D_DRIVER_TYPE_UNKNOWN : D3D_DRIVER_TYPE_HARDWARE); HRESULT hr = D3D11CreateDevice( - nullptr, // Specify nullptr to use the default adapter. - D3D_DRIVER_TYPE_HARDWARE, // Create a device using the hardware graphics driver. + vAdapter, // Specify nullptr to use the default adapter. + hardwareType, // Create a device using the hardware graphics driver. 0, // Should be 0 unless the driver is D3D_DRIVER_TYPE_SOFTWARE. creationFlags, // Set debug and Direct2D compatibility flags. featureLevels, // List of feature levels this app can support. @@ -194,6 +198,14 @@ void DX::DeviceResources::CreateDeviceResources() ); } + if (!vAdapter && CreateAdaptersList(device)) { + // While fetching the adapters list + // we will check if the configs has custom adapter + // then will recall this function to create device with custom adapter + // so we have to stop here, if the `CreateAdaptersList` return true + return; + } + // Store pointers to the Direct3D 11.3 API device and immediate context. DX::ThrowIfFailed( device.As(&m_d3dDevice) @@ -203,14 +215,25 @@ void DX::DeviceResources::CreateDeviceResources() context.As(&m_d3dContext) ); - // Create the Direct2D device object and a corresponding context. - ComPtr dxgiDevice; DX::ThrowIfFailed( - m_d3dDevice.As(&dxgiDevice) + m_d3dDevice.As(&m_dxgiDevice) + ); + + DX::ThrowIfFailed( + m_dxgiDevice->GetAdapter(&m_dxgiAdapter) + ); + + DX::ThrowIfFailed( + m_dxgiAdapter->GetParent(IID_PPV_ARGS(&m_dxgiFactory)) + ); + + // Create the Direct2D device object and a corresponding context. + DX::ThrowIfFailed( + m_d3dDevice.As(&m_dxgiDevice) ); DX::ThrowIfFailed( - m_d2dFactory->CreateDevice(dxgiDevice.Get(), &m_d2dDevice) + m_d2dFactory->CreateDevice(m_dxgiDevice.Get(), &m_d2dDevice) ); DX::ThrowIfFailed( @@ -221,9 +244,53 @@ void DX::DeviceResources::CreateDeviceResources() ); } +bool DX::DeviceResources::CreateAdaptersList(ComPtr device) { + ComPtr dxgi_device; + DX::ThrowIfFailed( + device.As(&dxgi_device) + ); + + Microsoft::WRL::ComPtr deviceAdapter; + dxgi_device->GetAdapter(&deviceAdapter); + + Microsoft::WRL::ComPtr deviceFactory; + deviceAdapter->GetParent(IID_PPV_ARGS(&deviceFactory)); + + UINT i = 0; + IDXGIAdapter* pAdapter; + IDXGIAdapter* customAdapter = nullptr; + while (deviceFactory->EnumAdapters(i, &pAdapter) != DXGI_ERROR_NOT_FOUND) + { + DXGI_ADAPTER_DESC vAdapterDesc; + pAdapter->GetDesc(&vAdapterDesc); + auto adapterDescription = ConvertWStringToUTF8(vAdapterDesc.Description); + m_vAdapters.push_back(adapterDescription); + if (g_Config.sD3D11Device == adapterDescription) { + customAdapter = pAdapter; + } + ++i; + } + deviceFactory->Release(); + + bool reCreateDevice = false; + if (customAdapter) { + reCreateDevice = true; + // Recreate device with custom adapter + CreateDeviceResources(customAdapter); + } + + return reCreateDevice; +} + // These resources need to be recreated every time the window size is changed. void DX::DeviceResources::CreateWindowSizeDependentResources() { + // Window pointer was previously submited by `SetWindow` called from (App.cpp) + // we don't have to do that since we can easily get the current window + auto coreWindow = CoreWindow::GetForCurrentThread(); + + SetWindow(coreWindow); + // Clear the previous window size specific context. ID3D11RenderTargetView* nullViews[] = {nullptr}; m_d3dContext->OMSetRenderTargets(ARRAYSIZE(nullViews), nullViews, nullptr); @@ -287,27 +354,11 @@ void DX::DeviceResources::CreateWindowSizeDependentResources() swapChainDesc.Scaling = scaling; swapChainDesc.AlphaMode = DXGI_ALPHA_MODE_IGNORE; - // This sequence obtains the DXGI factory that was used to create the Direct3D device above. - ComPtr dxgiDevice; - DX::ThrowIfFailed( - m_d3dDevice.As(&dxgiDevice) - ); - - ComPtr dxgiAdapter; - DX::ThrowIfFailed( - dxgiDevice->GetAdapter(&dxgiAdapter) - ); - - ComPtr dxgiFactory; - DX::ThrowIfFailed( - dxgiAdapter->GetParent(IID_PPV_ARGS(&dxgiFactory)) - ); - ComPtr swapChain; DX::ThrowIfFailed( - dxgiFactory->CreateSwapChainForCoreWindow( + m_dxgiFactory->CreateSwapChainForCoreWindow( m_d3dDevice.Get(), - reinterpret_cast(m_window.Get()), + reinterpret_cast(coreWindow), &swapChainDesc, nullptr, &swapChain @@ -320,7 +371,7 @@ void DX::DeviceResources::CreateWindowSizeDependentResources() // Ensure that DXGI does not queue more than one frame at a time. This both reduces latency and // ensures that the application will only render after each VSync, minimizing power consumption. DX::ThrowIfFailed( - dxgiDevice->SetMaximumFrameLatency(1) + m_dxgiDevice->SetMaximumFrameLatency(1) ); } @@ -461,7 +512,6 @@ void DX::DeviceResources::SetWindow(CoreWindow^ window) { DisplayInformation^ currentDisplayInformation = DisplayInformation::GetForCurrentView(); - m_window = window; if (Windows::System::Profile::AnalyticsInfo::VersionInfo->DeviceFamily == L"Windows.Xbox") { const auto hdi = Windows::Graphics::Display::Core::HdmiDisplayInformation::GetForCurrentView(); @@ -494,8 +544,6 @@ void DX::DeviceResources::SetWindow(CoreWindow^ window) m_currentOrientation = currentDisplayInformation->CurrentOrientation; m_d2dContext->SetDpi(m_dpi, m_dpi); - - CreateWindowSizeDependentResources(); } // This method is called in the event handler for the SizeChanged event. @@ -515,10 +563,8 @@ void DX::DeviceResources::SetDpi(float dpi) { m_dpi = dpi; - // When the display DPI changes, the logical size of the window (measured in Dips) also changes and needs to be updated. - m_logicalSize = Windows::Foundation::Size(m_window->Bounds.Width, m_window->Bounds.Height); - - m_d2dContext->SetDpi(m_dpi, m_dpi); + // When the display DPI changes, the logical size of the window (measured in Dips) + // also changes and needs to be updated. CreateWindowSizeDependentResources(); } } @@ -540,18 +586,8 @@ void DX::DeviceResources::ValidateDevice() // was created or if the device has been removed. // First, get the information for the default adapter from when the device was created. - - ComPtr dxgiDevice; - DX::ThrowIfFailed(m_d3dDevice.As(&dxgiDevice)); - - ComPtr deviceAdapter; - DX::ThrowIfFailed(dxgiDevice->GetAdapter(&deviceAdapter)); - - ComPtr deviceFactory; - DX::ThrowIfFailed(deviceAdapter->GetParent(IID_PPV_ARGS(&deviceFactory))); - ComPtr previousDefaultAdapter; - DX::ThrowIfFailed(deviceFactory->EnumAdapters1(0, &previousDefaultAdapter)); + DX::ThrowIfFailed(m_dxgiFactory->EnumAdapters1(0, &previousDefaultAdapter)); DXGI_ADAPTER_DESC1 previousDesc; DX::ThrowIfFailed(previousDefaultAdapter->GetDesc1(&previousDesc)); @@ -575,9 +611,6 @@ void DX::DeviceResources::ValidateDevice() FAILED(m_d3dDevice->GetDeviceRemovedReason())) { // Release references to resources related to the old device. - dxgiDevice = nullptr; - deviceAdapter = nullptr; - deviceFactory = nullptr; previousDefaultAdapter = nullptr; // Create a new device and swap chain. diff --git a/UWP/Common/DeviceResources.h b/UWP/Common/DeviceResources.h index 8942c49014..8d2723cf10 100644 --- a/UWP/Common/DeviceResources.h +++ b/UWP/Common/DeviceResources.h @@ -1,4 +1,7 @@ -#pragma once +#pragma once + +#include +#include namespace DX { @@ -14,6 +17,7 @@ namespace DX { public: DeviceResources(); + void CreateWindowSizeDependentResources(); void SetWindow(Windows::UI::Core::CoreWindow^ window); void SetLogicalSize(Windows::Foundation::Size logicalSize); void SetCurrentOrientation(Windows::Graphics::Display::DisplayOrientations currentOrientation); @@ -52,16 +56,24 @@ namespace DX DXGI_MODE_ROTATION ComputeDisplayRotation(); + std::vector GetAdapters() const { return m_vAdapters; }; + private: void CreateDeviceIndependentResources(); - void CreateDeviceResources(); - void CreateWindowSizeDependentResources(); + void CreateDeviceResources(IDXGIAdapter* vAdapter = nullptr); void UpdateRenderTargetSize(); + bool CreateAdaptersList(Microsoft::WRL::ComPtr device); // Direct3D objects. Microsoft::WRL::ComPtr m_d3dDevice; Microsoft::WRL::ComPtr m_d3dContext; Microsoft::WRL::ComPtr m_swapChain; + Microsoft::WRL::ComPtr m_dxgiDevice; + Microsoft::WRL::ComPtr m_dxgiFactory; + Microsoft::WRL::ComPtr m_dxgiAdapter; + + // Direct3D adapters + std::vector m_vAdapters; // Direct3D rendering objects. Required for 3D. Microsoft::WRL::ComPtr m_d3dRenderTargetView; @@ -77,9 +89,6 @@ namespace DX Microsoft::WRL::ComPtr m_dwriteFactory; Microsoft::WRL::ComPtr m_wicFactory; - // Cached reference to the Window. - Platform::Agile m_window; - // Cached device properties. D3D_FEATURE_LEVEL m_d3dFeatureLevel; Windows::Foundation::Size m_d3dRenderTargetSize; @@ -99,4 +108,4 @@ namespace DX // The IDeviceNotify can be held directly as it owns the DeviceResources. IDeviceNotify* m_deviceNotify; }; -} \ No newline at end of file +} diff --git a/UWP/NKCodeFromWindowsSystem.cpp b/UWP/NKCodeFromWindowsSystem.cpp index c7d6c5d769..fe109d62d1 100644 --- a/UWP/NKCodeFromWindowsSystem.cpp +++ b/UWP/NKCodeFromWindowsSystem.cpp @@ -73,6 +73,7 @@ std::map virtualKeyCodeToNKCode{ { VirtualKey::PageUp, NKCODE_PAGE_UP }, { VirtualKey::PageDown, NKCODE_PAGE_DOWN }, { VirtualKey::Delete, NKCODE_FORWARD_DEL }, + { VirtualKey::Back, NKCODE_DEL }, { VirtualKey::End, NKCODE_MOVE_END }, { VirtualKey::Tab, NKCODE_TAB }, { VirtualKey::Down, NKCODE_DPAD_DOWN }, diff --git a/UWP/PPSSPP_UWPMain.cpp b/UWP/PPSSPP_UWPMain.cpp index 499bdc81a2..e840879a34 100644 --- a/UWP/PPSSPP_UWPMain.cpp +++ b/UWP/PPSSPP_UWPMain.cpp @@ -25,7 +25,7 @@ #include "Common/System/Display.h" #include "Common/System/NativeApp.h" #include "Common/System/Request.h" -#include +#include "Common/OSVersion.h" #include "Core/System.h" #include "Core/Loaders.h" @@ -38,10 +38,11 @@ #include "UWPUtil.h" #include "App.h" -// UWP Storage helper includes +// UWP Helpers includes #include "UWPHelpers/StorageManager.h" #include "UWPHelpers/StorageAsync.h" #include "UWPHelpers/LaunchItem.h" +#include using namespace UWP; using namespace Windows::Foundation; @@ -53,9 +54,7 @@ using namespace Windows::Devices::Enumeration; using namespace Concurrency; // UGLY! -PPSSPP_UWPMain *g_main; extern WindowsAudioBackend *winAudioBackend; -std::string langRegion; std::list> g_input; // TODO: Use Microsoft::WRL::ComPtr<> for D3D11 objects? @@ -67,87 +66,29 @@ PPSSPP_UWPMain::PPSSPP_UWPMain(App ^app, const std::shared_ptrRegisterDeviceNotify(this); - // create_task(KnownFolders::GetFolderForUserAsync(nullptr, KnownFolderId::RemovableDevices)).then([this](StorageFolder ^)); - - // TODO: Change the timer settings if you want something other than the default variable timestep mode. - // e.g. for 60 FPS fixed timestep update logic, call: - /* - m_timer.SetFixedTimeStep(true); - m_timer.SetTargetElapsedSeconds(1.0 / 60); - */ - ctx_.reset(new UWPGraphicsContext(deviceResources)); - const Path &exePath = File::GetExeDirectory(); + // Get install location + auto packageDirectory = Package::Current->InstalledPath; + const Path &exePath = Path(FromPlatformString(packageDirectory)); g_VFS.Register("", new DirectoryReader(exePath / "Content")); g_VFS.Register("", new DirectoryReader(exePath)); - wchar_t lcCountry[256]; - - if (0 != GetLocaleInfoEx(LOCALE_NAME_USER_DEFAULT, LOCALE_SNAME, lcCountry, 256)) { - langRegion = ConvertWStringToUTF8(lcCountry); - for (size_t i = 0; i < langRegion.size(); i++) { - if (langRegion[i] == '-') - langRegion[i] = '_'; - } - } else { - langRegion = "en_US"; - } - - std::wstring internalDataFolderW = ApplicationData::Current->LocalFolder->Path->Data(); - g_Config.internalDataDirectory = Path(internalDataFolderW); - g_Config.memStickDirectory = g_Config.internalDataDirectory; // Mount a filesystem g_Config.flash0Directory = exePath / "assets/flash0"; - // On Win32 it makes more sense to initialize the system directories here - // because the next place it was called was in the EmuThread, and it's too late by then. - CreateSysDirectories(); - - LogManager::Init(&g_Config.bEnableLogging); - - // Load config up here, because those changes below would be overwritten - // if it's not loaded here first. - g_Config.SetSearchPath(GetSysDirectory(DIRECTORY_SYSTEM)); - g_Config.Load(); - - if (g_Config.bFirstRun) { - g_Config.memStickDirectory.clear(); - } - - bool debugLogLevel = false; - - g_Config.iGPUBackend = (int)GPUBackend::DIRECT3D11; - - if (debugLogLevel) { +#if _DEBUG LogManager::GetInstance()->SetAllLogLevels(LogTypes::LDEBUG); - } - // 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()); + if (g_Config.bEnableLogging) { + LogManager::GetInstance()->ChangeFileLog(GetLogFile().c_str()); + } +#endif + // At this point we have main requirements initialized (Log, Config, NativeInit, Device) NativeInitGraphics(ctx_.get()); NativeResized(); @@ -185,15 +126,10 @@ void PPSSPP_UWPMain::CreateWindowSizeDependentResources() { ctx_->GetDrawContext()->HandleEvent(Draw::Event::GOT_BACKBUFFER, width, height, m_deviceResources->GetBackBufferRenderTargetView()); } -// 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("UWPRenderThread"); - hasSetThreadName = true; - } - +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()) { @@ -232,6 +168,18 @@ bool PPSSPP_UWPMain::Render() { 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("UWPRenderThread"); + hasSetThreadName = true; + } + + UpdateScreenState(); NativeFrame(ctx_.get()); return true; @@ -271,6 +219,17 @@ void PPSSPP_UWPMain::OnKeyUp(int scanCode, Windows::System::VirtualKey virtualKe } } +void PPSSPP_UWPMain::OnCharacterReceived(int scanCode, unsigned int keyCode) { + // TODO: Once on-screen keyboard show/hide solved, add `InputPaneVisible()` as extra condition + if (!PSP_IsInited() && !IsCtrlOnHold()) { + KeyInput key{}; + key.deviceId = DEVICE_ID_KEYBOARD; + key.keyCode = (InputKeyCode)keyCode; + key.flags = KEY_DOWN | KEY_CHAR; + NativeKey(key); + } +} + void PPSSPP_UWPMain::OnMouseWheel(float delta) { InputKeyCode key = NKCODE_EXT_MOUSEWHEEL_UP; if (delta < 0) { @@ -335,7 +294,7 @@ void PPSSPP_UWPMain::OnSuspend() { UWPGraphicsContext::UWPGraphicsContext(std::shared_ptr resources) { - std::vector adapterNames; + std::vector adapterNames = resources->GetAdapters(); draw_ = Draw::T3DCreateD3D11Context( resources->GetD3DDevice(), resources->GetD3DDeviceContext(), resources->GetD3DDevice(), resources->GetD3DDeviceContext(), resources->GetSwapChain(), resources->GetDeviceFeatureLevel(), 0, adapterNames, g_Config.iInflightFrames); @@ -353,7 +312,7 @@ std::string System_GetProperty(SystemProperty prop) { case SYSPROP_NAME: return GetWindowsVersion(); case SYSPROP_LANGREGION: - return langRegion; + return GetLangRegion(); case SYSPROP_CLIPBOARD_TEXT: /* TODO: Need to either change this API or do this on a thread in an ugly fashion. DataPackageView ^view = Clipboard::GetContent(); @@ -412,12 +371,16 @@ int System_GetPropertyInt(SystemProperty prop) { case SYSPROP_DISPLAY_XRES: { CoreWindow^ corewindow = CoreWindow::GetForCurrentThread(); - return (int)corewindow->Bounds.Width; + if (corewindow) { + return (int)corewindow->Bounds.Width; + } } case SYSPROP_DISPLAY_YRES: { CoreWindow^ corewindow = CoreWindow::GetForCurrentThread(); - return (int)corewindow->Bounds.Height; + if (corewindow) { + return (int)corewindow->Bounds.Height; + } } default: return -1; @@ -464,7 +427,12 @@ bool System_GetPropertyBool(SystemProperty prop) { case SYSPROP_CAN_JIT: return true; case SYSPROP_HAS_KEYBOARD: - return true; + { + // 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 isKeybaordAvailable() || isTouchAvailable(); + } default: return false; } @@ -576,6 +544,24 @@ bool System_MakeRequest(SystemRequestType type, int requestId, const std::string if (!param1.empty() && !strcmp(param1.c_str(), "menu")) { CloseLaunchItem(); } + else if (!strcmp(param1.c_str(), "show_keyboard")) { + // Must be performed from UI thread + Windows::ApplicationModel::Core::CoreApplication::MainView->CoreWindow->Dispatcher->RunAsync( + CoreDispatcherPriority::Normal, + ref new Windows::UI::Core::DispatchedHandler([]() + { + ShowInputPane(); + })); + } + else if (!strcmp(param1.c_str(), "hide_keyboard")) { + // Must be performed from UI thread + Windows::ApplicationModel::Core::CoreApplication::MainView->CoreWindow->Dispatcher->RunAsync( + CoreDispatcherPriority::Normal, + ref new Windows::UI::Core::DispatchedHandler([]() + { + HideInputPane(); + })); + } return true; } case SystemRequestType::COPY_TO_CLIPBOARD: @@ -695,20 +681,3 @@ std::string GetCPUBrandString() { return "Unknown"; } } - -// Emulation of TlsAlloc for Windows 10. Used by glslang. Doesn't actually seem to work, other than fixing the linking errors? - -extern "C" { -DWORD WINAPI __imp_TlsAlloc() { - return FlsAlloc(nullptr); -} -BOOL WINAPI __imp_TlsFree(DWORD index) { - return FlsFree(index); -} -BOOL WINAPI __imp_TlsSetValue(DWORD dwTlsIndex, LPVOID lpTlsValue) { - return FlsSetValue(dwTlsIndex, lpTlsValue); -} -LPVOID WINAPI __imp_TlsGetValue(DWORD dwTlsIndex) { - return FlsGetValue(dwTlsIndex); -} -} diff --git a/UWP/PPSSPP_UWPMain.h b/UWP/PPSSPP_UWPMain.h index 211966400c..09cdfbff3b 100644 --- a/UWP/PPSSPP_UWPMain.h +++ b/UWP/PPSSPP_UWPMain.h @@ -34,6 +34,7 @@ public: PPSSPP_UWPMain(App ^app, const std::shared_ptr& deviceResources); ~PPSSPP_UWPMain(); void CreateWindowSizeDependentResources(); + void UpdateScreenState(); bool Render(); // IDeviceNotify @@ -44,6 +45,7 @@ public: // Not sure whether this abstraction is worth it. void OnKeyDown(int scanCode, Windows::System::VirtualKey virtualKey, int repeatCount); void OnKeyUp(int scanCode, Windows::System::VirtualKey virtualKey); + void OnCharacterReceived(int scanCode,unsigned int keyCode); void OnTouchEvent(int touchEvent, int touchId, float x, float y, double timestamp); @@ -57,8 +59,6 @@ public: void OnSuspend(); void Close(); - void LoadStorageFile(Windows::Storage::StorageFile ^file); - private: App ^app_; diff --git a/UWP/Package.appxmanifest b/UWP/Package.appxmanifest index aff2f449dc..9a2de41ac2 100644 --- a/UWP/Package.appxmanifest +++ b/UWP/Package.appxmanifest @@ -1,6 +1,6 @@ - + PPSSPP - PSP emulator diff --git a/UWP/PackageGold.appxmanifest b/UWP/PackageGold.appxmanifest index 3ff86124f9..b4b3569a0c 100644 --- a/UWP/PackageGold.appxmanifest +++ b/UWP/PackageGold.appxmanifest @@ -1,6 +1,6 @@ - + PPSSPP Gold - PSP emulator diff --git a/UWP/PackageNormal.appxmanifest b/UWP/PackageNormal.appxmanifest index aff2f449dc..9a2de41ac2 100644 --- a/UWP/PackageNormal.appxmanifest +++ b/UWP/PackageNormal.appxmanifest @@ -1,6 +1,6 @@ - + PPSSPP - PSP emulator diff --git a/UWP/UWP.vcxproj b/UWP/UWP.vcxproj index 13a2da925c..f41e3d5f66 100644 --- a/UWP/UWP.vcxproj +++ b/UWP/UWP.vcxproj @@ -77,7 +77,6 @@ True False Never - C8DEB388B9BC89D1DC61324E4E9D9FE6A796B7AA False 0 OnApplicationRun @@ -372,6 +371,7 @@ + @@ -398,6 +398,7 @@ Create + diff --git a/UWP/UWP.vcxproj.filters b/UWP/UWP.vcxproj.filters index b0703d24c6..53bb1b424a 100644 --- a/UWP/UWP.vcxproj.filters +++ b/UWP/UWP.vcxproj.filters @@ -126,6 +126,9 @@ UWPHelpers + + UWPHelpers + @@ -145,6 +148,9 @@ Input + + UWPHelpers + diff --git a/UWP/UWPHelpers/InputHelpers.cpp b/UWP/UWPHelpers/InputHelpers.cpp new file mode 100644 index 0000000000..0f2f593d62 --- /dev/null +++ b/UWP/UWPHelpers/InputHelpers.cpp @@ -0,0 +1,95 @@ +// 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/. + + +#include "InputHelpers.h" +#include "UWPUtil.h" +#include "NKCodeFromWindowsSystem.h" +#include "Common/Log.h" + +using namespace Windows::System; +using namespace Windows::Foundation; +using namespace Windows::UI::Core; +using namespace Windows::UI::ViewManagement; +using namespace Windows::ApplicationModel::Core; +using namespace Windows::Data::Xml::Dom; +using namespace Windows::UI::Notifications; + +#pragma region Input Keyboard + +bool isKeybaordAvailable() { + Windows::Devices::Input::KeyboardCapabilities^ keyboardCapabilities = ref new Windows::Devices::Input::KeyboardCapabilities(); + bool hasKeyboard = keyboardCapabilities->KeyboardPresent != 0; + return hasKeyboard; +} + +bool isTouchAvailable() { + Windows::Devices::Input::TouchCapabilities^ touchCapabilities = ref new Windows::Devices::Input::TouchCapabilities(); + bool hasTouch = touchCapabilities->TouchPresent != 0; + return hasTouch; +} + +bool keyboardVisible = false; +bool InputPaneVisible() { + return keyboardVisible; +} + + +void ShowInputPane() { + VERBOSE_LOG(COMMON, "ShowInputKeyboard"); + InputPane::GetForCurrentView()->TryShow(); + keyboardVisible = true; +} + +void HideInputPane() { + VERBOSE_LOG(COMMON, "HideInputKeyboard"); + InputPane::GetForCurrentView()->TryHide(); + keyboardVisible = false; +} + +#pragma endregion + +#pragma region Keys Status +bool IsCapsLockOn() { + auto capsLockState = CoreApplication::MainView->CoreWindow->GetKeyState(VirtualKey::CapitalLock); + return (capsLockState == CoreVirtualKeyStates::Locked); +} +bool IsShiftOnHold() { + auto shiftState = CoreApplication::MainView->CoreWindow->GetKeyState(VirtualKey::Shift); + return (shiftState == CoreVirtualKeyStates::Down); +} +bool IsCtrlOnHold() { + auto ctrlState = CoreApplication::MainView->CoreWindow->GetKeyState(VirtualKey::Control); + return (ctrlState == CoreVirtualKeyStates::Down); +} +#pragma endregion + +#pragma region Misc +std::string GetLangRegion() { + std::string langRegion = "en_US"; + wchar_t lcCountry[256]; + + if (GetLocaleInfoEx(LOCALE_NAME_USER_DEFAULT, LOCALE_SNAME, lcCountry, 256) != FALSE) { + langRegion = ConvertWStringToUTF8(lcCountry); + for (size_t i = 0; i < langRegion.size(); i++) { + if (langRegion[i] == '-') + langRegion[i] = '_'; + } + } + return langRegion; +} +#pragma endregion diff --git a/UWP/UWPHelpers/InputHelpers.h b/UWP/UWPHelpers/InputHelpers.h new file mode 100644 index 0000000000..64504ffd5b --- /dev/null +++ b/UWP/UWPHelpers/InputHelpers.h @@ -0,0 +1,37 @@ +// 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 + +// Input Devices +bool isKeybaordAvailable(); +bool isTouchAvailable(); + +// Input Pane +bool InputPaneVisible(); +void ShowInputPane(); +void HideInputPane(); + + +// Keys Status +bool IsCapsLockOn(); +bool IsShiftOnHold(); +bool IsCtrlOnHold(); + +// Misc +std::string GetLangRegion(); diff --git a/UWP/UWPHelpers/LaunchItem.cpp b/UWP/UWPHelpers/LaunchItem.cpp index a01b303f00..d2c11610fd 100644 --- a/UWP/UWPHelpers/LaunchItem.cpp +++ b/UWP/UWPHelpers/LaunchItem.cpp @@ -21,18 +21,15 @@ #include #include "LaunchItem.h" +#include "StorageAccess.h" #include "Common/Log.h" -#include -#include +#include "Common/System/System.h" +#include "Common/File/Path.h" #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; +#include +#include #pragma region LaunchItemClass class LaunchItem { @@ -93,10 +90,13 @@ public: 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 + concurrency::create_task([&] { + SetState(true); + std::string path = GetFilePath(); + // Delay to be able to launch on startup too + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + System_PostUIMessage("boot", path.c_str()); + }); } } @@ -135,7 +135,6 @@ public: } } launchOnExit = std::string(); - boot_filename.clear(); // Temporary to handle startup items } private: diff --git a/UWP/UWPHelpers/StorageAccess.cpp b/UWP/UWPHelpers/StorageAccess.cpp index 99d4b535cd..d3075345a3 100644 --- a/UWP/UWPHelpers/StorageAccess.cpp +++ b/UWP/UWPHelpers/StorageAccess.cpp @@ -21,7 +21,6 @@ #include "Common/File/Path.h" using namespace Platform; -using namespace Windows::Storage; using namespace Windows::Foundation; using namespace Windows::Foundation::Collections; using namespace Windows::Storage::AccessCache; diff --git a/UWP/UWPHelpers/StorageAccess.h b/UWP/UWPHelpers/StorageAccess.h index e7ad863eae..e7b967b4fb 100644 --- a/UWP/UWPHelpers/StorageAccess.h +++ b/UWP/UWPHelpers/StorageAccess.h @@ -15,12 +15,18 @@ // Official git repository and contact information can be found at // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. +// This header meant to be included within the storage manager +// don't use it within PPSSPP core + #pragma once #include #include +using namespace Windows::Storage; + // Local settings std::string GetDataFromLocalSettings(std::string key); bool AddDataToLocalSettings(std::string key, std::string data, bool replace); +void AddItemToFutureList(IStorageItem^ item); std::list GetFutureAccessList(); diff --git a/UWP/UWPHelpers/StorageManager.cpp b/UWP/UWPHelpers/StorageManager.cpp index b42e27edfd..fed038b22b 100644 --- a/UWP/UWPHelpers/StorageManager.cpp +++ b/UWP/UWPHelpers/StorageManager.cpp @@ -87,7 +87,7 @@ std::string GetMusicFolder() { std::string GetPreviewPath(std::string path) { std::string pathView = path; pathView = ReplaceAll(pathView, "/", "\\"); - std::string currentMemoryStick = ConvertWStringToUTF8(g_Config.memStickDirectory.ToWString()); + std::string currentMemoryStick = GetPSPFolder(); // Ensure memStick sub path replaced by 'ms:' pathView = ReplaceAll(pathView, currentMemoryStick + "\\", "ms:\\"); auto appData = ReplaceAll(GetLocalFolder(), "\\LocalState", ""); @@ -349,6 +349,13 @@ bool OpenFolder(std::string path) { bool GetDriveFreeSpace(Path path, int64_t& space) { bool state = false; + if (path.empty()) { + // This case happen on first start only + path = Path(GetPSPFolder()); + if (g_Config.memStickDirectory.empty()) { + g_Config.memStickDirectory = path; + } + } Platform::String^ wString = ref new Platform::String(path.ToWString().c_str()); StorageFolder^ storageItem; ExecuteTask(storageItem, StorageFolder::GetFolderFromPathAsync(wString)); @@ -377,7 +384,7 @@ bool GetDriveFreeSpace(Path path, int64_t& space) { #pragma region Logs std::string GetLogFile() { std::string logFile; - Path logFilePath = Path(GetPSPFolder() + "\\PSP\\ppsspp.txt"); + Path logFilePath = Path(GetPSPFolder() + "\\PSP\\ppsspplog.txt"); HANDLE h = CreateFile2FromAppW(logFilePath.ToWString().c_str(), GENERIC_WRITE, FILE_SHARE_WRITE, CREATE_ALWAYS, nullptr); if (h != INVALID_HANDLE_VALUE) { logFile = logFilePath.ToString(); diff --git a/UWP/UWPHelpers/StorageManager.h b/UWP/UWPHelpers/StorageManager.h index 996b4be739..4b9f2f0f4c 100644 --- a/UWP/UWPHelpers/StorageManager.h +++ b/UWP/UWPHelpers/StorageManager.h @@ -19,10 +19,9 @@ #include #include +#include #include "Common/File/DirListing.h" - -#include "StorageAccess.h" #include "StoragePickers.h" // Locations