mirror of
https://github.com/RetroDECK/Duckstation.git
synced 2024-11-27 08:05:41 +00:00
NoGUI: Merge in old branch and use base NoGUI class for SDL
This commit is contained in:
parent
b09da307b5
commit
98bad30af8
|
@ -1,16 +1,35 @@
|
|||
add_executable(duckstation-nogui
|
||||
imgui_impl_sdl.cpp
|
||||
imgui_impl_sdl.h
|
||||
main.cpp
|
||||
sdl_host_interface.cpp
|
||||
sdl_host_interface.h
|
||||
sdl_key_names.h
|
||||
sdl_util.cpp
|
||||
sdl_util.h
|
||||
nogui_host_interface.cpp
|
||||
nogui_host_interface.h
|
||||
)
|
||||
|
||||
target_include_directories(duckstation-nogui PRIVATE ${SDL2_INCLUDE_DIRS})
|
||||
target_link_libraries(duckstation-nogui PRIVATE core common imgui glad frontend-common scmversion vulkan-loader ${SDL2_LIBRARIES})
|
||||
target_link_libraries(duckstation-nogui PRIVATE core common imgui glad frontend-common scmversion vulkan-loader)
|
||||
|
||||
if(USE_SDL2)
|
||||
target_sources(duckstation-nogui PRIVATE
|
||||
imgui_impl_sdl.cpp
|
||||
imgui_impl_sdl.h
|
||||
sdl_host_interface.cpp
|
||||
sdl_host_interface.h
|
||||
sdl_key_names.h
|
||||
)
|
||||
target_include_directories(duckstation-nogui PRIVATE ${SDL2_INCLUDE_DIRS})
|
||||
target_link_libraries(duckstation-nogui PRIVATE ${SDL2_LIBRARIES})
|
||||
endif()
|
||||
|
||||
if(USE_DRMKMS)
|
||||
find_package(LIBEVDEV REQUIRED)
|
||||
|
||||
target_sources(duckstation-nogui PRIVATE
|
||||
drm_host_interface.cpp
|
||||
drm_host_interface.h
|
||||
)
|
||||
target_compile_definitions(duckstation-nogui PRIVATE "-DUSE_DRMKMS=1")
|
||||
target_compile_definitions(duckstation-nogui PRIVATE "-DUSE_LIBEVDEV=1")
|
||||
target_include_directories(duckstation-nogui PRIVATE ${LIBEVDEV_INCLUDE_DIRS})
|
||||
target_link_libraries(duckstation-nogui PRIVATE ${LIBEVDEV_LIBRARIES})
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
target_sources(duckstation-nogui PRIVATE
|
||||
|
|
211
src/duckstation-nogui/drm_host_interface.cpp
Normal file
211
src/duckstation-nogui/drm_host_interface.cpp
Normal file
|
@ -0,0 +1,211 @@
|
|||
#include "drm_host_interface.h"
|
||||
#include "common/log.h"
|
||||
#include "common/string_util.h"
|
||||
#include "evdev_key_names.h"
|
||||
#include "imgui.h"
|
||||
#include <fcntl.h>
|
||||
#include <linux/input-event-codes.h>
|
||||
#include <signal.h>
|
||||
#include <unistd.h>
|
||||
Log_SetChannel(DRMHostInterface);
|
||||
|
||||
DRMHostInterface::DRMHostInterface() = default;
|
||||
|
||||
DRMHostInterface::~DRMHostInterface()
|
||||
{
|
||||
CloseEVDevFDs();
|
||||
}
|
||||
|
||||
std::unique_ptr<NoGUIHostInterface> DRMHostInterface::Create()
|
||||
{
|
||||
return std::make_unique<DRMHostInterface>();
|
||||
}
|
||||
|
||||
bool DRMHostInterface::Initialize()
|
||||
{
|
||||
if (!NoGUIHostInterface::Initialize())
|
||||
return false;
|
||||
|
||||
OpenEVDevFDs();
|
||||
|
||||
signal(SIGTERM, SIGTERMHandler);
|
||||
signal(SIGINT, SIGTERMHandler);
|
||||
signal(SIGQUIT, SIGTERMHandler);
|
||||
return true;
|
||||
}
|
||||
|
||||
void DRMHostInterface::Shutdown()
|
||||
{
|
||||
CloseEVDevFDs();
|
||||
NoGUIHostInterface::Shutdown();
|
||||
}
|
||||
|
||||
bool DRMHostInterface::IsFullscreen() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DRMHostInterface::SetFullscreen(bool enabled)
|
||||
{
|
||||
return enabled;
|
||||
}
|
||||
|
||||
void DRMHostInterface::FixIncompatibleSettings(bool display_osd_messages)
|
||||
{
|
||||
NoGUIHostInterface::FixIncompatibleSettings(display_osd_messages);
|
||||
|
||||
// Some things we definitely don't want.
|
||||
g_settings.confim_power_off = false;
|
||||
}
|
||||
|
||||
bool DRMHostInterface::CreatePlatformWindow()
|
||||
{
|
||||
Assert(!m_drm_display);
|
||||
m_drm_display = std::make_unique<DRMDisplay>();
|
||||
if (!m_drm_display->Initialize())
|
||||
{
|
||||
m_drm_display.reset();
|
||||
return false;
|
||||
}
|
||||
|
||||
SetImGuiKeyMap();
|
||||
return true;
|
||||
}
|
||||
|
||||
void DRMHostInterface::DestroyPlatformWindow()
|
||||
{
|
||||
m_drm_display.reset();
|
||||
}
|
||||
|
||||
std::optional<WindowInfo> DRMHostInterface::GetPlatformWindowInfo()
|
||||
{
|
||||
WindowInfo wi;
|
||||
wi.type = WindowInfo::Type::DRM;
|
||||
wi.display_connection = m_drm_display.get();
|
||||
wi.surface_width = m_drm_display->GetWidth();
|
||||
wi.surface_height = m_drm_display->GetHeight();
|
||||
wi.surface_format = WindowInfo::SurfaceFormat::Auto;
|
||||
return wi;
|
||||
}
|
||||
|
||||
void DRMHostInterface::PollAndUpdate()
|
||||
{
|
||||
PollEvDevKeyboards();
|
||||
|
||||
NoGUIHostInterface::PollAndUpdate();
|
||||
}
|
||||
|
||||
void DRMHostInterface::OpenEVDevFDs()
|
||||
{
|
||||
for (int i = 0; i < 1000; i++)
|
||||
{
|
||||
TinyString path;
|
||||
path.Format("/dev/input/event%d", i);
|
||||
|
||||
int fd = open(path, O_RDONLY | O_NONBLOCK);
|
||||
if (fd < 0)
|
||||
break;
|
||||
|
||||
struct libevdev* obj;
|
||||
if (libevdev_new_from_fd(fd, &obj) != 0)
|
||||
{
|
||||
Log_ErrorPrintf("libevdev_new_from_fd(%s) failed", path.GetCharArray());
|
||||
close(fd);
|
||||
continue;
|
||||
}
|
||||
|
||||
Log_DevPrintf("Input path: %s", path.GetCharArray());
|
||||
Log_DevPrintf("Input device name: \"%s\"", libevdev_get_name(obj));
|
||||
Log_DevPrintf("Input device ID: bus %#x vendor %#x product %#x", libevdev_get_id_bustype(obj),
|
||||
libevdev_get_id_vendor(obj), libevdev_get_id_product(obj));
|
||||
if (!libevdev_has_event_code(obj, EV_KEY, KEY_SPACE))
|
||||
{
|
||||
Log_DevPrintf("This device does not look like a keyboard");
|
||||
libevdev_free(obj);
|
||||
close(fd);
|
||||
continue;
|
||||
}
|
||||
|
||||
const int grab_res = libevdev_grab(obj, LIBEVDEV_GRAB);
|
||||
if (grab_res != 0)
|
||||
Log_WarningPrintf("Failed to grab '%s' (%s): %d", libevdev_get_name(obj), path.GetCharArray(), grab_res);
|
||||
|
||||
m_evdev_keyboards.push_back({obj, fd});
|
||||
}
|
||||
}
|
||||
|
||||
void DRMHostInterface::CloseEVDevFDs()
|
||||
{
|
||||
for (const EvDevKeyboard& kb : m_evdev_keyboards)
|
||||
{
|
||||
libevdev_grab(kb.obj, LIBEVDEV_UNGRAB);
|
||||
libevdev_free(kb.obj);
|
||||
close(kb.fd);
|
||||
}
|
||||
m_evdev_keyboards.clear();
|
||||
}
|
||||
|
||||
void DRMHostInterface::PollEvDevKeyboards()
|
||||
{
|
||||
for (const EvDevKeyboard& kb : m_evdev_keyboards)
|
||||
{
|
||||
struct input_event ev;
|
||||
while (libevdev_next_event(kb.obj, LIBEVDEV_READ_FLAG_NORMAL, &ev) == 0)
|
||||
{
|
||||
// auto-repeat
|
||||
if (ev.value == 2)
|
||||
continue;
|
||||
|
||||
const bool pressed = (ev.value == 1);
|
||||
const HostKeyCode code = static_cast<HostKeyCode>(ev.code);
|
||||
if (code >= 0 && code < countof(ImGuiIO::KeysDown))
|
||||
ImGui::GetIO().KeysDown[code] = pressed;
|
||||
|
||||
HandleHostKeyEvent(code, pressed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DRMHostInterface::SetImGuiKeyMap()
|
||||
{
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
|
||||
io.KeyMap[ImGuiKey_Tab] = KEY_TAB;
|
||||
io.KeyMap[ImGuiKey_LeftArrow] = KEY_LEFT;
|
||||
io.KeyMap[ImGuiKey_RightArrow] = KEY_RIGHT;
|
||||
io.KeyMap[ImGuiKey_UpArrow] = KEY_UP;
|
||||
io.KeyMap[ImGuiKey_DownArrow] = KEY_DOWN;
|
||||
io.KeyMap[ImGuiKey_PageUp] = KEY_PAGEUP;
|
||||
io.KeyMap[ImGuiKey_PageDown] = KEY_PAGEDOWN;
|
||||
io.KeyMap[ImGuiKey_Home] = KEY_HOME;
|
||||
io.KeyMap[ImGuiKey_End] = KEY_END;
|
||||
io.KeyMap[ImGuiKey_Insert] = KEY_INSERT;
|
||||
io.KeyMap[ImGuiKey_Delete] = KEY_DELETE;
|
||||
io.KeyMap[ImGuiKey_Backspace] = KEY_BACKSPACE;
|
||||
io.KeyMap[ImGuiKey_Space] = KEY_SPACE;
|
||||
io.KeyMap[ImGuiKey_Enter] = KEY_ENTER;
|
||||
io.KeyMap[ImGuiKey_Escape] = KEY_ESC;
|
||||
io.KeyMap[ImGuiKey_KeyPadEnter] = KEY_KPENTER;
|
||||
io.KeyMap[ImGuiKey_A] = KEY_A;
|
||||
io.KeyMap[ImGuiKey_C] = KEY_C;
|
||||
io.KeyMap[ImGuiKey_V] = KEY_V;
|
||||
io.KeyMap[ImGuiKey_X] = KEY_X;
|
||||
io.KeyMap[ImGuiKey_Y] = KEY_Y;
|
||||
io.KeyMap[ImGuiKey_Z] = KEY_Z;
|
||||
}
|
||||
|
||||
std::optional<DRMHostInterface::HostKeyCode> DRMHostInterface::GetHostKeyCode(const std::string_view key_code) const
|
||||
{
|
||||
std::optional<int> kc = EvDevKeyNames::GetKeyCodeForName(key_code);
|
||||
if (!kc.has_value())
|
||||
return std::nullopt;
|
||||
|
||||
return static_cast<HostKeyCode>(kc.value());
|
||||
}
|
||||
|
||||
void DRMHostInterface::SIGTERMHandler(int sig)
|
||||
{
|
||||
Log_InfoPrintf("Recieved SIGTERM");
|
||||
static_cast<DRMHostInterface*>(g_host_interface)->m_quit_request = true;
|
||||
signal(sig, SIG_DFL);
|
||||
}
|
50
src/duckstation-nogui/drm_host_interface.h
Normal file
50
src/duckstation-nogui/drm_host_interface.h
Normal file
|
@ -0,0 +1,50 @@
|
|||
#pragma once
|
||||
#include "common/drm_display.h"
|
||||
#include "nogui_host_interface.h"
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <libevdev/libevdev.h>
|
||||
|
||||
class DRMHostInterface final : public NoGUIHostInterface
|
||||
{
|
||||
public:
|
||||
DRMHostInterface();
|
||||
~DRMHostInterface();
|
||||
|
||||
bool Initialize();
|
||||
void Shutdown();
|
||||
|
||||
bool IsFullscreen() const override;
|
||||
bool SetFullscreen(bool enabled) override;
|
||||
|
||||
static std::unique_ptr<NoGUIHostInterface> Create();
|
||||
|
||||
protected:
|
||||
virtual void FixIncompatibleSettings(bool display_osd_messages) override;
|
||||
|
||||
bool CreatePlatformWindow() override;
|
||||
void DestroyPlatformWindow() override;
|
||||
std::optional<WindowInfo> GetPlatformWindowInfo() override;
|
||||
|
||||
std::optional<HostKeyCode> GetHostKeyCode(const std::string_view key_code) const override;
|
||||
|
||||
void PollAndUpdate() override;
|
||||
|
||||
private:
|
||||
static void SIGTERMHandler(int sig);
|
||||
|
||||
void OpenEVDevFDs();
|
||||
void CloseEVDevFDs();
|
||||
void PollEvDevKeyboards();
|
||||
void SetImGuiKeyMap();
|
||||
|
||||
std::unique_ptr<DRMDisplay> m_drm_display;
|
||||
|
||||
struct EvDevKeyboard
|
||||
{
|
||||
struct libevdev* obj;
|
||||
int fd;
|
||||
};
|
||||
|
||||
std::vector<EvDevKeyboard> m_evdev_keyboards;
|
||||
};
|
|
@ -68,17 +68,61 @@
|
|||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="drm_host_interface.cpp">
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='ReleaseLTCG|Win32'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='DebugFast|Win32'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='ReleaseLTCG|ARM64'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='DebugFast|ARM64'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='ReleaseLTCG|x64'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='DebugFast|x64'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<ClCompile Include="imgui_impl_sdl.cpp" />
|
||||
<ClCompile Include="nogui_host_interface.cpp" />
|
||||
<ClCompile Include="sdl_host_interface.cpp" />
|
||||
<ClCompile Include="main.cpp" />
|
||||
<ClCompile Include="sdl_util.cpp" />
|
||||
<ClCompile Include="win32_host_interface.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="drm_host_interface.h">
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='ReleaseLTCG|Win32'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='DebugFast|Win32'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='ReleaseLTCG|ARM64'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='DebugFast|ARM64'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='ReleaseLTCG|x64'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='DebugFast|x64'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
|
||||
</ClInclude>
|
||||
<ClInclude Include="evdev_key_names.h">
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='ReleaseLTCG|Win32'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='DebugFast|Win32'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='ReleaseLTCG|ARM64'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='DebugFast|ARM64'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='ReleaseLTCG|x64'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='DebugFast|x64'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
|
||||
</ClInclude>
|
||||
<ClInclude Include="imgui_impl_sdl.h" />
|
||||
<ClInclude Include="nogui_host_interface.h" />
|
||||
<ClInclude Include="resource.h" />
|
||||
<ClInclude Include="sdl_host_interface.h" />
|
||||
<ClInclude Include="sdl_key_names.h" />
|
||||
<ClInclude Include="sdl_util.h" />
|
||||
<ClInclude Include="win32_host_interface.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Manifest Include="duckstation-nogui.manifest" />
|
||||
|
@ -103,44 +147,44 @@
|
|||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<CharacterSet>NotSet</CharacterSet>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<CharacterSet>NotSet</CharacterSet>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<CharacterSet>NotSet</CharacterSet>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='DebugFast|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<CharacterSet>NotSet</CharacterSet>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='DebugFast|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<CharacterSet>NotSet</CharacterSet>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='DebugFast|ARM64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<CharacterSet>NotSet</CharacterSet>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>NotSet</CharacterSet>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
<SpectreMitigation>false</SpectreMitigation>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseLTCG|Win32'" Label="Configuration">
|
||||
|
@ -148,7 +192,7 @@
|
|||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>NotSet</CharacterSet>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
<SpectreMitigation>false</SpectreMitigation>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
|
@ -156,7 +200,7 @@
|
|||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>NotSet</CharacterSet>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
<SpectreMitigation>false</SpectreMitigation>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'" Label="Configuration">
|
||||
|
@ -164,7 +208,7 @@
|
|||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>NotSet</CharacterSet>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
<SpectreMitigation>false</SpectreMitigation>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseLTCG|x64'" Label="Configuration">
|
||||
|
@ -172,7 +216,7 @@
|
|||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>NotSet</CharacterSet>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
<SpectreMitigation>false</SpectreMitigation>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseLTCG|ARM64'" Label="Configuration">
|
||||
|
@ -180,7 +224,7 @@
|
|||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>NotSet</CharacterSet>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
<SpectreMitigation>false</SpectreMitigation>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
|
@ -311,7 +355,7 @@
|
|||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalDependencies>SDL2.lib;SDL2main.lib;dxgi.lib;d3d11.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalDependencies>SDL2.lib;dxgi.lib;d3d11.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
|
@ -333,7 +377,7 @@
|
|||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalDependencies>SDL2.lib;SDL2main.lib;dxgi.lib;d3d11.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalDependencies>SDL2.lib;dxgi.lib;d3d11.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">
|
||||
|
@ -355,7 +399,7 @@
|
|||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalDependencies>SDL2.lib;SDL2main.lib;dxgi.lib;d3d11.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalDependencies>SDL2.lib;dxgi.lib;d3d11.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='DebugFast|Win32'">
|
||||
|
@ -380,7 +424,7 @@
|
|||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalDependencies>SDL2.lib;SDL2main.lib;dxgi.lib;d3d11.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalDependencies>SDL2.lib;dxgi.lib;d3d11.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='DebugFast|x64'">
|
||||
|
@ -405,7 +449,7 @@
|
|||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalDependencies>SDL2.lib;SDL2main.lib;dxgi.lib;d3d11.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalDependencies>SDL2.lib;dxgi.lib;d3d11.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='DebugFast|ARM64'">
|
||||
|
@ -430,7 +474,7 @@
|
|||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalDependencies>SDL2.lib;SDL2main.lib;dxgi.lib;d3d11.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalDependencies>SDL2.lib;dxgi.lib;d3d11.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
|
@ -453,7 +497,7 @@
|
|||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<AdditionalDependencies>SDL2.lib;SDL2main.lib;dxgi.lib;d3d11.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalDependencies>SDL2.lib;dxgi.lib;d3d11.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<LinkTimeCodeGeneration>Default</LinkTimeCodeGeneration>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
|
@ -478,7 +522,7 @@
|
|||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<AdditionalDependencies>SDL2.lib;SDL2main.lib;dxgi.lib;d3d11.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalDependencies>SDL2.lib;dxgi.lib;d3d11.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<LinkTimeCodeGeneration>UseLinkTimeCodeGeneration</LinkTimeCodeGeneration>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
|
@ -502,7 +546,7 @@
|
|||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<AdditionalDependencies>SDL2.lib;SDL2main.lib;dxgi.lib;d3d11.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalDependencies>SDL2.lib;dxgi.lib;d3d11.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<LinkTimeCodeGeneration>Default</LinkTimeCodeGeneration>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
|
@ -526,7 +570,7 @@
|
|||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<AdditionalDependencies>SDL2.lib;SDL2main.lib;dxgi.lib;d3d11.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalDependencies>SDL2.lib;dxgi.lib;d3d11.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<LinkTimeCodeGeneration>Default</LinkTimeCodeGeneration>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
|
@ -551,7 +595,7 @@
|
|||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<AdditionalDependencies>SDL2.lib;SDL2main.lib;dxgi.lib;d3d11.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalDependencies>SDL2.lib;dxgi.lib;d3d11.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<LinkTimeCodeGeneration>UseLinkTimeCodeGeneration</LinkTimeCodeGeneration>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
|
@ -576,11 +620,11 @@
|
|||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<AdditionalDependencies>SDL2.lib;SDL2main.lib;dxgi.lib;d3d11.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalDependencies>SDL2.lib;dxgi.lib;d3d11.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<LinkTimeCodeGeneration>UseLinkTimeCodeGeneration</LinkTimeCodeGeneration>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
</Project>
|
|
@ -4,22 +4,27 @@
|
|||
<ClCompile Include="main.cpp" />
|
||||
<ClCompile Include="sdl_host_interface.cpp" />
|
||||
<ClCompile Include="imgui_impl_sdl.cpp" />
|
||||
<ClCompile Include="sdl_util.cpp" />
|
||||
<ClCompile Include="nogui_host_interface.cpp" />
|
||||
<ClCompile Include="win32_host_interface.cpp" />
|
||||
<ClCompile Include="drm_host_interface.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="sdl_host_interface.h" />
|
||||
<ClInclude Include="imgui_impl_sdl.h" />
|
||||
<ClInclude Include="sdl_key_names.h" />
|
||||
<ClInclude Include="resource.h" />
|
||||
<ClInclude Include="sdl_util.h" />
|
||||
<ClInclude Include="nogui_host_interface.h" />
|
||||
<ClInclude Include="win32_host_interface.h" />
|
||||
<ClInclude Include="drm_host_interface.h" />
|
||||
<ClInclude Include="evdev_key_names.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Manifest Include="duckstation-sdl.manifest" />
|
||||
<Manifest Include="duckstation-nogui.manifest" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Image Include="duckstation-sdl.ico" />
|
||||
<Image Include="duckstation-nogui.ico" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="duckstation-sdl.rc" />
|
||||
<ResourceCompile Include="duckstation-nogui.rc" />
|
||||
</ItemGroup>
|
||||
</Project>
|
278
src/duckstation-nogui/evdev_key_names.h
Normal file
278
src/duckstation-nogui/evdev_key_names.h
Normal file
|
@ -0,0 +1,278 @@
|
|||
#pragma once
|
||||
#include "common/string.h"
|
||||
#include "common/types.h"
|
||||
#include <array>
|
||||
#include <cstring>
|
||||
#include <linux/input-event-codes.h>
|
||||
#include <map>
|
||||
#include <optional>
|
||||
#include <string_view>
|
||||
|
||||
namespace EvDevKeyNames {
|
||||
|
||||
static const std::map<int, const char*> s_evdev_key_names = {{KEY_ESC, "Escape"},
|
||||
{KEY_1, "1"},
|
||||
{KEY_2, "2"},
|
||||
{KEY_3, "3"},
|
||||
{KEY_4, "4"},
|
||||
{KEY_5, "5"},
|
||||
{KEY_6, "6"},
|
||||
{KEY_7, "7"},
|
||||
{KEY_8, "8"},
|
||||
{KEY_9, "9"},
|
||||
{KEY_0, "0"},
|
||||
{KEY_MINUS, "Minus"},
|
||||
{KEY_EQUAL, "Equal"},
|
||||
{KEY_BACKSPACE, "Backspace"},
|
||||
{KEY_TAB, "Tab"},
|
||||
{KEY_Q, "Q"},
|
||||
{KEY_W, "W"},
|
||||
{KEY_E, "E"},
|
||||
{KEY_R, "R"},
|
||||
{KEY_T, "T"},
|
||||
{KEY_Y, "Y"},
|
||||
{KEY_U, "U"},
|
||||
{KEY_I, "I"},
|
||||
{KEY_O, "O"},
|
||||
{KEY_P, "P"},
|
||||
{KEY_LEFTBRACE, "Leftbrace"},
|
||||
{KEY_RIGHTBRACE, "Rightbrace"},
|
||||
{KEY_ENTER, "Return"},
|
||||
{KEY_LEFTCTRL, "Leftctrl"},
|
||||
{KEY_A, "A"},
|
||||
{KEY_S, "S"},
|
||||
{KEY_D, "D"},
|
||||
{KEY_F, "F"},
|
||||
{KEY_G, "G"},
|
||||
{KEY_H, "H"},
|
||||
{KEY_J, "J"},
|
||||
{KEY_K, "K"},
|
||||
{KEY_L, "L"},
|
||||
{KEY_SEMICOLON, "Semicolon"},
|
||||
{KEY_APOSTROPHE, "Apostrophe"},
|
||||
{KEY_GRAVE, "Grave"},
|
||||
{KEY_LEFTSHIFT, "Leftshift"},
|
||||
{KEY_BACKSLASH, "Backslash"},
|
||||
{KEY_Z, "Z"},
|
||||
{KEY_X, "X"},
|
||||
{KEY_C, "C"},
|
||||
{KEY_V, "V"},
|
||||
{KEY_B, "B"},
|
||||
{KEY_N, "N"},
|
||||
{KEY_M, "M"},
|
||||
{KEY_COMMA, "Comma"},
|
||||
{KEY_DOT, "Dot"},
|
||||
{KEY_SLASH, "Slash"},
|
||||
{KEY_RIGHTSHIFT, "Rightshift"},
|
||||
{KEY_KPASTERISK, "Kpasterisk"},
|
||||
{KEY_LEFTALT, "Leftalt"},
|
||||
{KEY_SPACE, "Space"},
|
||||
{KEY_CAPSLOCK, "Capslock"},
|
||||
{KEY_F1, "F1"},
|
||||
{KEY_F2, "F2"},
|
||||
{KEY_F3, "F3"},
|
||||
{KEY_F4, "F4"},
|
||||
{KEY_F5, "F5"},
|
||||
{KEY_F6, "F6"},
|
||||
{KEY_F7, "F7"},
|
||||
{KEY_F8, "F8"},
|
||||
{KEY_F9, "F9"},
|
||||
{KEY_F10, "F10"},
|
||||
{KEY_NUMLOCK, "Numlock"},
|
||||
{KEY_SCROLLLOCK, "Scrolllock"},
|
||||
{KEY_KP7, "Kp7"},
|
||||
{KEY_KP8, "Kp8"},
|
||||
{KEY_KP9, "Kp9"},
|
||||
{KEY_KPMINUS, "Kpminus"},
|
||||
{KEY_KP4, "Kp4"},
|
||||
{KEY_KP5, "Kp5"},
|
||||
{KEY_KP6, "Kp6"},
|
||||
{KEY_KPPLUS, "Kpplus"},
|
||||
{KEY_KP1, "Kp1"},
|
||||
{KEY_KP2, "Kp2"},
|
||||
{KEY_KP3, "Kp3"},
|
||||
{KEY_KP0, "Kp0"},
|
||||
{KEY_KPDOT, "Kpdot"},
|
||||
{KEY_ZENKAKUHANKAKU, "Zenkakuhankaku"},
|
||||
{KEY_102ND, "102nd"},
|
||||
{KEY_F11, "F11"},
|
||||
{KEY_F12, "F12"},
|
||||
{KEY_RO, "Ro"},
|
||||
{KEY_KATAKANA, "Katakana"},
|
||||
{KEY_HIRAGANA, "Hiragana"},
|
||||
{KEY_HENKAN, "Henkan"},
|
||||
{KEY_KATAKANAHIRAGANA, "Katakanahiragana"},
|
||||
{KEY_MUHENKAN, "Muhenkan"},
|
||||
{KEY_KPJPCOMMA, "Kpjpcomma"},
|
||||
{KEY_KPENTER, "Kpenter"},
|
||||
{KEY_RIGHTCTRL, "Rightctrl"},
|
||||
{KEY_KPSLASH, "Kpslash"},
|
||||
{KEY_SYSRQ, "Sysrq"},
|
||||
{KEY_RIGHTALT, "RightAlt"},
|
||||
{KEY_LINEFEED, "Linefeed"},
|
||||
{KEY_HOME, "Home"},
|
||||
{KEY_UP, "Up"},
|
||||
{KEY_PAGEUP, "PageUp"},
|
||||
{KEY_LEFT, "Left"},
|
||||
{KEY_RIGHT, "Right"},
|
||||
{KEY_END, "End"},
|
||||
{KEY_DOWN, "Down"},
|
||||
{KEY_PAGEDOWN, "PageDown"},
|
||||
{KEY_INSERT, "Insert"},
|
||||
{KEY_DELETE, "Delete"},
|
||||
{KEY_MACRO, "Macro"},
|
||||
{KEY_MUTE, "Mute"},
|
||||
{KEY_VOLUMEDOWN, "VolumeDown"},
|
||||
{KEY_VOLUMEUP, "VolumeUp"},
|
||||
{KEY_POWER, "Power"},
|
||||
{KEY_KPEQUAL, "Kpequal"},
|
||||
{KEY_KPPLUSMINUS, "Kpplusminus"},
|
||||
{KEY_PAUSE, "Pause"},
|
||||
{KEY_SCALE, "Scale"},
|
||||
{KEY_KPCOMMA, "Kpcomma"},
|
||||
{KEY_HANGEUL, "Hangeul"},
|
||||
{KEY_HANGUEL, "Hanguel"},
|
||||
{KEY_HANJA, "Hanja"},
|
||||
{KEY_YEN, "Yen"},
|
||||
{KEY_LEFTMETA, "Leftmeta"},
|
||||
{KEY_RIGHTMETA, "Rightmeta"},
|
||||
{KEY_COMPOSE, "Compose"},
|
||||
{KEY_STOP, "Stop"},
|
||||
{KEY_AGAIN, "Again"},
|
||||
{KEY_PROPS, "Props"},
|
||||
{KEY_UNDO, "Undo"},
|
||||
{KEY_FRONT, "Front"},
|
||||
{KEY_COPY, "Copy"},
|
||||
{KEY_OPEN, "Open"},
|
||||
{KEY_PASTE, "Paste"},
|
||||
{KEY_FIND, "Find"},
|
||||
{KEY_CUT, "Cut"},
|
||||
{KEY_HELP, "Help"},
|
||||
{KEY_MENU, "Menu"},
|
||||
{KEY_CALC, "Calc"},
|
||||
{KEY_SETUP, "Setup"},
|
||||
{KEY_SLEEP, "Sleep"},
|
||||
{KEY_WAKEUP, "Wakeup"},
|
||||
{KEY_FILE, "File"},
|
||||
{KEY_SENDFILE, "Sendfile"},
|
||||
{KEY_DELETEFILE, "Deletefile"},
|
||||
{KEY_XFER, "Xfer"},
|
||||
{KEY_PROG1, "Prog1"},
|
||||
{KEY_PROG2, "Prog2"},
|
||||
{KEY_WWW, "Www"},
|
||||
{KEY_MSDOS, "Msdos"},
|
||||
{KEY_COFFEE, "Coffee"},
|
||||
{KEY_SCREENLOCK, "Screenlock"},
|
||||
{KEY_ROTATE_DISPLAY, "Rotate_display"},
|
||||
{KEY_DIRECTION, "Direction"},
|
||||
{KEY_CYCLEWINDOWS, "Cyclewindows"},
|
||||
{KEY_MAIL, "Mail"},
|
||||
{KEY_BOOKMARKS, "Bookmarks"},
|
||||
{KEY_COMPUTER, "Computer"},
|
||||
{KEY_BACK, "Back"},
|
||||
{KEY_FORWARD, "Forward"},
|
||||
{KEY_CLOSECD, "Closecd"},
|
||||
{KEY_EJECTCD, "Ejectcd"},
|
||||
{KEY_EJECTCLOSECD, "Ejectclosecd"},
|
||||
{KEY_NEXTSONG, "Nextsong"},
|
||||
{KEY_PLAYPAUSE, "Playpause"},
|
||||
{KEY_PREVIOUSSONG, "Previoussong"},
|
||||
{KEY_STOPCD, "Stopcd"},
|
||||
{KEY_RECORD, "Record"},
|
||||
{KEY_REWIND, "Rewind"},
|
||||
{KEY_PHONE, "Phone"},
|
||||
{KEY_ISO, "Iso"},
|
||||
{KEY_CONFIG, "Config"},
|
||||
{KEY_HOMEPAGE, "Homepage"},
|
||||
{KEY_REFRESH, "Refresh"},
|
||||
{KEY_EXIT, "Exit"},
|
||||
{KEY_MOVE, "Move"},
|
||||
{KEY_EDIT, "Edit"},
|
||||
{KEY_SCROLLUP, "Scrollup"},
|
||||
{KEY_SCROLLDOWN, "Scrolldown"},
|
||||
{KEY_KPLEFTPAREN, "Kpleftparen"},
|
||||
{KEY_KPRIGHTPAREN, "Kprightparen"},
|
||||
{KEY_NEW, "New"},
|
||||
{KEY_REDO, "Redo"},
|
||||
{KEY_F13, "F13"},
|
||||
{KEY_F14, "F14"},
|
||||
{KEY_F15, "F15"},
|
||||
{KEY_F16, "F16"},
|
||||
{KEY_F17, "F17"},
|
||||
{KEY_F18, "F18"},
|
||||
{KEY_F19, "F19"},
|
||||
{KEY_F20, "F20"},
|
||||
{KEY_F21, "F21"},
|
||||
{KEY_F22, "F22"},
|
||||
{KEY_F23, "F23"},
|
||||
{KEY_F24, "F24"},
|
||||
{KEY_PLAYCD, "Playcd"},
|
||||
{KEY_PAUSECD, "Pausecd"},
|
||||
{KEY_PROG3, "Prog3"},
|
||||
{KEY_PROG4, "Prog4"},
|
||||
{KEY_DASHBOARD, "Dashboard"},
|
||||
{KEY_SUSPEND, "Suspend"},
|
||||
{KEY_CLOSE, "Close"},
|
||||
{KEY_PLAY, "Play"},
|
||||
{KEY_FASTFORWARD, "Fastforward"},
|
||||
{KEY_BASSBOOST, "Bassboost"},
|
||||
{KEY_PRINT, "Print"},
|
||||
{KEY_HP, "Hp"},
|
||||
{KEY_CAMERA, "Camera"},
|
||||
{KEY_SOUND, "Sound"},
|
||||
{KEY_QUESTION, "Question"},
|
||||
{KEY_EMAIL, "Email"},
|
||||
{KEY_CHAT, "Chat"},
|
||||
{KEY_SEARCH, "Search"},
|
||||
{KEY_CONNECT, "Connect"},
|
||||
{KEY_FINANCE, "Finance"},
|
||||
{KEY_SPORT, "Sport"},
|
||||
{KEY_SHOP, "Shop"},
|
||||
{KEY_ALTERASE, "Alterase"},
|
||||
{KEY_CANCEL, "Cancel"},
|
||||
{KEY_BRIGHTNESSDOWN, "Brightnessdown"},
|
||||
{KEY_BRIGHTNESSUP, "Brightnessup"},
|
||||
{KEY_MEDIA, "Media"},
|
||||
{KEY_SWITCHVIDEOMODE, "Switchvideomode"},
|
||||
{KEY_KBDILLUMTOGGLE, "Kbdillumtoggle"},
|
||||
{KEY_KBDILLUMDOWN, "Kbdillumdown"},
|
||||
{KEY_KBDILLUMUP, "Kbdillumup"},
|
||||
{KEY_SEND, "Send"},
|
||||
{KEY_REPLY, "Reply"},
|
||||
{KEY_FORWARDMAIL, "Forwardmail"},
|
||||
{KEY_SAVE, "Save"},
|
||||
{KEY_DOCUMENTS, "Documents"},
|
||||
{KEY_BATTERY, "Battery"},
|
||||
{KEY_BLUETOOTH, "Bluetooth"},
|
||||
{KEY_WLAN, "Wlan"},
|
||||
{KEY_UWB, "Uwb"},
|
||||
{KEY_UNKNOWN, "Unknown"},
|
||||
{KEY_VIDEO_NEXT, "Video_next"},
|
||||
{KEY_VIDEO_PREV, "Video_prev"},
|
||||
{KEY_BRIGHTNESS_CYCLE, "Brightness_cycle"},
|
||||
{KEY_BRIGHTNESS_AUTO, "Brightness_auto"},
|
||||
{KEY_BRIGHTNESS_ZERO, "Brightness_zero"},
|
||||
{KEY_DISPLAY_OFF, "Display_off"},
|
||||
{KEY_WWAN, "Wwan"},
|
||||
{KEY_WIMAX, "Wimax"},
|
||||
{KEY_RFKILL, "Rfkill"},
|
||||
{KEY_MICMUTE, "Micmute"}};
|
||||
|
||||
const char* GetKeyName(int key)
|
||||
{
|
||||
const auto it = s_evdev_key_names.find(key);
|
||||
return it == s_evdev_key_names.end() ? nullptr : it->second;
|
||||
}
|
||||
|
||||
std::optional<int> GetKeyCodeForName(const std::string_view key_name)
|
||||
{
|
||||
for (const auto& it : s_evdev_key_names)
|
||||
{
|
||||
if (key_name == it.second)
|
||||
return it.first;
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
} // namespace EvDevKeyNames
|
|
@ -131,7 +131,7 @@ bool ImGui_ImplSDL2_ProcessEvent(const SDL_Event* event)
|
|||
return false;
|
||||
}
|
||||
|
||||
bool ImGui_ImplSDL2_Init(SDL_Window* window)
|
||||
void ImGui_ImplSDL2_Init(SDL_Window* window)
|
||||
{
|
||||
g_Window = window;
|
||||
|
||||
|
@ -176,7 +176,6 @@ bool ImGui_ImplSDL2_Init(SDL_Window* window)
|
|||
g_MouseCursors[ImGuiMouseCursor_ResizeNESW] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZENESW);
|
||||
g_MouseCursors[ImGuiMouseCursor_ResizeNWSE] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZENWSE);
|
||||
g_MouseCursors[ImGuiMouseCursor_Hand] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_HAND);
|
||||
return true;
|
||||
}
|
||||
|
||||
void ImGui_ImplSDL2_Shutdown()
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
struct SDL_Window;
|
||||
typedef union SDL_Event SDL_Event;
|
||||
|
||||
IMGUI_IMPL_API bool ImGui_ImplSDL2_Init(SDL_Window* window);
|
||||
IMGUI_IMPL_API void ImGui_ImplSDL2_Init(SDL_Window* window);
|
||||
IMGUI_IMPL_API void ImGui_ImplSDL2_Shutdown();
|
||||
IMGUI_IMPL_API void ImGui_ImplSDL2_NewFrame();
|
||||
IMGUI_IMPL_API bool ImGui_ImplSDL2_ProcessEvent(const SDL_Event* event);
|
||||
|
|
|
@ -1,48 +1,129 @@
|
|||
#include "common/assert.h"
|
||||
#include "common/file_system.h"
|
||||
#include "common/log.h"
|
||||
#include "common/string_util.h"
|
||||
#include "core/system.h"
|
||||
#include "frontend-common/sdl_initializer.h"
|
||||
#include "sdl_host_interface.h"
|
||||
#include <SDL.h>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <vector>
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
#ifdef USE_DRMKMS
|
||||
#include "drm_host_interface.h"
|
||||
#endif
|
||||
|
||||
#ifdef WITH_SDL2
|
||||
#include "sdl_host_interface.h"
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
#include "common/windows_headers.h"
|
||||
#include "win32_host_interface.h"
|
||||
#include <shellapi.h>
|
||||
#endif
|
||||
|
||||
static std::unique_ptr<NoGUIHostInterface> CreateHostInterface(const char* platform)
|
||||
{
|
||||
FrontendCommon::EnsureSDLInitialized();
|
||||
std::unique_ptr<NoGUIHostInterface> host_interface;
|
||||
|
||||
std::unique_ptr<SDLHostInterface> host_interface = SDLHostInterface::Create();
|
||||
std::unique_ptr<SystemBootParameters> boot_params;
|
||||
if (!host_interface->ParseCommandLineParameters(argc, argv, &boot_params))
|
||||
{
|
||||
SDL_Quit();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
#ifdef USE_DRMKMS
|
||||
// TODO: We should detect if we have a display here...
|
||||
if (!host_interface && (!platform || StringUtil::Strcasecmp(platform, "drm") == 0))
|
||||
host_interface = DRMHostInterface::Create();
|
||||
#endif
|
||||
|
||||
#ifdef WITH_SDL2
|
||||
if (!host_interface && (!platform || StringUtil::Strcasecmp(platform, "sdl") == 0))
|
||||
host_interface = SDLHostInterface::Create();
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
if (!host_interface && (!platform || StringUtil::Strcasecmp(platform, "win32") == 0))
|
||||
host_interface = Win32HostInterface::Create();
|
||||
#endif
|
||||
|
||||
return host_interface;
|
||||
}
|
||||
|
||||
static int Run(std::unique_ptr<NoGUIHostInterface> host_interface, std::unique_ptr<SystemBootParameters> boot_params)
|
||||
{
|
||||
if (!host_interface->Initialize())
|
||||
{
|
||||
host_interface->Shutdown();
|
||||
SDL_Quit();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (boot_params)
|
||||
{
|
||||
if (!host_interface->BootSystem(*boot_params) && host_interface->InBatchMode())
|
||||
{
|
||||
host_interface->Shutdown();
|
||||
host_interface.reset();
|
||||
SDL_Quit();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
host_interface->BootSystem(*boot_params);
|
||||
|
||||
boot_params.reset();
|
||||
int result;
|
||||
if (System::IsValid() || !host_interface->InBatchMode())
|
||||
{
|
||||
host_interface->Run();
|
||||
result = EXIT_SUCCESS;
|
||||
}
|
||||
else
|
||||
{
|
||||
host_interface->ReportError("No file specified, and we're in batch mode. Exiting.");
|
||||
result = EXIT_FAILURE;
|
||||
}
|
||||
|
||||
host_interface->Run();
|
||||
host_interface->Shutdown();
|
||||
host_interface.reset();
|
||||
|
||||
SDL_Quit();
|
||||
return EXIT_SUCCESS;
|
||||
return result;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
int wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nShowCmd)
|
||||
{
|
||||
std::unique_ptr<NoGUIHostInterface> host_interface = CreateHostInterface(nullptr);
|
||||
std::unique_ptr<SystemBootParameters> boot_params;
|
||||
|
||||
{
|
||||
std::vector<std::string> argc_strings;
|
||||
argc_strings.reserve(1);
|
||||
|
||||
// CommandLineToArgvW() only adds the program path if the command line is empty?!
|
||||
argc_strings.push_back(FileSystem::GetProgramPath());
|
||||
|
||||
if (std::wcslen(lpCmdLine) > 0)
|
||||
{
|
||||
int argc;
|
||||
LPWSTR* argv_wide = CommandLineToArgvW(lpCmdLine, &argc);
|
||||
if (argv_wide)
|
||||
{
|
||||
for (int i = 0; i < argc; i++)
|
||||
argc_strings.push_back(StringUtil::WideStringToUTF8String(argv_wide[i]));
|
||||
|
||||
LocalFree(argv_wide);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<char*> argc_pointers;
|
||||
argc_pointers.reserve(argc_strings.size());
|
||||
for (std::string& arg : argc_strings)
|
||||
argc_pointers.push_back(arg.data());
|
||||
|
||||
if (!host_interface->ParseCommandLineParameters(static_cast<int>(argc_pointers.size()), argc_pointers.data(),
|
||||
&boot_params))
|
||||
{
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
return Run(std::move(host_interface), std::move(boot_params));
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
std::unique_ptr<NoGUIHostInterface> host_interface = CreateHostInterface(nullptr);
|
||||
std::unique_ptr<SystemBootParameters> boot_params;
|
||||
if (!host_interface->ParseCommandLineParameters(argc, argv, &boot_params))
|
||||
return EXIT_FAILURE;
|
||||
|
||||
return Run(std::move(host_interface), std::move(boot_params));
|
||||
}
|
||||
|
||||
#endif
|
410
src/duckstation-nogui/nogui_host_interface.cpp
Normal file
410
src/duckstation-nogui/nogui_host_interface.cpp
Normal file
|
@ -0,0 +1,410 @@
|
|||
#include "nogui_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/fullscreen_ui.h"
|
||||
#include "frontend-common/icon.h"
|
||||
#include "frontend-common/imgui_fullscreen.h"
|
||||
#include "frontend-common/imgui_styles.h"
|
||||
#include "frontend-common/ini_settings_interface.h"
|
||||
#include "frontend-common/opengl_host_display.h"
|
||||
#include "frontend-common/vulkan_host_display.h"
|
||||
#include <cinttypes>
|
||||
#include <cmath>
|
||||
#include <imgui.h>
|
||||
#include <imgui_stdlib.h>
|
||||
Log_SetChannel(NoGUIHostInterface);
|
||||
|
||||
#ifdef WIN32
|
||||
#include "frontend-common/d3d11_host_display.h"
|
||||
#endif
|
||||
|
||||
NoGUIHostInterface::NoGUIHostInterface() = default;
|
||||
|
||||
NoGUIHostInterface::~NoGUIHostInterface() = default;
|
||||
|
||||
const char* NoGUIHostInterface::GetFrontendName() const
|
||||
{
|
||||
return "DuckStation NoGUI Frontend";
|
||||
}
|
||||
|
||||
bool NoGUIHostInterface::Initialize()
|
||||
{
|
||||
// TODO: Make command line.
|
||||
m_fullscreen_ui_enabled = true;
|
||||
|
||||
// we're always in batch mode for now
|
||||
m_command_line_flags.batch_mode = !m_fullscreen_ui_enabled;
|
||||
|
||||
if (!CommonHostInterface::Initialize())
|
||||
return false;
|
||||
|
||||
CreateImGuiContext();
|
||||
|
||||
if (!CreatePlatformWindow())
|
||||
{
|
||||
Log_ErrorPrintf("Failed to create platform window");
|
||||
ImGui::DestroyContext();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!CreateDisplay())
|
||||
{
|
||||
Log_ErrorPrintf("Failed to create host display");
|
||||
DestroyPlatformWindow();
|
||||
ImGui::DestroyContext();
|
||||
return false;
|
||||
}
|
||||
|
||||
// process events to pick up controllers before updating input map
|
||||
PollAndUpdate();
|
||||
UpdateInputMap();
|
||||
return true;
|
||||
}
|
||||
|
||||
void NoGUIHostInterface::Shutdown()
|
||||
{
|
||||
DestroySystem();
|
||||
|
||||
CommonHostInterface::Shutdown();
|
||||
|
||||
if (m_display)
|
||||
{
|
||||
DestroyDisplay();
|
||||
ImGui::DestroyContext();
|
||||
}
|
||||
|
||||
DestroyPlatformWindow();
|
||||
}
|
||||
|
||||
std::string NoGUIHostInterface::GetStringSettingValue(const char* section, const char* key,
|
||||
const char* default_value /*= ""*/)
|
||||
{
|
||||
return m_settings_interface->GetStringValue(section, key, default_value);
|
||||
}
|
||||
|
||||
bool NoGUIHostInterface::GetBoolSettingValue(const char* section, const char* key, bool default_value /* = false */)
|
||||
{
|
||||
return m_settings_interface->GetBoolValue(section, key, default_value);
|
||||
}
|
||||
|
||||
int NoGUIHostInterface::GetIntSettingValue(const char* section, const char* key, int default_value /* = 0 */)
|
||||
{
|
||||
return m_settings_interface->GetIntValue(section, key, default_value);
|
||||
}
|
||||
|
||||
float NoGUIHostInterface::GetFloatSettingValue(const char* section, const char* key, float default_value /* = 0.0f */)
|
||||
{
|
||||
return m_settings_interface->GetFloatValue(section, key, default_value);
|
||||
}
|
||||
|
||||
void NoGUIHostInterface::LoadSettings()
|
||||
{
|
||||
m_settings_interface = std::make_unique<INISettingsInterface>(GetSettingsFileName());
|
||||
CommonHostInterface::LoadSettings(*m_settings_interface.get());
|
||||
CommonHostInterface::FixIncompatibleSettings(false);
|
||||
}
|
||||
|
||||
void NoGUIHostInterface::UpdateInputMap()
|
||||
{
|
||||
CommonHostInterface::UpdateInputMap(*m_settings_interface.get());
|
||||
}
|
||||
|
||||
void NoGUIHostInterface::ApplySettings(bool display_osd_messages)
|
||||
{
|
||||
Settings old_settings(std::move(g_settings));
|
||||
CommonHostInterface::LoadSettings(*m_settings_interface.get());
|
||||
CommonHostInterface::ApplyGameSettings(display_osd_messages);
|
||||
CommonHostInterface::FixIncompatibleSettings(display_osd_messages);
|
||||
CheckForSettingsChanges(old_settings);
|
||||
}
|
||||
|
||||
void NoGUIHostInterface::CreateImGuiContext()
|
||||
{
|
||||
ImGui::CreateContext();
|
||||
ImGui::GetIO().IniFilename = nullptr;
|
||||
ImGui::GetIO().ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard | ImGuiConfigFlags_NavEnableGamepad;
|
||||
}
|
||||
|
||||
void NoGUIHostInterface::OnPlatformWindowResized(u32 new_width, u32 new_height, float new_scale)
|
||||
{
|
||||
if (new_scale != ImGui::GetIO().DisplayFramebufferScale.x)
|
||||
{
|
||||
ImGui::GetIO().DisplayFramebufferScale = ImVec2(new_scale, new_scale);
|
||||
ImGui::GetStyle() = ImGuiStyle();
|
||||
ImGui::StyleColorsDarker();
|
||||
ImGui::GetStyle().ScaleAllSizes(new_scale);
|
||||
}
|
||||
|
||||
if (ImGuiFullscreen::UpdateLayoutScale())
|
||||
{
|
||||
if (ImGuiFullscreen::UpdateFonts())
|
||||
{
|
||||
if (!m_display->UpdateImGuiFontTexture())
|
||||
Panic("Failed to update font texture");
|
||||
}
|
||||
}
|
||||
|
||||
if (!System::IsShutdown())
|
||||
g_gpu->UpdateResolutionScale();
|
||||
}
|
||||
|
||||
bool NoGUIHostInterface::CreateDisplay()
|
||||
{
|
||||
std::optional<WindowInfo> wi = GetPlatformWindowInfo();
|
||||
if (!wi)
|
||||
{
|
||||
ReportError("Failed to get platform window info");
|
||||
return false;
|
||||
}
|
||||
|
||||
// imgui init from window
|
||||
ImGui::GetIO().DisplayFramebufferScale.x = wi->surface_scale;
|
||||
ImGui::GetIO().DisplayFramebufferScale.y = wi->surface_scale;
|
||||
ImGui::GetStyle() = ImGuiStyle();
|
||||
ImGui::GetStyle().ScaleAllSizes(wi->surface_scale);
|
||||
ImGui::StyleColorsDarker();
|
||||
|
||||
Assert(!m_display);
|
||||
switch (g_settings.gpu_renderer)
|
||||
{
|
||||
case GPURenderer::HardwareVulkan:
|
||||
m_display = std::make_unique<FrontendCommon::VulkanHostDisplay>();
|
||||
break;
|
||||
|
||||
case GPURenderer::HardwareOpenGL:
|
||||
#ifndef WIN32
|
||||
default:
|
||||
#endif
|
||||
m_display = std::make_unique<FrontendCommon::OpenGLHostDisplay>();
|
||||
break;
|
||||
|
||||
#ifdef WIN32
|
||||
case GPURenderer::HardwareD3D11:
|
||||
default:
|
||||
m_display = std::make_unique<FrontendCommon::D3D11HostDisplay>();
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
|
||||
if (!m_display->CreateRenderDevice(wi.value(), 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))
|
||||
{
|
||||
ReportError("Failed to create/initialize display render device");
|
||||
m_display->DestroyRenderDevice();
|
||||
m_display.reset();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!m_display->CreateImGuiContext() ||
|
||||
(m_fullscreen_ui_enabled && !FullscreenUI::Initialize(this, m_settings_interface.get())) ||
|
||||
!m_display->UpdateImGuiFontTexture())
|
||||
{
|
||||
ReportError("Failed to initialize imgui/fonts/fullscreen UI");
|
||||
if (m_fullscreen_ui_enabled)
|
||||
FullscreenUI::Shutdown();
|
||||
|
||||
m_display->DestroyImGuiContext();
|
||||
m_display->DestroyRenderDevice();
|
||||
m_display.reset();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void NoGUIHostInterface::DestroyDisplay()
|
||||
{
|
||||
if (m_fullscreen_ui_enabled)
|
||||
FullscreenUI::Shutdown();
|
||||
|
||||
if (m_display)
|
||||
{
|
||||
m_display->DestroyImGuiContext();
|
||||
m_display->DestroyRenderDevice();
|
||||
}
|
||||
m_display.reset();
|
||||
}
|
||||
|
||||
bool NoGUIHostInterface::AcquireHostDisplay()
|
||||
{
|
||||
// Handle renderer switch if required.
|
||||
const HostDisplay::RenderAPI render_api = m_display->GetRenderAPI();
|
||||
bool needs_switch = false;
|
||||
switch (g_settings.gpu_renderer)
|
||||
{
|
||||
#ifdef WIN32
|
||||
case GPURenderer::HardwareD3D11:
|
||||
needs_switch = (render_api != HostDisplay::RenderAPI::D3D11);
|
||||
break;
|
||||
#endif
|
||||
|
||||
case GPURenderer::HardwareVulkan:
|
||||
needs_switch = (render_api != HostDisplay::RenderAPI::Vulkan);
|
||||
break;
|
||||
|
||||
case GPURenderer::HardwareOpenGL:
|
||||
needs_switch = (render_api != HostDisplay::RenderAPI::OpenGL && render_api != HostDisplay::RenderAPI::OpenGLES);
|
||||
break;
|
||||
|
||||
case GPURenderer::Software:
|
||||
default:
|
||||
needs_switch = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (needs_switch)
|
||||
{
|
||||
ImGui::EndFrame();
|
||||
DestroyDisplay();
|
||||
|
||||
// We need to recreate the window, otherwise bad things happen...
|
||||
DestroyPlatformWindow();
|
||||
if (!CreatePlatformWindow())
|
||||
Panic("Failed to recreate platform window on GPU renderer switch");
|
||||
|
||||
if (!CreateDisplay())
|
||||
Panic("Failed to recreate display on GPU renderer switch");
|
||||
|
||||
ImGui::NewFrame();
|
||||
}
|
||||
|
||||
if (!CreateHostDisplayResources())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void NoGUIHostInterface::ReleaseHostDisplay()
|
||||
{
|
||||
ReleaseHostDisplayResources();
|
||||
|
||||
// restore vsync, since we don't want to burn cycles at the menu
|
||||
m_display->SetVSync(true);
|
||||
}
|
||||
|
||||
void NoGUIHostInterface::OnSystemCreated()
|
||||
{
|
||||
CommonHostInterface::OnSystemCreated();
|
||||
if (m_fullscreen_ui_enabled)
|
||||
FullscreenUI::SystemCreated();
|
||||
}
|
||||
|
||||
void NoGUIHostInterface::OnSystemPaused(bool paused)
|
||||
{
|
||||
CommonHostInterface::OnSystemPaused(paused);
|
||||
if (m_fullscreen_ui_enabled)
|
||||
FullscreenUI::SystemPaused(paused);
|
||||
}
|
||||
|
||||
void NoGUIHostInterface::OnSystemDestroyed()
|
||||
{
|
||||
CommonHostInterface::OnSystemDestroyed();
|
||||
ReportFormattedMessage("System shut down.");
|
||||
if (m_fullscreen_ui_enabled)
|
||||
FullscreenUI::SystemDestroyed();
|
||||
}
|
||||
|
||||
void NoGUIHostInterface::OnRunningGameChanged()
|
||||
{
|
||||
CommonHostInterface::OnRunningGameChanged();
|
||||
|
||||
// TODO: Move to common
|
||||
if (g_settings.apply_game_settings)
|
||||
ApplySettings(true);
|
||||
}
|
||||
|
||||
void NoGUIHostInterface::RequestExit()
|
||||
{
|
||||
m_quit_request = true;
|
||||
}
|
||||
|
||||
void NoGUIHostInterface::PollAndUpdate()
|
||||
{
|
||||
CommonHostInterface::PollAndUpdate();
|
||||
|
||||
if (m_controller_interface)
|
||||
m_controller_interface->PollEvents();
|
||||
}
|
||||
|
||||
void NoGUIHostInterface::Run()
|
||||
{
|
||||
while (!m_quit_request)
|
||||
{
|
||||
RunCallbacks();
|
||||
PollAndUpdate();
|
||||
if (m_fullscreen_ui_enabled)
|
||||
FullscreenUI::SetImGuiNavInputs();
|
||||
|
||||
ImGui::NewFrame();
|
||||
|
||||
if (System::IsRunning())
|
||||
{
|
||||
if (m_display_all_frames)
|
||||
System::RunFrame();
|
||||
else
|
||||
System::RunFrames();
|
||||
|
||||
UpdateControllerRumble();
|
||||
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())
|
||||
{
|
||||
if (g_settings.save_state_on_exit)
|
||||
SaveResumeSaveState();
|
||||
DestroySystem();
|
||||
}
|
||||
}
|
||||
|
||||
void NoGUIHostInterface::RunLater(std::function<void()> callback)
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(m_queued_callbacks_lock);
|
||||
m_queued_callbacks.push_back(std::move(callback));
|
||||
}
|
||||
|
||||
void NoGUIHostInterface::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();
|
||||
}
|
||||
}
|
73
src/duckstation-nogui/nogui_host_interface.h
Normal file
73
src/duckstation-nogui/nogui_host_interface.h
Normal file
|
@ -0,0 +1,73 @@
|
|||
#pragma once
|
||||
#include "common/window_info.h"
|
||||
#include "core/host_display.h"
|
||||
#include "core/host_interface.h"
|
||||
#include "frontend-common/common_host_interface.h"
|
||||
#include <array>
|
||||
#include <deque>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
|
||||
class INISettingsInterface;
|
||||
|
||||
class NoGUIHostInterface : public CommonHostInterface
|
||||
{
|
||||
public:
|
||||
NoGUIHostInterface();
|
||||
~NoGUIHostInterface();
|
||||
|
||||
const char* GetFrontendName() const override;
|
||||
|
||||
virtual bool Initialize() override;
|
||||
virtual void Shutdown() override;
|
||||
virtual void Run();
|
||||
|
||||
std::string GetStringSettingValue(const char* section, const char* key, const char* default_value = "") override;
|
||||
bool GetBoolSettingValue(const char* section, const char* key, bool default_value = false) override;
|
||||
int GetIntSettingValue(const char* section, const char* key, int default_value = 0) override;
|
||||
float GetFloatSettingValue(const char* section, const char* key, float default_value = 0.0f) override;
|
||||
|
||||
void RunLater(std::function<void()> callback) override;
|
||||
void ApplySettings(bool display_osd_messages) override;
|
||||
|
||||
protected:
|
||||
enum : u32
|
||||
{
|
||||
DEFAULT_WINDOW_WIDTH = 1280,
|
||||
DEFAULT_WINDOW_HEIGHT = 720
|
||||
};
|
||||
|
||||
virtual void LoadSettings() override;
|
||||
|
||||
bool AcquireHostDisplay() override;
|
||||
void ReleaseHostDisplay() override;
|
||||
|
||||
void UpdateInputMap() override;
|
||||
|
||||
void OnSystemCreated() override;
|
||||
void OnSystemPaused(bool paused) override;
|
||||
void OnSystemDestroyed() override;
|
||||
void OnRunningGameChanged() override;
|
||||
|
||||
void RequestExit() override;
|
||||
virtual void PollAndUpdate() override;
|
||||
|
||||
virtual bool CreatePlatformWindow() = 0;
|
||||
virtual void DestroyPlatformWindow() = 0;
|
||||
virtual std::optional<WindowInfo> GetPlatformWindowInfo() = 0;
|
||||
void OnPlatformWindowResized(u32 new_width, u32 new_height, float new_scale);
|
||||
|
||||
bool CreateDisplay();
|
||||
void DestroyDisplay();
|
||||
void CreateImGuiContext();
|
||||
void RunCallbacks();
|
||||
|
||||
std::unique_ptr<INISettingsInterface> m_settings_interface;
|
||||
std::deque<std::function<void()>> m_queued_callbacks;
|
||||
std::mutex m_queued_callbacks_lock;
|
||||
|
||||
bool m_quit_request = false;
|
||||
};
|
|
@ -1,41 +1,61 @@
|
|||
#include "sdl_host_interface.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/byte_stream.h"
|
||||
#include "common/file_system.h"
|
||||
#include "common/image.h"
|
||||
#include "common/log.h"
|
||||
#include "common/string_util.h"
|
||||
#include "core/gpu.h"
|
||||
#include "core/host_display.h"
|
||||
#include "core/system.h"
|
||||
#include "frontend-common/fullscreen_ui.h"
|
||||
#include "frontend-common/controller_interface.h"
|
||||
#include "frontend-common/icon.h"
|
||||
#include "frontend-common/imgui_fullscreen.h"
|
||||
#include "frontend-common/imgui_styles.h"
|
||||
#include "frontend-common/ini_settings_interface.h"
|
||||
#include "frontend-common/opengl_host_display.h"
|
||||
#include "frontend-common/sdl_audio_stream.h"
|
||||
#include "frontend-common/sdl_controller_interface.h"
|
||||
#include "frontend-common/vulkan_host_display.h"
|
||||
#include "frontend-common/sdl_initializer.h"
|
||||
#include "imgui.h"
|
||||
#include "imgui_impl_sdl.h"
|
||||
#include "imgui_stdlib.h"
|
||||
#include "scmversion/scmversion.h"
|
||||
#include "sdl_key_names.h"
|
||||
#include "sdl_util.h"
|
||||
#include <SDL_syswm.h>
|
||||
#include <cinttypes>
|
||||
#include <cmath>
|
||||
Log_SetChannel(SDLHostInterface);
|
||||
|
||||
#ifdef WIN32
|
||||
#include "frontend-common/d3d11_host_display.h"
|
||||
#ifdef __APPLE__
|
||||
#include <objc/message.h>
|
||||
struct NSView;
|
||||
|
||||
static NSView* GetContentViewFromWindow(NSWindow* window)
|
||||
{
|
||||
// window.contentView
|
||||
return reinterpret_cast<NSView* (*)(id, SEL)>(objc_msgSend)(reinterpret_cast<id>(window), sel_getUid("contentView"));
|
||||
}
|
||||
#endif
|
||||
|
||||
SDLHostInterface::SDLHostInterface()
|
||||
static float GetDPIScaleFactor(SDL_Window* window)
|
||||
{
|
||||
m_run_later_event_id = SDL_RegisterEvents(1);
|
||||
#ifdef __APPLE__
|
||||
static constexpr float DEFAULT_DPI = 72.0f;
|
||||
#else
|
||||
static constexpr float DEFAULT_DPI = 96.0f;
|
||||
#endif
|
||||
|
||||
if (!window)
|
||||
{
|
||||
SDL_Window* dummy_window = SDL_CreateWindow("", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 1, 1,
|
||||
SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_HIDDEN);
|
||||
if (!dummy_window)
|
||||
return 1.0f;
|
||||
|
||||
const float scale = GetDPIScaleFactor(dummy_window);
|
||||
|
||||
SDL_DestroyWindow(dummy_window);
|
||||
|
||||
return scale;
|
||||
}
|
||||
|
||||
int display_index = SDL_GetWindowDisplayIndex(window);
|
||||
float display_dpi = DEFAULT_DPI;
|
||||
if (SDL_GetDisplayDPI(display_index, &display_dpi, nullptr, nullptr) != 0)
|
||||
return 1.0f;
|
||||
|
||||
return display_dpi / DEFAULT_DPI;
|
||||
}
|
||||
|
||||
SDLHostInterface::SDLHostInterface() = default;
|
||||
|
||||
SDLHostInterface::~SDLHostInterface() = default;
|
||||
|
||||
const char* SDLHostInterface::GetFrontendName() const
|
||||
|
@ -43,16 +63,76 @@ const char* SDLHostInterface::GetFrontendName() const
|
|||
return "DuckStation NoGUI Frontend";
|
||||
}
|
||||
|
||||
std::unique_ptr<SDLHostInterface> SDLHostInterface::Create()
|
||||
{
|
||||
return std::make_unique<SDLHostInterface>();
|
||||
}
|
||||
|
||||
bool SDLHostInterface::Initialize()
|
||||
{
|
||||
FrontendCommon::EnsureSDLInitialized();
|
||||
|
||||
if (!NoGUIHostInterface::Initialize())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SDLHostInterface::Shutdown()
|
||||
{
|
||||
NoGUIHostInterface::Shutdown();
|
||||
}
|
||||
|
||||
bool SDLHostInterface::IsFullscreen() const
|
||||
{
|
||||
return m_fullscreen;
|
||||
}
|
||||
|
||||
bool SDLHostInterface::SetFullscreen(bool enabled)
|
||||
{
|
||||
if (m_fullscreen == enabled)
|
||||
return true;
|
||||
|
||||
SDL_SetWindowFullscreen(m_window, enabled ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0);
|
||||
|
||||
int window_width, window_height;
|
||||
SDL_GetWindowSize(m_window, &window_width, &window_height);
|
||||
m_display->ResizeRenderWindow(window_width, window_height);
|
||||
OnPlatformWindowResized(window_width, window_height, GetDPIScaleFactor(m_window));
|
||||
|
||||
m_fullscreen = enabled;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SDLHostInterface::RequestRenderWindowSize(s32 new_window_width, s32 new_window_height)
|
||||
{
|
||||
if (new_window_width <= 0 || new_window_height <= 0 || m_fullscreen)
|
||||
return false;
|
||||
|
||||
// use imgui scale as the dpr
|
||||
const float dpi_scale = ImGui::GetIO().DisplayFramebufferScale.x;
|
||||
const s32 scaled_width =
|
||||
std::max<s32>(static_cast<s32>(std::ceil(static_cast<float>(new_window_width) * dpi_scale)), 1);
|
||||
const s32 scaled_height = std::max<s32>(
|
||||
static_cast<s32>(std::ceil(static_cast<float>(new_window_height) * dpi_scale)) + m_display->GetDisplayTopMargin(),
|
||||
1);
|
||||
|
||||
SDL_SetWindowSize(m_window, scaled_width, scaled_height);
|
||||
|
||||
s32 window_width, window_height;
|
||||
SDL_GetWindowSize(m_window, &window_width, &window_height);
|
||||
m_display->ResizeRenderWindow(window_width, window_height);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
ALWAYS_INLINE static TinyString GetWindowTitle()
|
||||
{
|
||||
return TinyString::FromFormat("DuckStation %s (%s)", g_scm_tag_str, g_scm_branch_str);
|
||||
}
|
||||
|
||||
bool SDLHostInterface::CreateSDLWindow()
|
||||
bool SDLHostInterface::CreatePlatformWindow()
|
||||
{
|
||||
static constexpr u32 DEFAULT_WINDOW_WIDTH = 1280;
|
||||
static constexpr u32 DEFAULT_WINDOW_HEIGHT = 720;
|
||||
|
||||
// Create window.
|
||||
const u32 window_flags = SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI;
|
||||
|
||||
|
@ -63,7 +143,7 @@ bool SDLHostInterface::CreateSDLWindow()
|
|||
#ifndef __APPLE__
|
||||
{
|
||||
// scale by default monitor's DPI
|
||||
float scale = SDLUtil::GetDPIScaleFactor(nullptr);
|
||||
float scale = GetDPIScaleFactor(nullptr);
|
||||
window_width = static_cast<u32>(std::round(static_cast<float>(window_width) * scale));
|
||||
window_height = static_cast<u32>(std::round(static_cast<float>(window_height) * scale));
|
||||
}
|
||||
|
@ -88,179 +168,77 @@ bool SDLHostInterface::CreateSDLWindow()
|
|||
if (m_fullscreen)
|
||||
SDL_SetWindowFullscreen(m_window, SDL_WINDOW_FULLSCREEN_DESKTOP);
|
||||
|
||||
ImGui_ImplSDL2_Init(m_window);
|
||||
|
||||
// Process events so that we have everything sorted out before creating a child window for the GL context (X11).
|
||||
SDL_PumpEvents();
|
||||
return true;
|
||||
}
|
||||
|
||||
void SDLHostInterface::DestroySDLWindow()
|
||||
void SDLHostInterface::DestroyPlatformWindow()
|
||||
{
|
||||
ImGui_ImplSDL2_Shutdown();
|
||||
SDL_DestroyWindow(m_window);
|
||||
m_window = nullptr;
|
||||
}
|
||||
|
||||
bool SDLHostInterface::CreateDisplay()
|
||||
std::optional<WindowInfo> SDLHostInterface::GetPlatformWindowInfo()
|
||||
{
|
||||
std::optional<WindowInfo> wi = SDLUtil::GetWindowInfoForSDLWindow(m_window);
|
||||
if (!wi.has_value())
|
||||
SDL_SysWMinfo syswm = {};
|
||||
SDL_VERSION(&syswm.version);
|
||||
if (!SDL_GetWindowWMInfo(m_window, &syswm))
|
||||
{
|
||||
ReportError("Failed to get window info from SDL window");
|
||||
return false;
|
||||
Log_ErrorPrintf("SDL_GetWindowWMInfo failed");
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
switch (g_settings.gpu_renderer)
|
||||
int window_width, window_height;
|
||||
SDL_GetWindowSize(m_window, &window_width, &window_height);
|
||||
|
||||
WindowInfo wi;
|
||||
wi.surface_width = static_cast<u32>(window_width);
|
||||
wi.surface_height = static_cast<u32>(window_height);
|
||||
wi.surface_scale = GetDPIScaleFactor(m_window);
|
||||
wi.surface_format = WindowInfo::SurfaceFormat::RGB8;
|
||||
|
||||
switch (syswm.subsystem)
|
||||
{
|
||||
case GPURenderer::HardwareVulkan:
|
||||
m_display = std::make_unique<FrontendCommon::VulkanHostDisplay>();
|
||||
break;
|
||||
|
||||
case GPURenderer::HardwareOpenGL:
|
||||
#ifndef WIN32
|
||||
default:
|
||||
#endif
|
||||
m_display = std::make_unique<FrontendCommon::OpenGLHostDisplay>();
|
||||
break;
|
||||
|
||||
#ifdef WIN32
|
||||
case GPURenderer::HardwareD3D11:
|
||||
default:
|
||||
m_display = std::make_unique<FrontendCommon::D3D11HostDisplay>();
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
|
||||
Assert(m_display);
|
||||
if (!m_display->CreateRenderDevice(wi.value(), 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))
|
||||
{
|
||||
ReportError("Failed to create/initialize display render device");
|
||||
m_display.reset();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!ImGui_ImplSDL2_Init(m_window) || !m_display->CreateImGuiContext())
|
||||
{
|
||||
ReportError("Failed to initialize ImGui SDL2 wrapper");
|
||||
ImGui_ImplSDL2_Shutdown();
|
||||
m_display->DestroyRenderDevice();
|
||||
m_display.reset();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!FullscreenUI::Initialize(this, m_settings_interface.get()) || !m_display->UpdateImGuiFontTexture())
|
||||
{
|
||||
ReportError("Failed to initialize fonts/fullscreen UI");
|
||||
FullscreenUI::Shutdown();
|
||||
m_display->DestroyImGuiContext();
|
||||
ImGui_ImplSDL2_Shutdown();
|
||||
m_display->DestroyRenderDevice();
|
||||
m_display.reset();
|
||||
return false;
|
||||
}
|
||||
|
||||
m_fullscreen_ui_enabled = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void SDLHostInterface::DestroyDisplay()
|
||||
{
|
||||
FullscreenUI::Shutdown();
|
||||
m_display->DestroyImGuiContext();
|
||||
m_display->DestroyRenderDevice();
|
||||
m_display.reset();
|
||||
}
|
||||
|
||||
void SDLHostInterface::CreateImGuiContext()
|
||||
{
|
||||
const float framebuffer_scale = SDLUtil::GetDPIScaleFactor(m_window);
|
||||
|
||||
ImGui::CreateContext();
|
||||
ImGui::GetIO().IniFilename = nullptr;
|
||||
ImGui::GetIO().ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard | ImGuiConfigFlags_NavEnableGamepad;
|
||||
ImGui::GetIO().DisplayFramebufferScale.x = framebuffer_scale;
|
||||
ImGui::GetIO().DisplayFramebufferScale.y = framebuffer_scale;
|
||||
ImGui::GetStyle().ScaleAllSizes(framebuffer_scale);
|
||||
|
||||
ImGui::StyleColorsDarker();
|
||||
}
|
||||
|
||||
void SDLHostInterface::UpdateFramebufferScale()
|
||||
{
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
const float framebuffer_scale = SDLUtil::GetDPIScaleFactor(m_window);
|
||||
if (framebuffer_scale != io.DisplayFramebufferScale.x)
|
||||
{
|
||||
io.DisplayFramebufferScale = ImVec2(framebuffer_scale, framebuffer_scale);
|
||||
ImGui::GetStyle().ScaleAllSizes(framebuffer_scale);
|
||||
}
|
||||
|
||||
if (ImGuiFullscreen::UpdateLayoutScale())
|
||||
{
|
||||
if (ImGuiFullscreen::UpdateFonts())
|
||||
{
|
||||
if (!m_display->UpdateImGuiFontTexture())
|
||||
Panic("Failed to update font texture");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool SDLHostInterface::AcquireHostDisplay()
|
||||
{
|
||||
// Handle renderer switch if required.
|
||||
const HostDisplay::RenderAPI render_api = m_display->GetRenderAPI();
|
||||
bool needs_switch = false;
|
||||
switch (g_settings.gpu_renderer)
|
||||
{
|
||||
#ifdef WIN32
|
||||
case GPURenderer::HardwareD3D11:
|
||||
needs_switch = (render_api != HostDisplay::RenderAPI::D3D11);
|
||||
#ifdef SDL_VIDEO_DRIVER_WINDOWS
|
||||
case SDL_SYSWM_WINDOWS:
|
||||
wi.type = WindowInfo::Type::Win32;
|
||||
wi.window_handle = syswm.info.win.window;
|
||||
break;
|
||||
#endif
|
||||
|
||||
case GPURenderer::HardwareVulkan:
|
||||
needs_switch = (render_api != HostDisplay::RenderAPI::Vulkan);
|
||||
#ifdef SDL_VIDEO_DRIVER_COCOA
|
||||
case SDL_SYSWM_COCOA:
|
||||
wi.type = WindowInfo::Type::MacOS;
|
||||
wi.window_handle = GetContentViewFromWindow(syswm.info.cocoa.window);
|
||||
break;
|
||||
#endif
|
||||
|
||||
case GPURenderer::HardwareOpenGL:
|
||||
needs_switch = (render_api != HostDisplay::RenderAPI::OpenGL && render_api != HostDisplay::RenderAPI::OpenGLES);
|
||||
#ifdef SDL_VIDEO_DRIVER_X11
|
||||
case SDL_SYSWM_X11:
|
||||
wi.type = WindowInfo::Type::X11;
|
||||
wi.window_handle = reinterpret_cast<void*>(static_cast<uintptr_t>(syswm.info.x11.window));
|
||||
wi.display_connection = syswm.info.x11.display;
|
||||
break;
|
||||
#endif
|
||||
|
||||
#ifdef SDL_VIDEO_DRIVER_WAYLAND
|
||||
case SDL_SYSWM_WAYLAND:
|
||||
wi.type = WindowInfo::Type::Wayland;
|
||||
wi.window_handle = syswm.info.wl.surface;
|
||||
wi.display_connection = syswm.info.wl.display;
|
||||
break;
|
||||
#endif
|
||||
|
||||
case GPURenderer::Software:
|
||||
default:
|
||||
needs_switch = false;
|
||||
break;
|
||||
Log_ErrorPrintf("Unhandled syswm subsystem %u", static_cast<u32>(syswm.subsystem));
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
if (needs_switch)
|
||||
{
|
||||
ImGui::EndFrame();
|
||||
DestroyDisplay();
|
||||
|
||||
// We need to recreate the window, otherwise bad things happen...
|
||||
DestroySDLWindow();
|
||||
if (!CreateSDLWindow())
|
||||
Panic("Failed to recreate SDL window on GPU renderer switch");
|
||||
|
||||
if (!CreateDisplay())
|
||||
Panic("Failed to recreate display on GPU renderer switch");
|
||||
|
||||
ImGui::NewFrame();
|
||||
}
|
||||
|
||||
if (!CreateHostDisplayResources())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SDLHostInterface::ReleaseHostDisplay()
|
||||
{
|
||||
ReleaseHostDisplayResources();
|
||||
|
||||
// restore vsync, since we don't want to burn cycles at the menu
|
||||
m_display->SetVSync(true);
|
||||
return wi;
|
||||
}
|
||||
|
||||
std::optional<CommonHostInterface::HostKeyCode> SDLHostInterface::GetHostKeyCode(const std::string_view key_code) const
|
||||
|
@ -272,197 +250,6 @@ std::optional<CommonHostInterface::HostKeyCode> SDLHostInterface::GetHostKeyCode
|
|||
return static_cast<HostKeyCode>(*code);
|
||||
}
|
||||
|
||||
void SDLHostInterface::UpdateInputMap()
|
||||
{
|
||||
CommonHostInterface::UpdateInputMap(*m_settings_interface.get());
|
||||
}
|
||||
|
||||
void SDLHostInterface::OnSystemCreated()
|
||||
{
|
||||
CommonHostInterface::OnSystemCreated();
|
||||
FullscreenUI::SystemCreated();
|
||||
}
|
||||
|
||||
void SDLHostInterface::OnSystemPaused(bool paused)
|
||||
{
|
||||
CommonHostInterface::OnSystemPaused(paused);
|
||||
FullscreenUI::SystemPaused(paused);
|
||||
}
|
||||
|
||||
void SDLHostInterface::OnSystemDestroyed()
|
||||
{
|
||||
CommonHostInterface::OnSystemDestroyed();
|
||||
ReportFormattedMessage("System shut down.");
|
||||
FullscreenUI::SystemDestroyed();
|
||||
}
|
||||
|
||||
void SDLHostInterface::OnRunningGameChanged()
|
||||
{
|
||||
CommonHostInterface::OnRunningGameChanged();
|
||||
|
||||
Settings old_settings(std::move(g_settings));
|
||||
CommonHostInterface::LoadSettings(*m_settings_interface.get());
|
||||
CommonHostInterface::ApplyGameSettings(true);
|
||||
CommonHostInterface::FixIncompatibleSettings(true);
|
||||
CheckForSettingsChanges(old_settings);
|
||||
|
||||
if (!System::GetRunningTitle().empty())
|
||||
SDL_SetWindowTitle(m_window, System::GetRunningTitle().c_str());
|
||||
else
|
||||
SDL_SetWindowTitle(m_window, GetWindowTitle());
|
||||
}
|
||||
|
||||
void SDLHostInterface::RequestExit()
|
||||
{
|
||||
m_quit_request = true;
|
||||
}
|
||||
|
||||
void SDLHostInterface::RunLater(std::function<void()> callback)
|
||||
{
|
||||
SDL_Event ev = {};
|
||||
ev.type = SDL_USEREVENT;
|
||||
ev.user.code = m_run_later_event_id;
|
||||
ev.user.data1 = new std::function<void()>(std::move(callback));
|
||||
SDL_PushEvent(&ev);
|
||||
}
|
||||
|
||||
void SDLHostInterface::ApplySettings(bool display_osd_messages)
|
||||
{
|
||||
Settings old_settings(std::move(g_settings));
|
||||
CommonHostInterface::LoadSettings(*m_settings_interface.get());
|
||||
CommonHostInterface::ApplyGameSettings(display_osd_messages);
|
||||
CommonHostInterface::FixIncompatibleSettings(display_osd_messages);
|
||||
CheckForSettingsChanges(old_settings);
|
||||
}
|
||||
|
||||
bool SDLHostInterface::IsFullscreen() const
|
||||
{
|
||||
return m_fullscreen;
|
||||
}
|
||||
|
||||
bool SDLHostInterface::SetFullscreen(bool enabled)
|
||||
{
|
||||
if (m_fullscreen == enabled)
|
||||
return true;
|
||||
|
||||
SDL_SetWindowFullscreen(m_window, enabled ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0);
|
||||
|
||||
int window_width, window_height;
|
||||
SDL_GetWindowSize(m_window, &window_width, &window_height);
|
||||
m_display->ResizeRenderWindow(window_width, window_height);
|
||||
|
||||
if (!System::IsShutdown())
|
||||
g_gpu->UpdateResolutionScale();
|
||||
|
||||
m_fullscreen = enabled;
|
||||
return true;
|
||||
}
|
||||
|
||||
std::unique_ptr<SDLHostInterface> SDLHostInterface::Create()
|
||||
{
|
||||
return std::make_unique<SDLHostInterface>();
|
||||
}
|
||||
|
||||
bool SDLHostInterface::Initialize()
|
||||
{
|
||||
if (!CommonHostInterface::Initialize())
|
||||
return false;
|
||||
|
||||
// Change to the user directory so that all default/relative paths in the config are after this.
|
||||
if (!FileSystem::SetWorkingDirectory(m_user_directory.c_str()))
|
||||
Log_ErrorPrintf("Failed to set working directory to '%s'", m_user_directory.c_str());
|
||||
|
||||
if (!CreateSDLWindow())
|
||||
{
|
||||
Log_ErrorPrintf("Failed to create SDL window");
|
||||
return false;
|
||||
}
|
||||
|
||||
CreateImGuiContext();
|
||||
if (!CreateDisplay())
|
||||
{
|
||||
Log_ErrorPrintf("Failed to create host display");
|
||||
return false;
|
||||
}
|
||||
|
||||
// process events to pick up controllers before updating input map
|
||||
ProcessEvents();
|
||||
UpdateInputMap();
|
||||
return true;
|
||||
}
|
||||
|
||||
void SDLHostInterface::Shutdown()
|
||||
{
|
||||
DestroySystem();
|
||||
|
||||
CommonHostInterface::Shutdown();
|
||||
|
||||
if (m_display)
|
||||
{
|
||||
DestroyDisplay();
|
||||
ImGui::DestroyContext();
|
||||
}
|
||||
|
||||
if (m_window)
|
||||
DestroySDLWindow();
|
||||
}
|
||||
|
||||
std::string SDLHostInterface::GetStringSettingValue(const char* section, const char* key,
|
||||
const char* default_value /*= ""*/)
|
||||
{
|
||||
return m_settings_interface->GetStringValue(section, key, default_value);
|
||||
}
|
||||
|
||||
bool SDLHostInterface::GetBoolSettingValue(const char* section, const char* key, bool default_value /* = false */)
|
||||
{
|
||||
return m_settings_interface->GetBoolValue(section, key, default_value);
|
||||
}
|
||||
|
||||
int SDLHostInterface::GetIntSettingValue(const char* section, const char* key, int default_value /* = 0 */)
|
||||
{
|
||||
return m_settings_interface->GetIntValue(section, key, default_value);
|
||||
}
|
||||
|
||||
float SDLHostInterface::GetFloatSettingValue(const char* section, const char* key, float default_value /* = 0.0f */)
|
||||
{
|
||||
return m_settings_interface->GetFloatValue(section, key, default_value);
|
||||
}
|
||||
|
||||
bool SDLHostInterface::RequestRenderWindowSize(s32 new_window_width, s32 new_window_height)
|
||||
{
|
||||
if (new_window_width <= 0 || new_window_height <= 0 || m_fullscreen)
|
||||
return false;
|
||||
|
||||
// use imgui scale as the dpr
|
||||
const float dpi_scale = ImGui::GetIO().DisplayFramebufferScale.x;
|
||||
const s32 scaled_width =
|
||||
std::max<s32>(static_cast<s32>(std::ceil(static_cast<float>(new_window_width) * dpi_scale)), 1);
|
||||
const s32 scaled_height = std::max<s32>(
|
||||
static_cast<s32>(std::ceil(static_cast<float>(new_window_height) * dpi_scale)) + m_display->GetDisplayTopMargin(),
|
||||
1);
|
||||
|
||||
SDL_SetWindowSize(m_window, scaled_width, scaled_height);
|
||||
|
||||
s32 window_width, window_height;
|
||||
SDL_GetWindowSize(m_window, &window_width, &window_height);
|
||||
m_display->ResizeRenderWindow(window_width, window_height);
|
||||
|
||||
UpdateFramebufferScale();
|
||||
|
||||
if (!System::IsShutdown())
|
||||
g_gpu->UpdateResolutionScale();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SDLHostInterface::LoadSettings()
|
||||
{
|
||||
// Settings need to be loaded prior to creating the window for OpenGL bits.
|
||||
m_settings_interface = std::make_unique<INISettingsInterface>(GetSettingsFileName());
|
||||
CommonHostInterface::LoadSettings(*m_settings_interface.get());
|
||||
CommonHostInterface::FixIncompatibleSettings(false);
|
||||
}
|
||||
|
||||
void SDLHostInterface::ReportError(const char* message)
|
||||
{
|
||||
const bool was_fullscreen = IsFullscreen();
|
||||
|
@ -514,16 +301,35 @@ bool SDLHostInterface::ConfirmMessage(const char* message)
|
|||
return result;
|
||||
}
|
||||
|
||||
void SDLHostInterface::PollAndUpdate()
|
||||
{
|
||||
// Process SDL events before the controller interface can steal them.
|
||||
const bool is_sdl_controller_interface =
|
||||
(m_controller_interface && m_controller_interface->GetBackend() == ControllerInterface::Backend::SDL);
|
||||
|
||||
for (;;)
|
||||
{
|
||||
SDL_Event ev;
|
||||
if (!SDL_PollEvent(&ev))
|
||||
break;
|
||||
|
||||
if (is_sdl_controller_interface &&
|
||||
static_cast<SDLControllerInterface*>(m_controller_interface.get())->ProcessSDLEvent(&ev))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
HandleSDLEvent(&ev);
|
||||
}
|
||||
|
||||
ImGui_ImplSDL2_NewFrame();
|
||||
CommonHostInterface::PollAndUpdate();
|
||||
}
|
||||
|
||||
void SDLHostInterface::HandleSDLEvent(const SDL_Event* event)
|
||||
{
|
||||
ImGui_ImplSDL2_ProcessEvent(event);
|
||||
|
||||
if (m_controller_interface &&
|
||||
static_cast<SDLControllerInterface*>(m_controller_interface.get())->ProcessSDLEvent(event))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
switch (event->type)
|
||||
{
|
||||
case SDL_WINDOWEVENT:
|
||||
|
@ -531,14 +337,11 @@ void SDLHostInterface::HandleSDLEvent(const SDL_Event* event)
|
|||
if (event->window.event == SDL_WINDOWEVENT_RESIZED)
|
||||
{
|
||||
m_display->ResizeRenderWindow(event->window.data1, event->window.data2);
|
||||
UpdateFramebufferScale();
|
||||
|
||||
if (!System::IsShutdown())
|
||||
g_gpu->UpdateResolutionScale();
|
||||
OnPlatformWindowResized(event->window.data1, event->window.data2, GetDPIScaleFactor(m_window));
|
||||
}
|
||||
else if (event->window.event == SDL_WINDOWEVENT_MOVED)
|
||||
{
|
||||
UpdateFramebufferScale();
|
||||
// TODO: Do we want to update DPI scale here?
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
@ -576,86 +379,5 @@ void SDLHostInterface::HandleSDLEvent(const SDL_Event* event)
|
|||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case SDL_USEREVENT:
|
||||
{
|
||||
if (static_cast<u32>(event->user.code) == m_run_later_event_id)
|
||||
{
|
||||
std::function<void()>* callback = static_cast<std::function<void()>*>(event->user.data1);
|
||||
Assert(callback);
|
||||
(*callback)();
|
||||
delete callback;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void SDLHostInterface::PollAndUpdate()
|
||||
{
|
||||
ProcessEvents();
|
||||
CommonHostInterface::PollAndUpdate();
|
||||
}
|
||||
|
||||
void SDLHostInterface::ProcessEvents()
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
SDL_Event ev;
|
||||
if (SDL_PollEvent(&ev))
|
||||
HandleSDLEvent(&ev);
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void SDLHostInterface::Run()
|
||||
{
|
||||
while (!m_quit_request)
|
||||
{
|
||||
PollAndUpdate();
|
||||
|
||||
if (System::IsRunning())
|
||||
{
|
||||
if (m_display_all_frames)
|
||||
System::RunFrame();
|
||||
else
|
||||
System::RunFrames();
|
||||
|
||||
UpdateControllerRumble();
|
||||
if (m_frame_step_request)
|
||||
{
|
||||
m_frame_step_request = false;
|
||||
PauseSystem(true);
|
||||
}
|
||||
}
|
||||
|
||||
// rendering
|
||||
{
|
||||
ImGui_ImplSDL2_NewFrame();
|
||||
FullscreenUI::SetImGuiNavInputs();
|
||||
ImGui::NewFrame();
|
||||
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())
|
||||
{
|
||||
if (g_settings.save_state_on_exit)
|
||||
SaveResumeSaveState();
|
||||
DestroySystem();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,24 +1,8 @@
|
|||
#pragma once
|
||||
#include "common/gl/program.h"
|
||||
#include "common/gl/texture.h"
|
||||
#include "core/host_display.h"
|
||||
#include "core/host_interface.h"
|
||||
#include "frontend-common/common_host_interface.h"
|
||||
#include "nogui_host_interface.h"
|
||||
#include <SDL.h>
|
||||
#include <array>
|
||||
#include <deque>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
|
||||
class AudioStream;
|
||||
|
||||
class INISettingsInterface;
|
||||
|
||||
struct GameListEntry;
|
||||
|
||||
class SDLHostInterface final : public CommonHostInterface
|
||||
class SDLHostInterface final : public NoGUIHostInterface
|
||||
{
|
||||
public:
|
||||
SDLHostInterface();
|
||||
|
@ -35,53 +19,23 @@ public:
|
|||
bool Initialize() override;
|
||||
void Shutdown() override;
|
||||
|
||||
std::string GetStringSettingValue(const char* section, const char* key, const char* default_value = "") override;
|
||||
bool GetBoolSettingValue(const char* section, const char* key, bool default_value = false) override;
|
||||
int GetIntSettingValue(const char* section, const char* key, int default_value = 0) override;
|
||||
float GetFloatSettingValue(const char* section, const char* key, float default_value = 0.0f) override;
|
||||
|
||||
bool RequestRenderWindowSize(s32 new_window_width, s32 new_window_height) override;
|
||||
|
||||
bool IsFullscreen() const override;
|
||||
bool SetFullscreen(bool enabled) override;
|
||||
|
||||
void RunLater(std::function<void()> callback) override;
|
||||
void ApplySettings(bool display_osd_messages) override;
|
||||
|
||||
void Run();
|
||||
|
||||
protected:
|
||||
void LoadSettings() override;
|
||||
|
||||
bool AcquireHostDisplay() override;
|
||||
void ReleaseHostDisplay() override;
|
||||
|
||||
void OnSystemCreated() override;
|
||||
void OnSystemPaused(bool paused) override;
|
||||
void OnSystemDestroyed() override;
|
||||
void OnRunningGameChanged() override;
|
||||
|
||||
void RequestExit() override;
|
||||
void PollAndUpdate() override;
|
||||
|
||||
std::optional<HostKeyCode> GetHostKeyCode(const std::string_view key_code) const override;
|
||||
void UpdateInputMap() override;
|
||||
|
||||
bool CreatePlatformWindow() override;
|
||||
void DestroyPlatformWindow() override;
|
||||
std::optional<WindowInfo> GetPlatformWindowInfo() override;
|
||||
|
||||
private:
|
||||
bool CreateSDLWindow();
|
||||
void DestroySDLWindow();
|
||||
bool CreateDisplay();
|
||||
void DestroyDisplay();
|
||||
void CreateImGuiContext();
|
||||
void UpdateFramebufferScale();
|
||||
|
||||
void HandleSDLEvent(const SDL_Event* event);
|
||||
void ProcessEvents();
|
||||
|
||||
SDL_Window* m_window = nullptr;
|
||||
std::unique_ptr<INISettingsInterface> m_settings_interface;
|
||||
u32 m_run_later_event_id = 0;
|
||||
|
||||
bool m_fullscreen = false;
|
||||
bool m_quit_request = false;
|
||||
};
|
||||
|
|
|
@ -1,107 +0,0 @@
|
|||
#include "sdl_util.h"
|
||||
#include "common/log.h"
|
||||
#include <SDL_syswm.h>
|
||||
Log_SetChannel(SDLUtil);
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include <objc/message.h>
|
||||
struct NSView;
|
||||
|
||||
static NSView* GetContentViewFromWindow(NSWindow* window)
|
||||
{
|
||||
// window.contentView
|
||||
return reinterpret_cast<NSView* (*)(id, SEL)>(objc_msgSend)(reinterpret_cast<id>(window), sel_getUid("contentView"));
|
||||
}
|
||||
#endif
|
||||
|
||||
namespace SDLUtil {
|
||||
|
||||
std::optional<WindowInfo> GetWindowInfoForSDLWindow(SDL_Window* window)
|
||||
{
|
||||
SDL_SysWMinfo syswm = {};
|
||||
SDL_VERSION(&syswm.version);
|
||||
if (!SDL_GetWindowWMInfo(window, &syswm))
|
||||
{
|
||||
Log_ErrorPrintf("SDL_GetWindowWMInfo failed");
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
int window_width, window_height;
|
||||
SDL_GetWindowSize(window, &window_width, &window_height);
|
||||
|
||||
WindowInfo wi;
|
||||
wi.surface_width = static_cast<u32>(window_width);
|
||||
wi.surface_height = static_cast<u32>(window_height);
|
||||
wi.surface_scale = GetDPIScaleFactor(window);
|
||||
wi.surface_format = WindowInfo::SurfaceFormat::RGB8;
|
||||
|
||||
switch (syswm.subsystem)
|
||||
{
|
||||
#ifdef SDL_VIDEO_DRIVER_WINDOWS
|
||||
case SDL_SYSWM_WINDOWS:
|
||||
wi.type = WindowInfo::Type::Win32;
|
||||
wi.window_handle = syswm.info.win.window;
|
||||
break;
|
||||
#endif
|
||||
|
||||
#ifdef SDL_VIDEO_DRIVER_COCOA
|
||||
case SDL_SYSWM_COCOA:
|
||||
wi.type = WindowInfo::Type::MacOS;
|
||||
wi.window_handle = GetContentViewFromWindow(syswm.info.cocoa.window);
|
||||
break;
|
||||
#endif
|
||||
|
||||
#ifdef SDL_VIDEO_DRIVER_X11
|
||||
case SDL_SYSWM_X11:
|
||||
wi.type = WindowInfo::Type::X11;
|
||||
wi.window_handle = reinterpret_cast<void*>(static_cast<uintptr_t>(syswm.info.x11.window));
|
||||
wi.display_connection = syswm.info.x11.display;
|
||||
break;
|
||||
#endif
|
||||
|
||||
#ifdef SDL_VIDEO_DRIVER_WAYLAND
|
||||
case SDL_SYSWM_WAYLAND:
|
||||
wi.type = WindowInfo::Type::Wayland;
|
||||
wi.window_handle = syswm.info.wl.surface;
|
||||
wi.display_connection = syswm.info.wl.display;
|
||||
break;
|
||||
#endif
|
||||
|
||||
default:
|
||||
Log_ErrorPrintf("Unhandled syswm subsystem %u", static_cast<u32>(syswm.subsystem));
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
return wi;
|
||||
}
|
||||
|
||||
float GetDPIScaleFactor(SDL_Window* window)
|
||||
{
|
||||
#ifdef __APPLE__
|
||||
static constexpr float DEFAULT_DPI = 72.0f;
|
||||
#else
|
||||
static constexpr float DEFAULT_DPI = 96.0f;
|
||||
#endif
|
||||
|
||||
if (!window)
|
||||
{
|
||||
SDL_Window* dummy_window = SDL_CreateWindow("", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 1, 1,
|
||||
SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_HIDDEN);
|
||||
if (!dummy_window)
|
||||
return 1.0f;
|
||||
|
||||
const float scale = GetDPIScaleFactor(dummy_window);
|
||||
|
||||
SDL_DestroyWindow(dummy_window);
|
||||
|
||||
return scale;
|
||||
}
|
||||
|
||||
int display_index = SDL_GetWindowDisplayIndex(window);
|
||||
float display_dpi = DEFAULT_DPI;
|
||||
if (SDL_GetDisplayDPI(display_index, &display_dpi, nullptr, nullptr) != 0)
|
||||
return 1.0f;
|
||||
|
||||
return display_dpi / DEFAULT_DPI;
|
||||
}
|
||||
} // namespace SDLUtil
|
|
@ -1,11 +0,0 @@
|
|||
#pragma once
|
||||
#include "common/types.h"
|
||||
#include "common/window_info.h"
|
||||
#include <optional>
|
||||
|
||||
struct SDL_Window;
|
||||
|
||||
namespace SDLUtil {
|
||||
std::optional<WindowInfo> GetWindowInfoForSDLWindow(SDL_Window* window);
|
||||
float GetDPIScaleFactor(SDL_Window* window);
|
||||
}
|
150
src/duckstation-nogui/win32_host_interface.cpp
Normal file
150
src/duckstation-nogui/win32_host_interface.cpp
Normal file
|
@ -0,0 +1,150 @@
|
|||
#include "win32_host_interface.h"
|
||||
#include "common/log.h"
|
||||
#include "common/string_util.h"
|
||||
#include "resource.h"
|
||||
#include <tchar.h>
|
||||
Log_SetChannel(Win32HostInterface);
|
||||
|
||||
static constexpr _TCHAR WINDOW_CLASS_NAME[] = _T("DuckStationNoGUI");
|
||||
|
||||
Win32HostInterface::Win32HostInterface() = default;
|
||||
|
||||
Win32HostInterface::~Win32HostInterface() = default;
|
||||
|
||||
std::unique_ptr<NoGUIHostInterface> Win32HostInterface::Create()
|
||||
{
|
||||
return std::make_unique<Win32HostInterface>();
|
||||
}
|
||||
|
||||
bool Win32HostInterface::Initialize()
|
||||
{
|
||||
if (!RegisterWindowClass())
|
||||
return false;
|
||||
|
||||
if (!NoGUIHostInterface::Initialize())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Win32HostInterface::Shutdown()
|
||||
{
|
||||
NoGUIHostInterface::Shutdown();
|
||||
}
|
||||
|
||||
bool Win32HostInterface::RegisterWindowClass()
|
||||
{
|
||||
WNDCLASSEXW wc = {};
|
||||
wc.cbSize = sizeof(WNDCLASSEXW);
|
||||
wc.style = 0;
|
||||
wc.lpfnWndProc = WndProc;
|
||||
wc.cbClsExtra = 0;
|
||||
wc.cbWndExtra = 0;
|
||||
wc.hInstance = GetModuleHandle(nullptr);
|
||||
wc.hIcon = LoadIconA(NULL, (LPCSTR)IDI_ICON1);
|
||||
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
|
||||
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
|
||||
wc.lpszMenuName = NULL;
|
||||
wc.lpszClassName = WINDOW_CLASS_NAME;
|
||||
wc.hIconSm = LoadIconA(NULL, (LPCSTR)IDI_ICON1);
|
||||
|
||||
if (!RegisterClassExW(&wc))
|
||||
{
|
||||
MessageBoxA(nullptr, "Window registration failed.", "Error", MB_ICONERROR | MB_OK);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Win32HostInterface::CreatePlatformWindow()
|
||||
{
|
||||
m_hwnd = CreateWindowExW(WS_EX_CLIENTEDGE, WINDOW_CLASS_NAME, _T("DuckStation"), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT,
|
||||
CW_USEDEFAULT, DEFAULT_WINDOW_WIDTH, DEFAULT_WINDOW_HEIGHT, nullptr, nullptr,
|
||||
GetModuleHandleA(nullptr), this);
|
||||
if (!m_hwnd)
|
||||
{
|
||||
MessageBoxA(nullptr, "CreateWindowEx failed.", "Error", MB_ICONERROR | MB_OK);
|
||||
return false;
|
||||
}
|
||||
|
||||
ShowWindow(m_hwnd, SW_SHOW);
|
||||
UpdateWindow(m_hwnd);
|
||||
ProcessWin32Events();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Win32HostInterface::DestroyPlatformWindow()
|
||||
{
|
||||
if (m_hwnd)
|
||||
{
|
||||
DestroyWindow(m_hwnd);
|
||||
m_hwnd = {};
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<WindowInfo> Win32HostInterface::GetPlatformWindowInfo()
|
||||
{
|
||||
RECT rc = {};
|
||||
GetClientRect(m_hwnd, &rc);
|
||||
|
||||
WindowInfo wi;
|
||||
wi.window_handle = static_cast<void*>(m_hwnd);
|
||||
wi.type = WindowInfo::Type::Win32;
|
||||
wi.surface_width = static_cast<u32>(rc.right - rc.left);
|
||||
wi.surface_height = static_cast<u32>(rc.bottom - rc.top);
|
||||
// wi.surface_format = WindowInfo::SurfaceFormat::Auto;
|
||||
return wi;
|
||||
}
|
||||
|
||||
void Win32HostInterface::PollAndUpdate()
|
||||
{
|
||||
ProcessWin32Events();
|
||||
|
||||
NoGUIHostInterface::PollAndUpdate();
|
||||
}
|
||||
|
||||
void Win32HostInterface::ProcessWin32Events()
|
||||
{
|
||||
MSG msg;
|
||||
while (PeekMessage(&msg, m_hwnd, 0, 0, PM_REMOVE))
|
||||
{
|
||||
TranslateMessage(&msg);
|
||||
DispatchMessage(&msg);
|
||||
}
|
||||
}
|
||||
|
||||
LRESULT Win32HostInterface::WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
Win32HostInterface* hi = static_cast<Win32HostInterface*>(g_host_interface);
|
||||
switch (msg)
|
||||
{
|
||||
case WM_SIZE:
|
||||
{
|
||||
const u32 width = LOWORD(lParam);
|
||||
const u32 height = HIWORD(lParam);
|
||||
if (hi->m_display)
|
||||
hi->m_display->ResizeRenderWindow(static_cast<s32>(width), static_cast<s32>(height));
|
||||
}
|
||||
break;
|
||||
|
||||
case WM_CLOSE:
|
||||
hi->m_quit_request = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
return DefWindowProc(hwnd, msg, wParam, lParam);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::optional<Win32HostInterface::HostKeyCode> Win32HostInterface::GetHostKeyCode(const std::string_view key_code) const
|
||||
{
|
||||
std::optional<int> kc; // = EvDevKeyNames::GetKeyCodeForName(key_code);
|
||||
if (!kc.has_value())
|
||||
return std::nullopt;
|
||||
|
||||
return static_cast<HostKeyCode>(kc.value());
|
||||
}
|
34
src/duckstation-nogui/win32_host_interface.h
Normal file
34
src/duckstation-nogui/win32_host_interface.h
Normal file
|
@ -0,0 +1,34 @@
|
|||
#pragma once
|
||||
#include "common/windows_headers.h"
|
||||
#include "nogui_host_interface.h"
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
class Win32HostInterface final : public NoGUIHostInterface
|
||||
{
|
||||
public:
|
||||
Win32HostInterface();
|
||||
~Win32HostInterface();
|
||||
|
||||
bool Initialize();
|
||||
void Shutdown();
|
||||
|
||||
static std::unique_ptr<NoGUIHostInterface> Create();
|
||||
|
||||
protected:
|
||||
bool CreatePlatformWindow() override;
|
||||
void DestroyPlatformWindow() override;
|
||||
std::optional<WindowInfo> GetPlatformWindowInfo() override;
|
||||
|
||||
std::optional<HostKeyCode> GetHostKeyCode(const std::string_view key_code) const override;
|
||||
|
||||
void PollAndUpdate() override;
|
||||
|
||||
private:
|
||||
static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
|
||||
|
||||
bool RegisterWindowClass();
|
||||
void ProcessWin32Events();
|
||||
|
||||
HWND m_hwnd{};
|
||||
};
|
Loading…
Reference in a new issue