mirror of https://github.com/stenzek/duckstation
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
770 lines
23 KiB
C++
770 lines
23 KiB
C++
#include "uwp_host_interface.h"
|
|
#include "common/assert.h"
|
|
#include "common/byte_stream.h"
|
|
#include "common/file_system.h"
|
|
#include "common/log.h"
|
|
#include "common/string_util.h"
|
|
#include "core/controller.h"
|
|
#include "core/gpu.h"
|
|
#include "core/host_display.h"
|
|
#include "core/system.h"
|
|
#include "frontend-common/controller_interface.h"
|
|
#include "frontend-common/d3d11_host_display.h"
|
|
#include "frontend-common/d3d12_host_display.h"
|
|
#include "frontend-common/fullscreen_ui.h"
|
|
#include "frontend-common/icon.h"
|
|
#include "frontend-common/imgui_styles.h"
|
|
#include "frontend-common/ini_settings_interface.h"
|
|
#include "imgui.h"
|
|
#include "imgui_internal.h"
|
|
#include "imgui_stdlib.h"
|
|
#include "uwp_key_map.h"
|
|
#include <cinttypes>
|
|
#include <cmath>
|
|
Log_SetChannel(UWPHostInterface);
|
|
|
|
#include <gamingdeviceinformation.h>
|
|
#include <winrt/Windows.Graphics.Display.Core.h>
|
|
#include <winrt/Windows.Graphics.Display.h>
|
|
#include <winrt/Windows.Storage.h>
|
|
#include <winrt/Windows.System.Profile.h>
|
|
|
|
static bool IsRunningOnXbox()
|
|
{
|
|
const auto version_info = winrt::Windows::System::Profile::AnalyticsInfo::VersionInfo();
|
|
const auto device_family = version_info.DeviceFamily();
|
|
return (device_family == L"Windows.Xbox");
|
|
}
|
|
|
|
UWPHostInterface::UWPHostInterface() = default;
|
|
|
|
UWPHostInterface::~UWPHostInterface() = default;
|
|
|
|
winrt::Windows::ApplicationModel::Core::IFrameworkView UWPHostInterface::CreateView()
|
|
{
|
|
return *this;
|
|
}
|
|
|
|
void UWPHostInterface::Initialize(const winrt::Windows::ApplicationModel::Core::CoreApplicationView& a)
|
|
{
|
|
winrt::Windows::ApplicationModel::Core::CoreApplication::Suspending({this, &UWPHostInterface::OnSuspending});
|
|
winrt::Windows::ApplicationModel::Core::CoreApplication::Resuming({this, &UWPHostInterface::OnResuming});
|
|
}
|
|
|
|
void UWPHostInterface::Load(const winrt::hstring&) {}
|
|
|
|
void UWPHostInterface::Uninitialize() {}
|
|
|
|
const char* UWPHostInterface::GetFrontendName() const
|
|
{
|
|
return "DuckStation UWP Frontend";
|
|
}
|
|
|
|
bool UWPHostInterface::Initialize()
|
|
{
|
|
Log::SetDebugOutputParams(true, nullptr, LOGLEVEL_DEBUG);
|
|
if (!SetDirectories())
|
|
return false;
|
|
|
|
m_settings_interface = std::make_unique<INISettingsInterface>(GetSettingsFileName());
|
|
m_flags.force_fullscreen_ui = true;
|
|
m_fullscreen_ui_enabled = true;
|
|
|
|
if (!CommonHostInterface::Initialize())
|
|
return false;
|
|
|
|
SetImGuiKeyMap();
|
|
|
|
const bool start_fullscreen = m_flags.start_fullscreen || g_settings.start_fullscreen;
|
|
if (!CreateDisplay(start_fullscreen))
|
|
{
|
|
Log_ErrorPrintf("Failed to create host display");
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void UWPHostInterface::Shutdown()
|
|
{
|
|
DestroyDisplay();
|
|
|
|
CommonHostInterface::Shutdown();
|
|
}
|
|
|
|
void UWPHostInterface::SetDefaultSettings(SettingsInterface& si)
|
|
{
|
|
CommonHostInterface::SetDefaultSettings(si);
|
|
|
|
si.SetStringValue("CPU", "FastmemMode", "LUT");
|
|
si.SetStringValue("Main", "ControllerBackend", "XInput");
|
|
si.AddToStringList("GameList", "RecursivePaths", GetUserDirectoryRelativePath("games").c_str());
|
|
|
|
// TODO: Maybe we should bind this to F1 in the future.
|
|
si.SetStringValue("Hotkeys", "OpenQuickMenu", "Keyboard/Escape");
|
|
|
|
if (IsRunningOnXbox())
|
|
SetDefaultSettingsForXbox(si);
|
|
}
|
|
|
|
bool UWPHostInterface::CreateDisplay(bool fullscreen)
|
|
{
|
|
Assert(!m_display);
|
|
|
|
m_appview = winrt::Windows::UI::ViewManagement::ApplicationView::GetForCurrentView();
|
|
m_appview.PreferredLaunchWindowingMode(
|
|
fullscreen ? winrt::Windows::UI::ViewManagement::ApplicationViewWindowingMode::FullScreen :
|
|
winrt::Windows::UI::ViewManagement::ApplicationViewWindowingMode::Auto);
|
|
|
|
m_window.Activate();
|
|
|
|
const auto di = winrt::Windows::Graphics::Display::DisplayInformation::GetForCurrentView();
|
|
const auto hdi = winrt::Windows::Graphics::Display::Core::HdmiDisplayInformation::GetForCurrentView();
|
|
const s32 resolution_scale = static_cast<s32>(di.ResolutionScale());
|
|
|
|
WindowInfo wi;
|
|
wi.type = WindowInfo::Type::WinRT;
|
|
wi.window_handle = winrt::get_unknown(m_window);
|
|
wi.surface_scale = static_cast<float>(resolution_scale) / 100.0f;
|
|
wi.surface_width = static_cast<u32>(m_window.Bounds().Width * wi.surface_scale);
|
|
wi.surface_height = static_cast<s32>(m_window.Bounds().Height * wi.surface_scale);
|
|
if (hdi)
|
|
{
|
|
try
|
|
{
|
|
const auto dm = hdi.GetCurrentDisplayMode();
|
|
const u32 hdmi_width = dm.ResolutionWidthInRawPixels();
|
|
const u32 hdmi_height = dm.ResolutionHeightInRawPixels();
|
|
wi.surface_refresh_rate = static_cast<float>(dm.RefreshRate());
|
|
Log_InfoPrintf("HDMI mode: %ux%u @ %.2f hz", hdmi_width, hdmi_height, wi.surface_refresh_rate);
|
|
|
|
// If we're running on Xbox, use the HDMI mode instead of the CoreWindow size.
|
|
// In UWP, the CoreWindow is always 1920x1080, even when running at 4K.
|
|
if (IsRunningOnXbox())
|
|
{
|
|
GAMING_DEVICE_MODEL_INFORMATION gdinfo = {};
|
|
if (SUCCEEDED(GetGamingDeviceModelInformation(&gdinfo)) && gdinfo.vendorId == GAMING_DEVICE_VENDOR_ID_MICROSOFT)
|
|
{
|
|
Log_InfoPrintf("Overriding core window size %ux%u with HDMI size %ux%u", wi.surface_width, wi.surface_height,
|
|
hdmi_width, hdmi_height);
|
|
wi.surface_scale *= static_cast<float>(hdmi_width) / static_cast<float>(wi.surface_width);
|
|
wi.surface_width = hdmi_width;
|
|
wi.surface_height = hdmi_height;
|
|
}
|
|
}
|
|
}
|
|
catch (const winrt::hresult_error&)
|
|
{
|
|
}
|
|
}
|
|
|
|
if (g_settings.gpu_renderer == GPURenderer::HardwareD3D12)
|
|
m_display = std::make_unique<FrontendCommon::D3D12HostDisplay>();
|
|
else
|
|
m_display = std::make_unique<FrontendCommon::D3D11HostDisplay>();
|
|
|
|
if (!m_display->CreateRenderDevice(wi, g_settings.gpu_adapter, g_settings.gpu_use_debug_device,
|
|
g_settings.gpu_threaded_presentation) ||
|
|
!m_display->InitializeRenderDevice(GetShaderCacheBasePath(), g_settings.gpu_use_debug_device,
|
|
g_settings.gpu_threaded_presentation) ||
|
|
!CreateHostDisplayResources())
|
|
{
|
|
m_display->DestroyRenderDevice();
|
|
m_display.reset();
|
|
ReportError("Failed to create/initialize display render device");
|
|
return false;
|
|
}
|
|
|
|
if (!CreateHostDisplayResources())
|
|
Log_WarningPrint("Failed to create host display resources");
|
|
|
|
Log_InfoPrintf("Host display initialized at %ux%u resolution", m_display->GetWindowWidth(),
|
|
m_display->GetWindowHeight());
|
|
return true;
|
|
}
|
|
|
|
void UWPHostInterface::DestroyDisplay()
|
|
{
|
|
ReleaseHostDisplayResources();
|
|
|
|
if (m_display)
|
|
m_display->DestroyRenderDevice();
|
|
|
|
m_display.reset();
|
|
}
|
|
|
|
bool UWPHostInterface::AcquireHostDisplay()
|
|
{
|
|
return true;
|
|
}
|
|
|
|
void UWPHostInterface::ReleaseHostDisplay()
|
|
{
|
|
// restore vsync, since we don't want to burn cycles at the menu
|
|
m_display->SetVSync(true);
|
|
}
|
|
|
|
void UWPHostInterface::PollAndUpdate()
|
|
{
|
|
CommonHostInterface::PollAndUpdate();
|
|
|
|
ImGuiIO& io = ImGui::GetIO();
|
|
if (m_text_input_requested != io.WantTextInput)
|
|
{
|
|
const bool activate = io.WantTextInput;
|
|
Log_InfoPrintf("%s input pane...", activate ? "showing" : "hiding");
|
|
|
|
m_text_input_requested = activate;
|
|
m_dispatcher.RunAsync(winrt::Windows::UI::Core::CoreDispatcherPriority::Normal, [this, activate]() {
|
|
const auto input_pane = winrt::Windows::UI::ViewManagement::InputPane::GetForCurrentView();
|
|
if (input_pane)
|
|
{
|
|
if (activate)
|
|
input_pane.TryShow();
|
|
else
|
|
input_pane.TryHide();
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
void UWPHostInterface::RequestExit()
|
|
{
|
|
m_shutdown_flag.store(true);
|
|
m_dispatcher.RunAsync(winrt::Windows::UI::Core::CoreDispatcherPriority::Normal,
|
|
[this]() { winrt::Windows::ApplicationModel::Core::CoreApplication::Exit(); });
|
|
}
|
|
|
|
void UWPHostInterface::SetMouseMode(bool relative, bool hide_cursor)
|
|
{
|
|
}
|
|
|
|
void UWPHostInterface::Run()
|
|
{
|
|
if (!Initialize())
|
|
{
|
|
Shutdown();
|
|
return;
|
|
}
|
|
|
|
m_emulation_thread = std::thread(&UWPHostInterface::EmulationThreadEntryPoint, this);
|
|
|
|
m_dispatcher.ProcessEvents(winrt::Windows::UI::Core::CoreProcessEventsOption::ProcessUntilQuit);
|
|
m_shutdown_flag.store(true);
|
|
m_emulation_thread.join();
|
|
}
|
|
|
|
void UWPHostInterface::EmulationThreadEntryPoint()
|
|
{
|
|
if (m_fullscreen_ui_enabled)
|
|
{
|
|
FullscreenUI::SetDebugMenuAllowed(true);
|
|
FullscreenUI::QueueGameListRefresh();
|
|
}
|
|
|
|
// process events to pick up controllers before updating input map
|
|
PollAndUpdate();
|
|
UpdateInputMap();
|
|
|
|
if (m_was_running_on_suspend && ShouldSaveResumeState())
|
|
ResumeSystemFromMostRecentState();
|
|
|
|
while (!m_shutdown_flag.load())
|
|
{
|
|
RunCallbacks();
|
|
PollAndUpdate();
|
|
|
|
ImGui::NewFrame();
|
|
|
|
if (System::IsRunning())
|
|
{
|
|
if (m_display_all_frames)
|
|
System::RunFrame();
|
|
else
|
|
System::RunFrames();
|
|
|
|
UpdateControllerMetaState();
|
|
if (m_frame_step_request)
|
|
{
|
|
m_frame_step_request = false;
|
|
PauseSystem(true);
|
|
}
|
|
}
|
|
|
|
// rendering
|
|
{
|
|
DrawImGuiWindows();
|
|
ImGui::Render();
|
|
ImGui::EndFrame();
|
|
|
|
m_display->Render();
|
|
|
|
if (System::IsRunning())
|
|
{
|
|
System::UpdatePerformanceCounters();
|
|
|
|
if (m_throttler_enabled)
|
|
System::Throttle();
|
|
}
|
|
}
|
|
}
|
|
|
|
// Save state on exit so it can be resumed
|
|
if (!System::IsShutdown())
|
|
PowerOffSystem(ShouldSaveResumeState());
|
|
}
|
|
|
|
void UWPHostInterface::ReportMessage(const char* message)
|
|
{
|
|
Log_InfoPrint(message);
|
|
AddOSDMessage(message, 10.0f);
|
|
}
|
|
|
|
void UWPHostInterface::ReportError(const char* message)
|
|
{
|
|
Log_ErrorPrint(message);
|
|
|
|
if (!m_display)
|
|
return;
|
|
|
|
const bool was_in_frame = GImGui->FrameCount != GImGui->FrameCountEnded;
|
|
if (was_in_frame)
|
|
ImGui::EndFrame();
|
|
|
|
bool done = false;
|
|
while (!done)
|
|
{
|
|
RunCallbacks();
|
|
PollAndUpdate();
|
|
if (m_fullscreen_ui_enabled)
|
|
FullscreenUI::SetImGuiNavInputs();
|
|
|
|
ImGui::NewFrame();
|
|
done = FullscreenUI::DrawErrorWindow(message);
|
|
ImGui::EndFrame();
|
|
m_display->Render();
|
|
}
|
|
|
|
if (was_in_frame)
|
|
ImGui::NewFrame();
|
|
}
|
|
|
|
bool UWPHostInterface::ConfirmMessage(const char* message)
|
|
{
|
|
Log_InfoPrintf("Confirm: %s", message);
|
|
|
|
if (!m_display)
|
|
return true;
|
|
|
|
const bool was_in_frame = GImGui->FrameCount != GImGui->FrameCountEnded;
|
|
if (was_in_frame)
|
|
ImGui::EndFrame();
|
|
|
|
bool done = false;
|
|
bool result = true;
|
|
while (!done)
|
|
{
|
|
RunCallbacks();
|
|
PollAndUpdate();
|
|
if (m_fullscreen_ui_enabled)
|
|
FullscreenUI::SetImGuiNavInputs();
|
|
|
|
ImGui::NewFrame();
|
|
done = FullscreenUI::DrawConfirmWindow(message, &result);
|
|
ImGui::EndFrame();
|
|
m_display->Render();
|
|
}
|
|
|
|
if (was_in_frame)
|
|
ImGui::NewFrame();
|
|
|
|
return result;
|
|
}
|
|
|
|
void UWPHostInterface::RunLater(std::function<void()> callback)
|
|
{
|
|
std::unique_lock<std::mutex> lock(m_queued_callbacks_lock);
|
|
m_queued_callbacks.push_back(std::move(callback));
|
|
}
|
|
|
|
void UWPHostInterface::OnDisplayInvalidated()
|
|
{
|
|
}
|
|
|
|
void UWPHostInterface::OnSystemPerformanceCountersUpdated()
|
|
{
|
|
}
|
|
|
|
bool UWPHostInterface::IsFullscreen() const
|
|
{
|
|
return m_appview.IsFullScreenMode();
|
|
}
|
|
|
|
bool UWPHostInterface::SetFullscreen(bool enabled)
|
|
{
|
|
m_dispatcher.RunAsync(winrt::Windows::UI::Core::CoreDispatcherPriority::Normal, [this, enabled]() {
|
|
if (enabled)
|
|
m_appview.TryEnterFullScreenMode();
|
|
else
|
|
m_appview.ExitFullScreenMode();
|
|
});
|
|
|
|
return true;
|
|
}
|
|
|
|
void UWPHostInterface::RunCallbacks()
|
|
{
|
|
std::unique_lock<std::mutex> lock(m_queued_callbacks_lock);
|
|
|
|
while (!m_queued_callbacks.empty())
|
|
{
|
|
auto callback = std::move(m_queued_callbacks.front());
|
|
m_queued_callbacks.pop_front();
|
|
lock.unlock();
|
|
callback();
|
|
lock.lock();
|
|
}
|
|
}
|
|
|
|
void UWPHostInterface::SetWindow(const winrt::Windows::UI::Core::CoreWindow& window)
|
|
{
|
|
m_window = window;
|
|
m_dispatcher = m_window.Dispatcher();
|
|
|
|
window.Closed({this, &UWPHostInterface::OnClosed});
|
|
window.SizeChanged({this, &UWPHostInterface::OnSizeChanged});
|
|
window.KeyDown({this, &UWPHostInterface::OnKeyDown});
|
|
window.KeyUp({this, &UWPHostInterface::OnKeyUp});
|
|
window.CharacterReceived({this, &UWPHostInterface::OnCharacterReceived});
|
|
window.PointerPressed({this, &UWPHostInterface::OnPointerPressed});
|
|
window.PointerReleased({this, &UWPHostInterface::OnPointerPressed});
|
|
window.PointerMoved({this, &UWPHostInterface::OnPointerMoved});
|
|
window.PointerWheelChanged({this, &UWPHostInterface::OnPointerWheelChanged});
|
|
}
|
|
|
|
bool UWPHostInterface::SetDirectories()
|
|
{
|
|
const auto install_location = winrt::Windows::ApplicationModel::Package::Current().InstalledLocation();
|
|
m_program_directory = StringUtil::WideStringToUTF8String(install_location.Path());
|
|
if (m_program_directory.empty())
|
|
{
|
|
Log_ErrorPrintf("Failed to get install location");
|
|
return false;
|
|
}
|
|
|
|
Log_InfoPrintf("Program directory: %s", m_program_directory.c_str());
|
|
|
|
const auto local_location = winrt::Windows::Storage::ApplicationData::Current().LocalFolder();
|
|
m_user_directory = StringUtil::WideStringToUTF8String(local_location.Path());
|
|
if (m_user_directory.empty())
|
|
{
|
|
Log_ErrorPrintf("Failed to get user directory");
|
|
return false;
|
|
}
|
|
|
|
Log_InfoPrintf("User directory: %s", m_user_directory.c_str());
|
|
return true;
|
|
}
|
|
|
|
void UWPHostInterface::OnSuspending(const IInspectable&,
|
|
const winrt::Windows::ApplicationModel::SuspendingEventArgs& args)
|
|
{
|
|
if (IsEmulationThreadRunning())
|
|
{
|
|
RunLater([this]() {
|
|
if (ShouldSaveResumeState())
|
|
SaveResumeSaveState();
|
|
|
|
m_was_running_on_suspend.store(System::IsRunning());
|
|
PauseSystem(true);
|
|
m_suspend_sync_event.Signal();
|
|
});
|
|
|
|
m_suspend_sync_event.Wait();
|
|
}
|
|
}
|
|
|
|
void UWPHostInterface::OnResuming(const IInspectable&, const IInspectable&)
|
|
{
|
|
if (IsEmulationThreadRunning())
|
|
{
|
|
if (m_was_running_on_suspend.load())
|
|
RunLater([this]() { PauseSystem(false); });
|
|
}
|
|
else
|
|
{
|
|
RunLater([this]() {
|
|
if (ShouldSaveResumeState())
|
|
ResumeSystemFromMostRecentState();
|
|
});
|
|
}
|
|
}
|
|
|
|
void UWPHostInterface::OnClosed(const IInspectable&, const winrt::Windows::UI::Core::CoreWindowEventArgs& args)
|
|
{
|
|
if (IsEmulationThreadRunning())
|
|
{
|
|
m_shutdown_flag.store(true);
|
|
m_emulation_thread.join();
|
|
}
|
|
args.Handled(true);
|
|
}
|
|
|
|
void UWPHostInterface::OnSizeChanged(const IInspectable&,
|
|
const winrt::Windows::UI::Core::WindowSizeChangedEventArgs& args)
|
|
{
|
|
if (IsEmulationThreadRunning())
|
|
{
|
|
const auto size = args.Size();
|
|
const float width = size.Width;
|
|
const float height = size.Height;
|
|
RunLater([this, width, height]() {
|
|
if (!m_display)
|
|
return;
|
|
|
|
m_display->ResizeRenderWindow(static_cast<s32>(width * m_display->GetWindowScale()),
|
|
static_cast<s32>(height * m_display->GetWindowScale()));
|
|
OnHostDisplayResized();
|
|
});
|
|
}
|
|
|
|
args.Handled(true);
|
|
}
|
|
|
|
void UWPHostInterface::OnKeyDown(const IInspectable&, const winrt::Windows::UI::Core::KeyEventArgs& args)
|
|
{
|
|
const auto status = args.KeyStatus();
|
|
if (!status.WasKeyDown && !status.IsKeyReleased && IsEmulationThreadRunning())
|
|
{
|
|
const HostKeyCode code = static_cast<HostKeyCode>(args.VirtualKey());
|
|
RunLater([this, code]() {
|
|
ImGuiIO& io = ImGui::GetIO();
|
|
if (code < countof(io.KeysDown))
|
|
io.KeysDown[code] = true;
|
|
|
|
if (!io.WantCaptureKeyboard)
|
|
HandleHostKeyEvent(code, 0, true);
|
|
});
|
|
}
|
|
|
|
args.Handled(true);
|
|
}
|
|
|
|
void UWPHostInterface::OnKeyUp(const IInspectable&, const winrt::Windows::UI::Core::KeyEventArgs& args)
|
|
{
|
|
const auto status = args.KeyStatus();
|
|
if (status.WasKeyDown && status.IsKeyReleased && IsEmulationThreadRunning())
|
|
{
|
|
const HostKeyCode code = static_cast<HostKeyCode>(args.VirtualKey());
|
|
RunLater([this, code]() {
|
|
ImGuiIO& io = ImGui::GetIO();
|
|
if (code < countof(io.KeysDown))
|
|
io.KeysDown[code] = false;
|
|
|
|
if (!io.WantCaptureKeyboard)
|
|
HandleHostKeyEvent(code, 0, false);
|
|
});
|
|
}
|
|
|
|
args.Handled(true);
|
|
}
|
|
|
|
void UWPHostInterface::OnCharacterReceived(const IInspectable&,
|
|
const winrt::Windows::UI::Core::CharacterReceivedEventArgs& args)
|
|
{
|
|
if (IsEmulationThreadRunning())
|
|
{
|
|
const u32 code = args.KeyCode();
|
|
RunLater([this, code]() { ImGui::GetIO().AddInputCharacter(code); });
|
|
}
|
|
|
|
args.Handled(true);
|
|
}
|
|
|
|
void UWPHostInterface::OnPointerPressed(const IInspectable&, const winrt::Windows::UI::Core::PointerEventArgs& args)
|
|
{
|
|
const auto pointer = args.CurrentPoint();
|
|
if (pointer.PointerDevice().PointerDeviceType() == winrt::Windows::Devices::Input::PointerDeviceType::Mouse)
|
|
UpdateMouseButtonState(pointer);
|
|
|
|
args.Handled(true);
|
|
}
|
|
|
|
void UWPHostInterface::OnPointerReleased(const IInspectable&, const winrt::Windows::UI::Core::PointerEventArgs& args)
|
|
{
|
|
const auto pointer = args.CurrentPoint();
|
|
if (pointer.PointerDevice().PointerDeviceType() == winrt::Windows::Devices::Input::PointerDeviceType::Mouse)
|
|
UpdateMouseButtonState(pointer);
|
|
|
|
args.Handled(true);
|
|
}
|
|
|
|
void UWPHostInterface::OnPointerMoved(const IInspectable&, const winrt::Windows::UI::Core::PointerEventArgs& args)
|
|
{
|
|
const auto pointer = args.CurrentPoint();
|
|
if (pointer.PointerDevice().PointerDeviceType() == winrt::Windows::Devices::Input::PointerDeviceType::Mouse)
|
|
{
|
|
const auto pos = pointer.Position();
|
|
const float x = pos.X * m_display->GetWindowScale();
|
|
const float y = pos.Y * m_display->GetWindowScale();
|
|
|
|
if (IsEmulationThreadRunning())
|
|
{
|
|
RunLater([this, x, y]() {
|
|
m_display->SetMousePosition(static_cast<s32>(x), static_cast<s32>(y));
|
|
|
|
if (ImGui::GetCurrentContext())
|
|
{
|
|
ImGuiIO& io = ImGui::GetIO();
|
|
io.MousePos.x = x;
|
|
io.MousePos.y = y;
|
|
}
|
|
});
|
|
}
|
|
|
|
UpdateMouseButtonState(pointer);
|
|
}
|
|
|
|
args.Handled(true);
|
|
}
|
|
|
|
void UWPHostInterface::OnPointerWheelChanged(const IInspectable&,
|
|
const winrt::Windows::UI::Core::PointerEventArgs& args)
|
|
{
|
|
const auto pointer = args.CurrentPoint();
|
|
const auto properties = pointer.Properties();
|
|
const s32 delta = properties.MouseWheelDelta();
|
|
const bool horizontal = properties.IsHorizontalMouseWheel();
|
|
|
|
if (IsEmulationThreadRunning())
|
|
{
|
|
RunLater([this, delta, horizontal]() {
|
|
if (ImGui::GetCurrentContext())
|
|
{
|
|
ImGuiIO& io = ImGui::GetIO();
|
|
const float dw = static_cast<float>(std::clamp<s32>(delta, -1, 1));
|
|
if (horizontal)
|
|
io.MouseWheelH = dw;
|
|
else
|
|
io.MouseWheel = dw;
|
|
}
|
|
});
|
|
}
|
|
|
|
args.Handled(true);
|
|
}
|
|
|
|
void UWPHostInterface::UpdateMouseButtonState(const winrt::Windows::UI::Input::PointerPoint& point)
|
|
{
|
|
const auto properties = point.Properties();
|
|
const bool states[3] = {properties.IsLeftButtonPressed(), properties.IsRightButtonPressed(),
|
|
properties.IsMiddleButtonPressed()};
|
|
|
|
if (IsEmulationThreadRunning())
|
|
{
|
|
RunLater([this, states]() {
|
|
if (!ImGui::GetCurrentContext())
|
|
return;
|
|
|
|
ImGuiIO& io = ImGui::GetIO();
|
|
for (u32 i = 0; i < countof(states); i++)
|
|
{
|
|
if (io.MouseDown[i] == states[i])
|
|
continue;
|
|
|
|
io.MouseDown[i] = states[i];
|
|
HandleHostMouseEvent(static_cast<HostMouseButton>(i + 1u), states[i]);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
std::optional<CommonHostInterface::HostKeyCode> UWPHostInterface::GetHostKeyCode(const std::string_view key_code) const
|
|
{
|
|
for (const auto& it : s_key_map)
|
|
{
|
|
if (key_code.compare(it.second) == 0)
|
|
return static_cast<HostKeyCode>(it.first);
|
|
}
|
|
|
|
return std::nullopt;
|
|
}
|
|
|
|
const char* UWPHostInterface::GetKeyCodeName(int key_code)
|
|
{
|
|
const auto it = s_key_map.find(key_code);
|
|
return (it != s_key_map.end()) ? it->second : nullptr;
|
|
}
|
|
|
|
void UWPHostInterface::SetImGuiKeyMap()
|
|
{
|
|
using namespace winrt::Windows::System;
|
|
|
|
ImGuiIO& io = ImGui::GetIO();
|
|
io.KeyMap[ImGuiKey_Tab] = static_cast<int>(VirtualKey::Tab);
|
|
io.KeyMap[ImGuiKey_LeftArrow] = static_cast<int>(VirtualKey::Left);
|
|
io.KeyMap[ImGuiKey_RightArrow] = static_cast<int>(VirtualKey::Right);
|
|
io.KeyMap[ImGuiKey_UpArrow] = static_cast<int>(VirtualKey::Up);
|
|
io.KeyMap[ImGuiKey_DownArrow] = static_cast<int>(VirtualKey::Down);
|
|
io.KeyMap[ImGuiKey_PageUp] = static_cast<int>(VirtualKey::PageUp);
|
|
io.KeyMap[ImGuiKey_PageDown] = static_cast<int>(VirtualKey::PageDown);
|
|
io.KeyMap[ImGuiKey_Home] = static_cast<int>(VirtualKey::Home);
|
|
io.KeyMap[ImGuiKey_End] = static_cast<int>(VirtualKey::End);
|
|
io.KeyMap[ImGuiKey_Insert] = static_cast<int>(VirtualKey::Insert);
|
|
io.KeyMap[ImGuiKey_Delete] = static_cast<int>(VirtualKey::Delete);
|
|
io.KeyMap[ImGuiKey_Backspace] = static_cast<int>(VirtualKey::Back);
|
|
io.KeyMap[ImGuiKey_Space] = static_cast<int>(VirtualKey::Space);
|
|
io.KeyMap[ImGuiKey_Enter] = static_cast<int>(VirtualKey::Enter);
|
|
io.KeyMap[ImGuiKey_Escape] = static_cast<int>(VirtualKey::Escape);
|
|
io.KeyMap[ImGuiKey_A] = static_cast<int>(VirtualKey::A);
|
|
io.KeyMap[ImGuiKey_C] = static_cast<int>(VirtualKey::C);
|
|
io.KeyMap[ImGuiKey_V] = static_cast<int>(VirtualKey::V);
|
|
io.KeyMap[ImGuiKey_X] = static_cast<int>(VirtualKey::X);
|
|
io.KeyMap[ImGuiKey_Y] = static_cast<int>(VirtualKey::Y);
|
|
io.KeyMap[ImGuiKey_Z] = static_cast<int>(VirtualKey::Z);
|
|
}
|
|
|
|
void UWPHostInterface::SetDefaultSettingsForXbox(SettingsInterface& si)
|
|
{
|
|
si.SetStringValue("GPU", "Renderer", "D3D12");
|
|
|
|
si.SetBoolValue("Main", "SyncToHostRefreshRate", true);
|
|
si.SetBoolValue("Display", "VSync", true);
|
|
si.SetBoolValue("Display", "DisplayAllFrames", true);
|
|
si.SetFloatValue("Display", "MaxFPS", 60.0f);
|
|
|
|
// Set up an analog controller in port 1.
|
|
si.SetStringValue("Controller1", "Type", "AnalogController");
|
|
si.SetStringValue("Controller1", "ButtonUp", "Controller0/Button11");
|
|
si.SetStringValue("Controller1", "ButtonDown", "Controller0/Button12");
|
|
si.SetStringValue("Controller1", "ButtonLeft", "Controller0/Button13");
|
|
si.SetStringValue("Controller1", "ButtonRight", "Controller0/Button14");
|
|
si.SetStringValue("Controller1", "ButtonStart", "Controller0/Button6");
|
|
si.SetStringValue("Controller1", "ButtonTriangle", "Controller0/Button3");
|
|
si.SetStringValue("Controller1", "ButtonCross", "Controller0/Button0");
|
|
si.SetStringValue("Controller1", "ButtonCircle", "Controller0/Button1");
|
|
si.SetStringValue("Controller1", "ButtonSquare", "Controller0/Button2");
|
|
si.SetStringValue("Controller1", "ButtonL1", "Controller0/Button9");
|
|
si.SetStringValue("Controller1", "ButtonL2", "Controller0/+Axis4");
|
|
si.SetStringValue("Controller1", "ButtonR1", "Controller0/Button10");
|
|
si.SetStringValue("Controller1", "ButtonR2", "Controller0/+Axis5");
|
|
si.SetStringValue("Controller1", "ButtonL3", "Controller0/Button7");
|
|
si.SetStringValue("Controller1", "ButtonR3", "Controller0/Button8");
|
|
si.SetStringValue("Controller1", "AxisLeftX", "Controller0/Axis0");
|
|
si.SetStringValue("Controller1", "AxisLeftY", "Controller0/Axis1");
|
|
si.SetStringValue("Controller1", "AxisRightX", "Controller0/Axis2");
|
|
si.SetStringValue("Controller1", "AxisRightY", "Controller0/Axis3");
|
|
si.SetStringValue("Controller1", "Rumble", "Controller0");
|
|
si.SetStringValue("Controller1", "ForceAnalogOnReset", "true");
|
|
si.SetStringValue("Controller1", "AnalogDPadInDigitalMode", "true");
|
|
|
|
// Repurpose the select button to open the menu.
|
|
// Not ideal, but all we can do until we have chords.
|
|
si.SetStringValue("Hotkeys", "OpenQuickMenu", "Controller0/Button4");
|
|
}
|
|
|
|
int __stdcall wWinMain(HINSTANCE, HINSTANCE, PWSTR, int)
|
|
{
|
|
winrt::Windows::ApplicationModel::Core::CoreApplication::Run(winrt::make<UWPHostInterface>());
|
|
}
|