CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
Path: blob/master/UWP/PPSSPP_UWPMain.cpp
Views: 1401
#include "pch.h"1#include "PPSSPP_UWPMain.h"23#include <mutex>4#include <list>5#include <memory>67#include "Common/File/FileUtil.h"8#include "Common/Net/HTTPClient.h"9#include "Common/Net/Resolve.h"10#include "Common/GPU/thin3d_create.h"1112#include "Common/Common.h"13#include "Common/Input/InputState.h"14#include "Common/File/VFS/VFS.h"15#include "Common/Thread/ThreadUtil.h"16#include "Common/Data/Encoding/Utf8.h"17#include "Common/DirectXHelper.h"18#include "Common/File/FileUtil.h"19#include "Common/Log.h"20#include "Common/Log/LogManager.h"21#include "Common/TimeUtil.h"22#include "Common/StringUtils.h"23#include "Common/System/Display.h"24#include "Common/System/NativeApp.h"25#include "Common/System/Request.h"2627#include "Core/System.h"28#include "Core/Loaders.h"29#include "Core/Config.h"3031#include "Windows/InputDevice.h"32#include "Windows/XinputDevice.h"33#include "NKCodeFromWindowsSystem.h"34#include "XAudioSoundStream.h"35#include "UWPUtil.h"36#include "App.h"3738// UWP Helpers includes39#include "UWPHelpers/StorageManager.h"40#include "UWPHelpers/StorageAsync.h"41#include "UWPHelpers/LaunchItem.h"42#include "UWPHelpers/InputHelpers.h"4344using namespace UWP;45using namespace Windows::Foundation;46using namespace Windows::Storage;47using namespace Windows::Storage::Streams;48using namespace Windows::System::Threading;49using namespace Windows::ApplicationModel::DataTransfer;50using namespace Windows::Devices::Enumeration;51using namespace Concurrency;5253// UGLY!54extern WindowsAudioBackend *winAudioBackend;55std::list<std::unique_ptr<InputDevice>> g_input;5657// TODO: Use Microsoft::WRL::ComPtr<> for D3D11 objects?58// TODO: See https://github.com/Microsoft/Windows-universal-samples/tree/master/Samples/WindowsAudioSession for WASAPI with UWP59// TODO: Low latency input: https://github.com/Microsoft/Windows-universal-samples/tree/master/Samples/LowLatencyInput/cpp6061// Loads and initializes application assets when the application is loaded.62PPSSPP_UWPMain::PPSSPP_UWPMain(App ^app, const std::shared_ptr<DX::DeviceResources>& deviceResources) :63app_(app),64m_deviceResources(deviceResources)65{66TimeInit();6768// Register to be notified if the Device is lost or recreated69m_deviceResources->RegisterDeviceNotify(this);7071ctx_.reset(new UWPGraphicsContext(deviceResources));7273#if _DEBUG74LogManager::GetInstance()->SetAllLogLevels(LogLevel::LDEBUG);7576if (g_Config.bEnableLogging) {77LogManager::GetInstance()->ChangeFileLog(GetLogFile().c_str());78}79#endif8081// At this point we have main requirements initialized (Log, Config, NativeInit, Device)82NativeInitGraphics(ctx_.get());83NativeResized();8485int width = m_deviceResources->GetScreenViewport().Width;86int height = m_deviceResources->GetScreenViewport().Height;8788ctx_->GetDrawContext()->HandleEvent(Draw::Event::GOT_BACKBUFFER, width, height, m_deviceResources->GetBackBufferRenderTargetView());8990// add first XInput device to respond91g_input.push_back(std::make_unique<XinputDevice>());9293InputDevice::BeginPolling();9495// Prepare input pane (for Xbox & touch devices)96PrepareInputPane();97}9899PPSSPP_UWPMain::~PPSSPP_UWPMain() {100InputDevice::StopPolling();101ctx_->GetDrawContext()->HandleEvent(Draw::Event::LOST_BACKBUFFER, 0, 0, nullptr);102NativeShutdownGraphics();103NativeShutdown();104g_VFS.Clear();105106// Deregister device notification107m_deviceResources->RegisterDeviceNotify(nullptr);108net::Shutdown();109}110111// Updates application state when the window size changes (e.g. device orientation change)112void PPSSPP_UWPMain::CreateWindowSizeDependentResources() {113ctx_->GetDrawContext()->HandleEvent(Draw::Event::LOST_BACKBUFFER, 0, 0, nullptr);114115NativeResized();116117int width = m_deviceResources->GetScreenViewport().Width;118int height = m_deviceResources->GetScreenViewport().Height;119ctx_->GetDrawContext()->HandleEvent(Draw::Event::GOT_BACKBUFFER, width, height, m_deviceResources->GetBackBufferRenderTargetView());120}121122void PPSSPP_UWPMain::UpdateScreenState() {123// This code was included into the render loop directly124// based on my test I don't understand why it should be called each loop125// is it better to call it on demand only, like when screen state changed?126auto context = m_deviceResources->GetD3DDeviceContext();127128switch (m_deviceResources->ComputeDisplayRotation()) {129case DXGI_MODE_ROTATION_IDENTITY: g_display.rotation = DisplayRotation::ROTATE_0; break;130case DXGI_MODE_ROTATION_ROTATE90: g_display.rotation = DisplayRotation::ROTATE_90; break;131case DXGI_MODE_ROTATION_ROTATE180: g_display.rotation = DisplayRotation::ROTATE_180; break;132case DXGI_MODE_ROTATION_ROTATE270: g_display.rotation = DisplayRotation::ROTATE_270; break;133}134// Not super elegant but hey.135memcpy(&g_display.rot_matrix, &m_deviceResources->GetOrientationTransform3D(), sizeof(float) * 16);136137// Reset the viewport to target the whole screen.138auto viewport = m_deviceResources->GetScreenViewport();139140g_display.pixel_xres = viewport.Width;141g_display.pixel_yres = viewport.Height;142143if (g_display.rotation == DisplayRotation::ROTATE_90 || g_display.rotation == DisplayRotation::ROTATE_270) {144// We need to swap our width/height.145std::swap(g_display.pixel_xres, g_display.pixel_yres);146}147148g_display.dpi = m_deviceResources->GetActualDpi();149150if (System_GetPropertyInt(SYSPROP_DEVICE_TYPE) == DEVICE_TYPE_MOBILE) {151// Boost DPI a bit to look better.152g_display.dpi *= 96.0f / 136.0f;153}154g_display.dpi_scale_x = 96.0f / g_display.dpi;155g_display.dpi_scale_y = 96.0f / g_display.dpi;156157g_display.pixel_in_dps_x = 1.0f / g_display.dpi_scale_x;158g_display.pixel_in_dps_y = 1.0f / g_display.dpi_scale_y;159160g_display.dp_xres = g_display.pixel_xres * g_display.dpi_scale_x;161g_display.dp_yres = g_display.pixel_yres * g_display.dpi_scale_y;162163context->RSSetViewports(1, &viewport);164}165166// Renders the current frame according to the current application state.167// Returns true if the frame was rendered and is ready to be displayed.168bool PPSSPP_UWPMain::Render() {169static bool hasSetThreadName = false;170if (!hasSetThreadName) {171SetCurrentThreadName("UWPRenderThread");172hasSetThreadName = true;173}174175UpdateScreenState();176177NativeFrame(ctx_.get());178return true;179}180181// Notifies renderers that device resources need to be released.182void PPSSPP_UWPMain::OnDeviceLost() {183ctx_->GetDrawContext()->HandleEvent(Draw::Event::LOST_DEVICE, 0, 0, nullptr);184}185186// Notifies renderers that device resources may now be recreated.187void PPSSPP_UWPMain::OnDeviceRestored() {188CreateWindowSizeDependentResources();189190ctx_->GetDrawContext()->HandleEvent(Draw::Event::GOT_DEVICE, 0, 0, nullptr);191}192193void PPSSPP_UWPMain::OnKeyDown(int scanCode, Windows::System::VirtualKey virtualKey, int repeatCount) {194// TODO: Look like (Ctrl, Alt, Shift) don't trigger this event195bool isDPad = (int)virtualKey >= 195 && (int)virtualKey <= 218; // DPad buttons range196DPadInputState(isDPad);197198auto iter = virtualKeyCodeToNKCode.find(virtualKey);199if (iter != virtualKeyCodeToNKCode.end()) {200KeyInput key{};201key.deviceId = DEVICE_ID_KEYBOARD;202key.keyCode = iter->second;203key.flags = KEY_DOWN | (repeatCount > 1 ? KEY_IS_REPEAT : 0);204NativeKey(key);205}206}207208void PPSSPP_UWPMain::OnKeyUp(int scanCode, Windows::System::VirtualKey virtualKey) {209auto iter = virtualKeyCodeToNKCode.find(virtualKey);210if (iter != virtualKeyCodeToNKCode.end()) {211KeyInput key{};212key.deviceId = DEVICE_ID_KEYBOARD;213key.keyCode = iter->second;214key.flags = KEY_UP;215NativeKey(key);216}217}218219void PPSSPP_UWPMain::OnCharacterReceived(int scanCode, unsigned int keyCode) {220// This event triggered only in chars case, (Arrows, Delete..etc don't call it)221// TODO: Add ` && !IsCtrlOnHold()` once it's ready and implemented222if (isTextEditActive()) {223KeyInput key{};224key.deviceId = DEVICE_ID_KEYBOARD;225key.keyCode = (InputKeyCode)keyCode;226// After many tests turns out for char just add `KEY_CHAR` for the flags227// any other flag like `KEY_DOWN` will cause conflict and trigger something else228key.flags = KEY_CHAR;229NativeKey(key);230}231}232233void PPSSPP_UWPMain::OnMouseWheel(float delta) {234InputKeyCode key = NKCODE_EXT_MOUSEWHEEL_UP;235if (delta < 0) {236key = NKCODE_EXT_MOUSEWHEEL_DOWN;237} else if (delta == 0) {238return;239}240241KeyInput keyInput{};242keyInput.keyCode = key;243keyInput.deviceId = DEVICE_ID_MOUSE;244keyInput.flags = KEY_DOWN;245NativeKey(keyInput);246247// KEY_UP is now sent automatically afterwards for mouse wheel events, see NativeKey.248}249250bool PPSSPP_UWPMain::OnHardwareButton(HardwareButton button) {251KeyInput keyInput{};252keyInput.deviceId = DEVICE_ID_KEYBOARD;253keyInput.flags = KEY_DOWN | KEY_UP;254switch (button) {255case HardwareButton::BACK:256keyInput.keyCode = NKCODE_BACK;257return NativeKey(keyInput);258default:259return false;260}261}262263void PPSSPP_UWPMain::OnTouchEvent(int touchEvent, int touchId, float x, float y, double timestamp) {264// We get the coordinate in Windows' device independent pixels already. So let's undo that,265// and then apply our own "dpi".266float dpiFactor_x = m_deviceResources->GetActualDpi() / 96.0f;267float dpiFactor_y = dpiFactor_x;268dpiFactor_x /= g_display.pixel_in_dps_x;269dpiFactor_y /= g_display.pixel_in_dps_y;270271TouchInput input{};272input.id = touchId;273input.x = x * dpiFactor_x;274input.y = y * dpiFactor_y;275input.flags = touchEvent;276input.timestamp = timestamp;277NativeTouch(input);278279KeyInput key{};280key.deviceId = DEVICE_ID_MOUSE;281if (touchEvent & TOUCH_DOWN) {282key.keyCode = NKCODE_EXT_MOUSEBUTTON_1;283key.flags = KEY_DOWN;284NativeKey(key);285}286if (touchEvent & TOUCH_UP) {287key.keyCode = NKCODE_EXT_MOUSEBUTTON_1;288key.flags = KEY_UP;289NativeKey(key);290}291}292293void PPSSPP_UWPMain::OnSuspend() {294// TODO295}296297298UWPGraphicsContext::UWPGraphicsContext(std::shared_ptr<DX::DeviceResources> resources) {299std::vector<std::string> adapterNames = resources->GetAdapters();300301draw_ = Draw::T3DCreateD3D11Context(302resources->GetD3DDevice(), resources->GetD3DDeviceContext(), resources->GetD3DDevice(), resources->GetD3DDeviceContext(), resources->GetSwapChain(), resources->GetDeviceFeatureLevel(), 0, adapterNames, g_Config.iInflightFrames);303bool success = draw_->CreatePresets();304_assert_(success);305}306307void UWPGraphicsContext::Shutdown() {308delete draw_;309}310311std::string System_GetProperty(SystemProperty prop) {312static bool hasCheckedGPUDriverVersion = false;313switch (prop) {314case SYSPROP_NAME:315return GetSystemName();316case SYSPROP_SYSTEMBUILD:317return GetWindowsBuild();318case SYSPROP_LANGREGION:319return GetLangRegion();320case SYSPROP_CLIPBOARD_TEXT:321/* TODO: Need to either change this API or do this on a thread in an ugly fashion.322DataPackageView ^view = Clipboard::GetContent();323if (view) {324string text = await view->GetTextAsync();325}326*/327return "";328case SYSPROP_GPUDRIVER_VERSION:329return "";330case SYSPROP_BUILD_VERSION:331return PPSSPP_GIT_VERSION;332default:333return "";334}335}336337std::vector<std::string> System_GetPropertyStringVec(SystemProperty prop) {338std::vector<std::string> result;339switch (prop) {340case SYSPROP_TEMP_DIRS:341{342std::wstring tempPath(MAX_PATH, '\0');343size_t sz = GetTempPath((DWORD)tempPath.size(), &tempPath[0]);344if (sz >= tempPath.size()) {345tempPath.resize(sz);346sz = GetTempPath((DWORD)tempPath.size(), &tempPath[0]);347}348// Need to resize off the null terminator either way.349tempPath.resize(sz);350result.push_back(ConvertWStringToUTF8(tempPath));351return result;352}353354default:355return result;356}357}358359int64_t System_GetPropertyInt(SystemProperty prop) {360switch (prop) {361case SYSPROP_AUDIO_SAMPLE_RATE:362return winAudioBackend ? winAudioBackend->GetSampleRate() : -1;363364case SYSPROP_DEVICE_TYPE:365{366if (IsMobile()) {367return DEVICE_TYPE_MOBILE;368} else if (IsXBox()) {369return DEVICE_TYPE_TV;370} else {371return DEVICE_TYPE_DESKTOP;372}373}374case SYSPROP_DISPLAY_XRES:375{376CoreWindow^ corewindow = CoreWindow::GetForCurrentThread();377if (corewindow) {378return (int)corewindow->Bounds.Width;379}380}381case SYSPROP_DISPLAY_YRES:382{383CoreWindow^ corewindow = CoreWindow::GetForCurrentThread();384if (corewindow) {385return (int)corewindow->Bounds.Height;386}387}388default:389return -1;390}391}392393float System_GetPropertyFloat(SystemProperty prop) {394switch (prop) {395case SYSPROP_DISPLAY_REFRESH_RATE:396return 60.f;397case SYSPROP_DISPLAY_SAFE_INSET_LEFT:398case SYSPROP_DISPLAY_SAFE_INSET_RIGHT:399case SYSPROP_DISPLAY_SAFE_INSET_TOP:400case SYSPROP_DISPLAY_SAFE_INSET_BOTTOM:401return 0.0f;402default:403return -1;404}405}406407void System_Toast(std::string_view str) {}408409bool System_GetPropertyBool(SystemProperty prop) {410switch (prop) {411case SYSPROP_HAS_TEXT_CLIPBOARD:412case SYSPROP_HAS_OPEN_DIRECTORY:413{414return !IsXBox();415}416case SYSPROP_HAS_FILE_BROWSER:417return true;418case SYSPROP_HAS_FOLDER_BROWSER:419return true;420case SYSPROP_HAS_IMAGE_BROWSER:421return true; // we just use the file browser422case SYSPROP_HAS_BACK_BUTTON:423return true;424case SYSPROP_HAS_ACCELEROMETER:425return IsMobile();426case SYSPROP_APP_GOLD:427#ifdef GOLD428return true;429#else430return false;431#endif432case SYSPROP_CAN_JIT:433return true;434case SYSPROP_HAS_KEYBOARD:435{436// Do actual check437// touch devices has input pane, we need to depend on it438// I don't know any possible way to display input dialog in non-xaml apps439return isKeyboardAvailable() || isTouchAvailable();440}441case SYSPROP_DEBUGGER_PRESENT:442return IsDebuggerPresent();443case SYSPROP_OK_BUTTON_LEFT:444return true;445default:446return false;447}448}449450void System_Notify(SystemNotification notification) {451switch (notification) {452case SystemNotification::POLL_CONTROLLERS:453{454for (const auto &device : g_input)455{456if (device->UpdateState() == InputDevice::UPDATESTATE_SKIP_PAD)457break;458}459break;460}461default:462break;463}464}465466bool System_MakeRequest(SystemRequestType type, int requestId, const std::string ¶m1, const std::string ¶m2, int64_t param3, int64_t param4) {467switch (type) {468469case SystemRequestType::EXIT_APP:470{471bool state = false;472ExecuteTask(state, Windows::UI::ViewManagement::ApplicationView::GetForCurrentView()->TryConsolidateAsync());473if (!state) {474// Notify the user?475}476return true;477}478case SystemRequestType::RESTART_APP:479{480Windows::ApplicationModel::Core::AppRestartFailureReason error;481ExecuteTask(error, Windows::ApplicationModel::Core::CoreApplication::RequestRestartAsync(nullptr));482if (error != Windows::ApplicationModel::Core::AppRestartFailureReason::RestartPending) {483// Shutdown484System_MakeRequest(SystemRequestType::EXIT_APP, requestId, param1, param2, param3, param4);485}486return true;487}488case SystemRequestType::BROWSE_FOR_IMAGE:489{490std::vector<std::string> supportedExtensions = { ".jpg", ".png" };491492//Call file picker493ChooseFile(supportedExtensions).then([requestId](std::string filePath) {494if (filePath.size() > 1) {495g_requestManager.PostSystemSuccess(requestId, filePath.c_str());496}497else {498g_requestManager.PostSystemFailure(requestId);499}500});501return true;502}503case SystemRequestType::BROWSE_FOR_FILE:504{505std::vector<std::string> supportedExtensions = {};506switch ((BrowseFileType)param3) {507case BrowseFileType::BOOTABLE:508supportedExtensions = { ".cso", ".iso", ".chd", ".elf", ".pbp", ".zip", ".prx", ".bin" }; // should .bin even be here?509break;510case BrowseFileType::INI:511supportedExtensions = { ".ini" };512break;513case BrowseFileType::ZIP:514supportedExtensions = { ".zip" };515break;516case BrowseFileType::DB:517supportedExtensions = { ".db" };518break;519case BrowseFileType::SOUND_EFFECT:520supportedExtensions = { ".wav", ".mp3" };521break;522case BrowseFileType::ANY:523// 'ChooseFile' will added '*' by default when there are no extensions assigned524break;525default:526ERROR_LOG(Log::FileSystem, "Unexpected BrowseFileType: %d", param3);527return false;528}529530//Call file picker531ChooseFile(supportedExtensions).then([requestId](std::string filePath) {532if (filePath.size() > 1) {533g_requestManager.PostSystemSuccess(requestId, filePath.c_str());534}535else {536g_requestManager.PostSystemFailure(requestId);537}538});539540return true;541}542case SystemRequestType::BROWSE_FOR_FOLDER:543{544ChooseFolder().then([requestId](std::string folderPath) {545if (folderPath.size() > 1) {546g_requestManager.PostSystemSuccess(requestId, folderPath.c_str());547}548else {549g_requestManager.PostSystemFailure(requestId);550}551});552return true;553}554case SystemRequestType::NOTIFY_UI_EVENT:555{556switch ((UIEventNotification)param3) {557case UIEventNotification::MENU_RETURN:558CloseLaunchItem();559break;560case UIEventNotification::POPUP_CLOSED:561DeactivateTextEditInput();562break;563case UIEventNotification::TEXT_GOTFOCUS:564ActivateTextEditInput(true);565break;566case UIEventNotification::TEXT_LOSTFOCUS:567DeactivateTextEditInput(true);568break;569default:570break;571}572return true;573}574case SystemRequestType::COPY_TO_CLIPBOARD:575{576auto dataPackage = ref new DataPackage();577dataPackage->RequestedOperation = DataPackageOperation::Copy;578dataPackage->SetText(ToPlatformString(param1));579Clipboard::SetContent(dataPackage);580return true;581}582case SystemRequestType::TOGGLE_FULLSCREEN_STATE:583{584auto view = Windows::UI::ViewManagement::ApplicationView::GetForCurrentView();585bool flag = !view->IsFullScreenMode;586if (param1 == "0") {587flag = false;588} else if (param1 == "1"){589flag = true;590}591if (flag) {592view->TryEnterFullScreenMode();593} else {594view->ExitFullScreenMode();595}596return true;597}598case SystemRequestType::SHOW_FILE_IN_FOLDER:599OpenFolder(param1);600return true;601default:602return false;603}604}605606void System_LaunchUrl(LaunchUrlType urlType, const char *url) {607auto uri = ref new Windows::Foundation::Uri(ToPlatformString(url));608609create_task(Windows::System::Launcher::LaunchUriAsync(uri)).then([](bool b) {});610}611612void System_Vibrate(int length_ms) {613#if _M_ARM614if (length_ms == -1 || length_ms == -3)615length_ms = 50;616else if (length_ms == -2)617length_ms = 25;618else619return;620621auto timeSpan = Windows::Foundation::TimeSpan();622timeSpan.Duration = length_ms * 10000;623// TODO: Can't use this?624// Windows::Phone::Devices::Notification::VibrationDevice::GetDefault()->Vibrate(timeSpan);625#endif626}627628void System_AskForPermission(SystemPermission permission) {629// Do nothing630}631632PermissionStatus System_GetPermissionStatus(SystemPermission permission) {633return PERMISSION_STATUS_GRANTED;634}635636std::string GetCPUBrandString() {637Platform::String^ cpu_id = nullptr;638Platform::String^ cpu_name = nullptr;639640// GUID_DEVICE_PROCESSOR: {97FADB10-4E33-40AE-359C-8BEF029DBDD0}641Platform::String^ if_filter = L"System.Devices.InterfaceClassGuid:=\"{97FADB10-4E33-40AE-359C-8BEF029DBDD0}\"";642643// Enumerate all CPU DeviceInterfaces, and get DeviceInstanceID of the first one.644auto if_task = create_task(645DeviceInformation::FindAllAsync(if_filter)).then([&](DeviceInformationCollection ^ collection) {646if (collection->Size > 0) {647auto cpu = collection->GetAt(0);648auto id = cpu->Properties->Lookup(L"System.Devices.DeviceInstanceID");649cpu_id = dynamic_cast<Platform::String^>(id);650}651});652653try {654if_task.wait();655}656catch (const std::exception & e) {657const char* what = e.what();658INFO_LOG(Log::System, "%s", what);659}660661if (cpu_id != nullptr) {662// Get the Device with the same ID as the DeviceInterface663// Then get the name (description) of that Device664// We have to do this because the DeviceInterface we get doesn't have a proper description.665Platform::String^ dev_filter = L"System.Devices.DeviceInstanceID:=\"" + cpu_id + L"\"";666667auto dev_task = create_task(668DeviceInformation::FindAllAsync(dev_filter, {}, DeviceInformationKind::Device)).then(669[&](DeviceInformationCollection ^ collection) {670if (collection->Size > 0) {671cpu_name = collection->GetAt(0)->Name;672}673});674675try {676dev_task.wait();677}678catch (const std::exception & e) {679const char* what = e.what();680INFO_LOG(Log::System, "%s", what);681}682}683684if (cpu_name != nullptr) {685return FromPlatformString(cpu_name);686} else {687return "Unknown";688}689}690691692