mirror of
https://github.com/RetroDECK/Duckstation.git
synced 2025-01-20 15:25:38 +00:00
NoGUI: Add SDL platform
This commit is contained in:
parent
604dd5df40
commit
bcc7ab71cb
|
@ -86,3 +86,12 @@ if(ENABLE_WAYLAND)
|
|||
X11::xkbcommon
|
||||
)
|
||||
endif()
|
||||
|
||||
if(ENABLE_SDL2)
|
||||
message(STATUS "Building SDL NoGUI Platform.")
|
||||
target_sources(duckstation-nogui PRIVATE
|
||||
sdl_nogui_platform.cpp
|
||||
sdl_nogui_platform.h
|
||||
)
|
||||
target_link_libraries(duckstation-nogui PUBLIC SDL2::SDL2)
|
||||
endif()
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
<ClCompile Include="pch.cpp">
|
||||
<PrecompiledHeader>Create</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
<ClCompile Include="sdl_nogui_platform.cpp" />
|
||||
<ClCompile Include="wayland_nogui_platform.cpp">
|
||||
<ExcludedFromBuild>true</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
|
@ -19,6 +20,7 @@
|
|||
<ClInclude Include="nogui_platform.h" />
|
||||
<ClInclude Include="pch.h" />
|
||||
<ClInclude Include="resource.h" />
|
||||
<ClInclude Include="sdl_nogui_platform.h" />
|
||||
<ClInclude Include="wayland_nogui_platform.h">
|
||||
<ExcludedFromBuild>true</ExcludedFromBuild>
|
||||
</ClInclude>
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
<ClCompile Include="wayland_nogui_platform.cpp" />
|
||||
<ClCompile Include="x11_nogui_platform.cpp" />
|
||||
<ClCompile Include="pch.cpp" />
|
||||
<ClCompile Include="sdl_nogui_platform.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="resource.h" />
|
||||
|
@ -15,6 +16,7 @@
|
|||
<ClInclude Include="wayland_nogui_platform.h" />
|
||||
<ClInclude Include="x11_nogui_platform.h" />
|
||||
<ClInclude Include="pch.h" />
|
||||
<ClInclude Include="sdl_nogui_platform.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Manifest Include="duckstation-nogui.manifest" />
|
||||
|
|
|
@ -870,13 +870,20 @@ std::unique_ptr<NoGUIPlatform> NoGUIHost::CreatePlatform()
|
|||
{
|
||||
std::unique_ptr<NoGUIPlatform> ret;
|
||||
|
||||
const char* platform = std::getenv("DUCKSTATION_NOGUI_PLATFORM");
|
||||
#ifdef ENABLE_SDL2
|
||||
if (platform && StringUtil::Strcasecmp(platform, "sdl") == 0)
|
||||
ret = NoGUIPlatform::CreateSDLPlatform();
|
||||
#endif
|
||||
|
||||
#if defined(_WIN32)
|
||||
ret = NoGUIPlatform::CreateWin32Platform();
|
||||
if (!ret)
|
||||
ret = NoGUIPlatform::CreateWin32Platform();
|
||||
#elif defined(__APPLE__)
|
||||
ret = NoGUIPlatform::CreateCocoaPlatform();
|
||||
if (!ret)
|
||||
ret = NoGUIPlatform::CreateCocoaPlatform();
|
||||
#else
|
||||
// linux
|
||||
const char* platform = std::getenv("DUCKSTATION_NOGUI_PLATFORM");
|
||||
#ifdef NOGUI_PLATFORM_WAYLAND
|
||||
if (!ret && (!platform || StringUtil::Strcasecmp(platform, "wayland") == 0) && std::getenv("WAYLAND_DISPLAY"))
|
||||
ret = NoGUIPlatform::CreateWaylandPlatform();
|
||||
|
|
|
@ -52,6 +52,9 @@ public:
|
|||
#ifdef __APPLE__
|
||||
static std::unique_ptr<NoGUIPlatform> CreateCocoaPlatform();
|
||||
#endif
|
||||
#ifdef ENABLE_SDL2
|
||||
static std::unique_ptr<NoGUIPlatform> CreateSDLPlatform();
|
||||
#endif
|
||||
#ifdef NOGUI_PLATFORM_WAYLAND
|
||||
static std::unique_ptr<NoGUIPlatform> CreateWaylandPlatform();
|
||||
#endif
|
||||
|
|
274
src/duckstation-nogui/sdl_key_names.h
Normal file
274
src/duckstation-nogui/sdl_key_names.h
Normal file
|
@ -0,0 +1,274 @@
|
|||
// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com>
|
||||
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/types.h"
|
||||
#include "common/windows_headers.h"
|
||||
|
||||
#include <array>
|
||||
#include <cstring>
|
||||
#include <map>
|
||||
#include <optional>
|
||||
#include <string_view>
|
||||
|
||||
namespace SDLKeyNames {
|
||||
|
||||
static const std::map<int, const char*> s_sdl_key_names = {{SDLK_RETURN, "Return"},
|
||||
{SDLK_ESCAPE, "Escape"},
|
||||
{SDLK_BACKSPACE, "Backspace"},
|
||||
{SDLK_TAB, "Tab"},
|
||||
{SDLK_SPACE, "Space"},
|
||||
{SDLK_EXCLAIM, "Exclam"},
|
||||
{SDLK_QUOTEDBL, "QuoteDbl"},
|
||||
{SDLK_HASH, "Hash"},
|
||||
{SDLK_PERCENT, "Percent"},
|
||||
{SDLK_DOLLAR, "Dollar"},
|
||||
{SDLK_AMPERSAND, "Ampersand"},
|
||||
{SDLK_QUOTE, "Apostrophe"},
|
||||
{SDLK_LEFTPAREN, "ParenLeft"},
|
||||
{SDLK_RIGHTPAREN, "ParenRight"},
|
||||
{SDLK_ASTERISK, "Asterisk"},
|
||||
{SDLK_PLUS, "PLus"},
|
||||
{SDLK_COMMA, "Comma"},
|
||||
{SDLK_MINUS, "Minus"},
|
||||
{SDLK_PERIOD, "Period"},
|
||||
{SDLK_SLASH, "Slash"},
|
||||
{SDLK_0, "0"},
|
||||
{SDLK_1, "1"},
|
||||
{SDLK_2, "2"},
|
||||
{SDLK_3, "3"},
|
||||
{SDLK_4, "4"},
|
||||
{SDLK_5, "5"},
|
||||
{SDLK_6, "6"},
|
||||
{SDLK_7, "7"},
|
||||
{SDLK_8, "8"},
|
||||
{SDLK_9, "9"},
|
||||
{SDLK_COLON, "Colon"},
|
||||
{SDLK_SEMICOLON, "Semcolon"},
|
||||
{SDLK_LESS, "Less"},
|
||||
{SDLK_EQUALS, "Equal"},
|
||||
{SDLK_GREATER, "Greater"},
|
||||
{SDLK_QUESTION, "Question"},
|
||||
{SDLK_AT, "AT"},
|
||||
{SDLK_LEFTBRACKET, "BracketLeft"},
|
||||
{SDLK_BACKSLASH, "Backslash"},
|
||||
{SDLK_RIGHTBRACKET, "BracketRight"},
|
||||
{SDLK_CARET, "Caret"},
|
||||
{SDLK_UNDERSCORE, "Underscore"},
|
||||
{SDLK_BACKQUOTE, "Backquote"},
|
||||
{SDLK_a, "A"},
|
||||
{SDLK_b, "B"},
|
||||
{SDLK_c, "C"},
|
||||
{SDLK_d, "D"},
|
||||
{SDLK_e, "E"},
|
||||
{SDLK_f, "F"},
|
||||
{SDLK_g, "G"},
|
||||
{SDLK_h, "H"},
|
||||
{SDLK_i, "I"},
|
||||
{SDLK_j, "J"},
|
||||
{SDLK_k, "K"},
|
||||
{SDLK_l, "L"},
|
||||
{SDLK_m, "M"},
|
||||
{SDLK_n, "N"},
|
||||
{SDLK_o, "O"},
|
||||
{SDLK_p, "P"},
|
||||
{SDLK_q, "Q"},
|
||||
{SDLK_r, "R"},
|
||||
{SDLK_s, "S"},
|
||||
{SDLK_t, "T"},
|
||||
{SDLK_u, "U"},
|
||||
{SDLK_v, "V"},
|
||||
{SDLK_w, "W"},
|
||||
{SDLK_x, "X"},
|
||||
{SDLK_y, "Y"},
|
||||
{SDLK_z, "Z"},
|
||||
{SDLK_CAPSLOCK, "CapsLock"},
|
||||
{SDLK_F1, "F1"},
|
||||
{SDLK_F2, "F2"},
|
||||
{SDLK_F3, "F3"},
|
||||
{SDLK_F4, "F4"},
|
||||
{SDLK_F5, "F5"},
|
||||
{SDLK_F6, "F6"},
|
||||
{SDLK_F7, "F7"},
|
||||
{SDLK_F8, "F8"},
|
||||
{SDLK_F9, "F9"},
|
||||
{SDLK_F10, "F10"},
|
||||
{SDLK_F11, "F11"},
|
||||
{SDLK_F12, "F12"},
|
||||
{SDLK_PRINTSCREEN, "Print"},
|
||||
{SDLK_SCROLLLOCK, "ScrollLock"},
|
||||
{SDLK_PAUSE, "Pause"},
|
||||
{SDLK_INSERT, "Insert"},
|
||||
{SDLK_HOME, "Home"},
|
||||
{SDLK_PAGEUP, "PageUp"},
|
||||
{SDLK_DELETE, "Delete"},
|
||||
{SDLK_END, "End"},
|
||||
{SDLK_PAGEDOWN, "PageDown"},
|
||||
{SDLK_RIGHT, "Right"},
|
||||
{SDLK_LEFT, "Left"},
|
||||
{SDLK_DOWN, "Down"},
|
||||
{SDLK_UP, "Up"},
|
||||
{SDLK_NUMLOCKCLEAR, "NumLock"},
|
||||
{SDLK_KP_DIVIDE, "Keypad+Divide"},
|
||||
{SDLK_KP_MULTIPLY, "Keypad+Multiply"},
|
||||
{SDLK_KP_MINUS, "Keypad+Minus"},
|
||||
{SDLK_KP_PLUS, "Keypad+Plus"},
|
||||
{SDLK_KP_ENTER, "Keypad+Return"},
|
||||
{SDLK_KP_1, "Keypad+1"},
|
||||
{SDLK_KP_2, "Keypad+2"},
|
||||
{SDLK_KP_3, "Keypad+3"},
|
||||
{SDLK_KP_4, "Keypad+4"},
|
||||
{SDLK_KP_5, "Keypad+5"},
|
||||
{SDLK_KP_6, "Keypad+6"},
|
||||
{SDLK_KP_7, "Keypad+7"},
|
||||
{SDLK_KP_8, "Keypad+8"},
|
||||
{SDLK_KP_9, "Keypad+9"},
|
||||
{SDLK_KP_0, "Keypad+0"},
|
||||
{SDLK_KP_PERIOD, "Keypad+Period"},
|
||||
{SDLK_APPLICATION, "Application"},
|
||||
{SDLK_POWER, "Power"},
|
||||
{SDLK_KP_EQUALS, "Keypad+Equal"},
|
||||
{SDLK_F13, "F13"},
|
||||
{SDLK_F14, "F14"},
|
||||
{SDLK_F15, "F15"},
|
||||
{SDLK_F16, "F16"},
|
||||
{SDLK_F17, "F17"},
|
||||
{SDLK_F18, "F18"},
|
||||
{SDLK_F19, "F19"},
|
||||
{SDLK_F20, "F20"},
|
||||
{SDLK_F21, "F21"},
|
||||
{SDLK_F22, "F22"},
|
||||
{SDLK_F23, "F23"},
|
||||
{SDLK_F24, "F24"},
|
||||
{SDLK_EXECUTE, "Execute"},
|
||||
{SDLK_HELP, "Help"},
|
||||
{SDLK_MENU, "Menu"},
|
||||
{SDLK_SELECT, "Select"},
|
||||
{SDLK_STOP, "Stop"},
|
||||
{SDLK_AGAIN, "Again"},
|
||||
{SDLK_UNDO, "Undo"},
|
||||
{SDLK_CUT, "Cut"},
|
||||
{SDLK_COPY, "Copy"},
|
||||
{SDLK_PASTE, "Paste"},
|
||||
{SDLK_FIND, "Find"},
|
||||
{SDLK_MUTE, "Mute"},
|
||||
{SDLK_VOLUMEUP, "VolumeUp"},
|
||||
{SDLK_VOLUMEDOWN, "VolumeDown"},
|
||||
{SDLK_KP_COMMA, "Keypad+Comma"},
|
||||
{SDLK_KP_EQUALSAS400, "Keypad+EqualAS400"},
|
||||
{SDLK_ALTERASE, "AltErase"},
|
||||
{SDLK_SYSREQ, "SysReq"},
|
||||
{SDLK_CANCEL, "Cancel"},
|
||||
{SDLK_CLEAR, "Clear"},
|
||||
{SDLK_PRIOR, "Prior"},
|
||||
{SDLK_RETURN2, "Return2"},
|
||||
{SDLK_SEPARATOR, "Separator"},
|
||||
{SDLK_OUT, "Out"},
|
||||
{SDLK_OPER, "Oper"},
|
||||
{SDLK_CLEARAGAIN, "ClearAgain"},
|
||||
{SDLK_CRSEL, "CrSel"},
|
||||
{SDLK_EXSEL, "ExSel"},
|
||||
{SDLK_KP_00, "Keypad+00"},
|
||||
{SDLK_KP_000, "Keypad+000"},
|
||||
{SDLK_THOUSANDSSEPARATOR, "ThousandsSeparator"},
|
||||
{SDLK_DECIMALSEPARATOR, "DecimalSeparator"},
|
||||
{SDLK_CURRENCYUNIT, "CurrencyUnit"},
|
||||
{SDLK_CURRENCYSUBUNIT, "CurrencySubunit"},
|
||||
{SDLK_KP_LEFTPAREN, "Keypad+ParenLeft"},
|
||||
{SDLK_KP_RIGHTPAREN, "Keypad+ParenRight"},
|
||||
{SDLK_KP_LEFTBRACE, "Keypad+LeftBrace"},
|
||||
{SDLK_KP_RIGHTBRACE, "Keypad+RightBrace"},
|
||||
{SDLK_KP_TAB, "Keypad+Tab"},
|
||||
{SDLK_KP_BACKSPACE, "Keypad+Backspace"},
|
||||
{SDLK_KP_A, "Keypad+A"},
|
||||
{SDLK_KP_B, "Keypad+B"},
|
||||
{SDLK_KP_C, "Keypad+C"},
|
||||
{SDLK_KP_D, "Keypad+D"},
|
||||
{SDLK_KP_E, "Keypad+E"},
|
||||
{SDLK_KP_F, "Keypad+F"},
|
||||
{SDLK_KP_XOR, "Keypad+XOR"},
|
||||
{SDLK_KP_POWER, "Keypad+Power"},
|
||||
{SDLK_KP_PERCENT, "Keypad+Percent"},
|
||||
{SDLK_KP_LESS, "Keypad+Less"},
|
||||
{SDLK_KP_GREATER, "Keypad+Greater"},
|
||||
{SDLK_KP_AMPERSAND, "Keypad+Ampersand"},
|
||||
{SDLK_KP_DBLAMPERSAND, "Keypad+AmpersandDbl"},
|
||||
{SDLK_KP_VERTICALBAR, "Keypad+Bar"},
|
||||
{SDLK_KP_DBLVERTICALBAR, "Keypad+BarDbl"},
|
||||
{SDLK_KP_COLON, "Keypad+Colon"},
|
||||
{SDLK_KP_HASH, "Keypad+Hash"},
|
||||
{SDLK_KP_SPACE, "Keypad+Space"},
|
||||
{SDLK_KP_AT, "Keypad+At"},
|
||||
{SDLK_KP_EXCLAM, "Keypad+Exclam"},
|
||||
{SDLK_KP_MEMSTORE, "Keypad+MemStore"},
|
||||
{SDLK_KP_MEMRECALL, "Keypad+MemRecall"},
|
||||
{SDLK_KP_MEMCLEAR, "Keypad+MemClear"},
|
||||
{SDLK_KP_MEMADD, "Keypad+MemAdd"},
|
||||
{SDLK_KP_MEMSUBTRACT, "Keypad+MemSubtract"},
|
||||
{SDLK_KP_MEMMULTIPLY, "Keypad+MemMultiply"},
|
||||
{SDLK_KP_MEMDIVIDE, "Keypad+MemDivide"},
|
||||
{SDLK_KP_PLUSMINUS, "Keypad+PlusMinus"},
|
||||
{SDLK_KP_CLEAR, "Keypad+Clear"},
|
||||
{SDLK_KP_CLEARENTRY, "Keypad+ClearEntry"},
|
||||
{SDLK_KP_BINARY, "Keypad+Binary"},
|
||||
{SDLK_KP_OCTAL, "Keypad+Octal"},
|
||||
{SDLK_KP_DECIMAL, "Keypad+Decimal"},
|
||||
{SDLK_KP_HEXADECIMAL, "Keypad+Hexadecimal"},
|
||||
{SDLK_LCTRL, "LeftControl"},
|
||||
{SDLK_LSHIFT, "LeftShift"},
|
||||
{SDLK_LALT, "LeftAlt"},
|
||||
{SDLK_LGUI, "Super_L"},
|
||||
{SDLK_RCTRL, "RightCtrl"},
|
||||
{SDLK_RSHIFT, "RightShift"},
|
||||
{SDLK_RALT, "RightAlt"},
|
||||
{SDLK_RGUI, "RightSuper"},
|
||||
{SDLK_MODE, "Mode"},
|
||||
{SDLK_AUDIONEXT, "MediaNext"},
|
||||
{SDLK_AUDIOPREV, "MediaPrevious"},
|
||||
{SDLK_AUDIOSTOP, "MediaStop"},
|
||||
{SDLK_AUDIOPLAY, "MediaPlay"},
|
||||
{SDLK_AUDIOMUTE, "VolumeMute"},
|
||||
{SDLK_MEDIASELECT, "MediaSelect"},
|
||||
{SDLK_WWW, "WWW"},
|
||||
{SDLK_MAIL, "Mail"},
|
||||
{SDLK_CALCULATOR, "Calculator"},
|
||||
{SDLK_COMPUTER, "Computer"},
|
||||
{SDLK_AC_SEARCH, "Search"},
|
||||
{SDLK_AC_HOME, "Home"},
|
||||
{SDLK_AC_BACK, "Back"},
|
||||
{SDLK_AC_FORWARD, "Forward"},
|
||||
{SDLK_AC_STOP, "Stop"},
|
||||
{SDLK_AC_REFRESH, "Refresh"},
|
||||
{SDLK_AC_BOOKMARKS, "Bookmarks"},
|
||||
{SDLK_BRIGHTNESSDOWN, "BrightnessDown"},
|
||||
{SDLK_BRIGHTNESSUP, "BrightnessUp"},
|
||||
{SDLK_DISPLAYSWITCH, "DisplaySwitch"},
|
||||
{SDLK_KBDILLUMTOGGLE, "IllumToggle"},
|
||||
{SDLK_KBDILLUMDOWN, "IllumDown"},
|
||||
{SDLK_KBDILLUMUP, "IllumUp"},
|
||||
{SDLK_EJECT, "Eject"},
|
||||
{SDLK_SLEEP, "Sleep"},
|
||||
{SDLK_APP1, "App1"},
|
||||
{SDLK_APP2, "App2"},
|
||||
{SDLK_AUDIOREWIND, "MediaRewind"},
|
||||
{SDLK_AUDIOFASTFORWARD, "MediaFastForward"}};
|
||||
|
||||
static const char* GetKeyName(DWORD key)
|
||||
{
|
||||
const auto it = s_sdl_key_names.find(key);
|
||||
return it == s_sdl_key_names.end() ? nullptr : it->second;
|
||||
}
|
||||
|
||||
static std::optional<DWORD> GetKeyCodeForName(const std::string_view& key_name)
|
||||
{
|
||||
for (const auto& it : s_sdl_key_names)
|
||||
{
|
||||
if (key_name == it.second)
|
||||
return it.first;
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
} // namespace SDLKeyNames
|
449
src/duckstation-nogui/sdl_nogui_platform.cpp
Normal file
449
src/duckstation-nogui/sdl_nogui_platform.cpp
Normal file
|
@ -0,0 +1,449 @@
|
|||
// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com>
|
||||
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
|
||||
|
||||
#include "sdl_nogui_platform.h"
|
||||
#include "nogui_host.h"
|
||||
#include "sdl_key_names.h"
|
||||
|
||||
#include "core/host.h"
|
||||
|
||||
#include "util/imgui_manager.h"
|
||||
#include "util/sdl_input_source.h"
|
||||
|
||||
#include "common/log.h"
|
||||
#include "common/scoped_guard.h"
|
||||
#include "common/string_util.h"
|
||||
#include "common/threading.h"
|
||||
|
||||
#include <SDL.h>
|
||||
#include <SDL_syswm.h>
|
||||
|
||||
Log_SetChannel(SDLNoGUIPlatform);
|
||||
|
||||
static constexpr float DEFAULT_WINDOW_DPI = 96.0f;
|
||||
|
||||
SDLNoGUIPlatform::SDLNoGUIPlatform()
|
||||
{
|
||||
m_message_loop_running.store(true, std::memory_order_release);
|
||||
}
|
||||
|
||||
SDLNoGUIPlatform::~SDLNoGUIPlatform()
|
||||
{
|
||||
SDL_QuitSubSystem(SDL_INIT_VIDEO | SDL_INIT_EVENTS);
|
||||
}
|
||||
|
||||
bool SDLNoGUIPlatform::Initialize()
|
||||
{
|
||||
if (SDL_InitSubSystem(SDL_INIT_VIDEO | SDL_INIT_EVENTS) != 0)
|
||||
{
|
||||
Log_ErrorFmt("SDL_InitSubSystem() failed: {}", SDL_GetError());
|
||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error",
|
||||
TinyString::from_format("SDL_InitSubSystem() failed: {}", SDL_GetError()), nullptr);
|
||||
return false;
|
||||
}
|
||||
|
||||
m_func_event_id = SDL_RegisterEvents(1);
|
||||
m_wakeup_event_id = SDL_RegisterEvents(1);
|
||||
if (m_func_event_id == static_cast<u32>(-1) || m_wakeup_event_id == static_cast<u32>(-1))
|
||||
{
|
||||
Log_ErrorFmt("SDL_RegisterEvents() failed: {}", SDL_GetError());
|
||||
return false;
|
||||
}
|
||||
|
||||
// prevent input source polling on main thread...
|
||||
SDLInputSource::ALLOW_EVENT_POLLING = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SDLNoGUIPlatform::ReportError(const std::string_view& title, const std::string_view& message)
|
||||
{
|
||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, SmallString(title).c_str(), SmallString(message).c_str(), m_window);
|
||||
}
|
||||
|
||||
bool SDLNoGUIPlatform::ConfirmMessage(const std::string_view& title, const std::string_view& message)
|
||||
{
|
||||
const SmallString title_copy(title);
|
||||
const SmallString message_copy(message);
|
||||
|
||||
static constexpr SDL_MessageBoxButtonData bd[2] = {
|
||||
{SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT, 1, "Yes"},
|
||||
{SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT, 2, "No"},
|
||||
};
|
||||
const SDL_MessageBoxData md = {SDL_MESSAGEBOX_INFORMATION,
|
||||
m_window,
|
||||
title_copy.c_str(),
|
||||
message_copy.c_str(),
|
||||
static_cast<int>(std::size(bd)),
|
||||
bd,
|
||||
nullptr};
|
||||
|
||||
int buttonid = -1;
|
||||
SDL_ShowMessageBox(&md, &buttonid);
|
||||
return (buttonid == 1);
|
||||
}
|
||||
|
||||
void SDLNoGUIPlatform::SetDefaultConfig(SettingsInterface& si)
|
||||
{
|
||||
// noop
|
||||
}
|
||||
|
||||
bool SDLNoGUIPlatform::CreatePlatformWindow(std::string title)
|
||||
{
|
||||
s32 window_x, window_y, window_width, window_height;
|
||||
if (!NoGUIHost::GetSavedPlatformWindowGeometry(&window_x, &window_y, &window_width, &window_height))
|
||||
{
|
||||
window_x = SDL_WINDOWPOS_UNDEFINED;
|
||||
window_y = SDL_WINDOWPOS_UNDEFINED;
|
||||
window_width = DEFAULT_WINDOW_WIDTH;
|
||||
window_height = DEFAULT_WINDOW_HEIGHT;
|
||||
}
|
||||
|
||||
m_window = SDL_CreateWindow(title.c_str(), window_x, window_y, window_width, window_height,
|
||||
SDL_WINDOW_RESIZABLE | SDL_WINDOW_INPUT_FOCUS | SDL_WINDOW_MOUSE_FOCUS |
|
||||
SDL_WINDOW_SHOWN | SDL_WINDOW_ALLOW_HIGHDPI);
|
||||
if (!m_window)
|
||||
{
|
||||
Log_ErrorFmt("SDL_CreateWindow() failed: {}", SDL_GetError());
|
||||
ReportError("Error", TinyString::from_format("SDL_CreateWindow() failed: {}", SDL_GetError()));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_fullscreen.load(std::memory_order_acquire))
|
||||
SetFullscreen(true);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SDLNoGUIPlatform::HasPlatformWindow() const
|
||||
{
|
||||
return (m_window != nullptr);
|
||||
}
|
||||
|
||||
void SDLNoGUIPlatform::DestroyPlatformWindow()
|
||||
{
|
||||
if (!m_window)
|
||||
return;
|
||||
|
||||
if (!m_fullscreen.load(std::memory_order_acquire))
|
||||
{
|
||||
int window_x = SDL_WINDOWPOS_UNDEFINED, window_y = SDL_WINDOWPOS_UNDEFINED;
|
||||
int window_width = DEFAULT_WINDOW_WIDTH, window_height = DEFAULT_WINDOW_HEIGHT;
|
||||
SDL_GetWindowPosition(m_window, &window_x, &window_y);
|
||||
SDL_GetWindowSize(m_window, &window_width, &window_height);
|
||||
NoGUIHost::SavePlatformWindowGeometry(window_x, window_y, window_width, window_height);
|
||||
}
|
||||
|
||||
SDL_DestroyWindow(m_window);
|
||||
m_window = nullptr;
|
||||
}
|
||||
|
||||
std::optional<WindowInfo> SDLNoGUIPlatform::GetPlatformWindowInfo()
|
||||
{
|
||||
if (!m_window)
|
||||
return std::nullopt;
|
||||
|
||||
SDL_SysWMinfo swi = {};
|
||||
SDL_VERSION(&swi.version);
|
||||
|
||||
if (!SDL_GetWindowWMInfo(m_window, &swi))
|
||||
{
|
||||
Log_ErrorFmt("SDL_GetWindowWMInfo() failed: {}", SDL_GetError());
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
int window_width = 1, window_height = 1;
|
||||
int window_px_width = 1, window_px_height = 1;
|
||||
SDL_GetWindowSize(m_window, &window_width, &window_height);
|
||||
SDL_GetWindowSizeInPixels(m_window, &window_px_width, &window_px_height);
|
||||
m_window_scale = static_cast<float>(std::max(window_px_width, 1)) / static_cast<float>(std::max(window_width, 1));
|
||||
|
||||
if (const int display_index = SDL_GetWindowDisplayIndex(m_window); display_index >= 0)
|
||||
{
|
||||
float ddpi, hdpi, vdpi;
|
||||
if (SDL_GetDisplayDPI(display_index, &ddpi, &hdpi, &vdpi) == 0)
|
||||
m_window_scale = std::max(ddpi / DEFAULT_WINDOW_DPI, 0.5f);
|
||||
}
|
||||
|
||||
WindowInfo wi;
|
||||
wi.surface_width = static_cast<u32>(window_px_width);
|
||||
wi.surface_height = static_cast<u32>(window_px_height);
|
||||
wi.surface_scale = m_window_scale;
|
||||
|
||||
switch (swi.subsystem)
|
||||
{
|
||||
#ifdef SDL_VIDEO_DRIVER_WINDOWS
|
||||
case SDL_SYSWM_WINDOWS:
|
||||
wi.type = WindowInfo::Type::Win32;
|
||||
wi.window_handle = swi.info.win.window;
|
||||
break;
|
||||
#endif
|
||||
|
||||
#ifdef SDL_VIDEO_DRIVER_X11
|
||||
case SDL_SYSWM_X11:
|
||||
wi.type = WindowInfo::Type::X11;
|
||||
wi.display_connection = swi.info.x11.display;
|
||||
wi.window_handle = swi.info.x11.window;
|
||||
break;
|
||||
#endif
|
||||
|
||||
#ifdef SDL_VIDEO_DRIVER_WAYLAND
|
||||
case SDL_SYSWM_WAYLAND:
|
||||
wi.type = WindowInfo::Type::Wayland;
|
||||
wi.display_connection = swi.info.wl.display;
|
||||
wi.window_handle = swi.info.wl.surface;
|
||||
break;
|
||||
#endif
|
||||
|
||||
#ifdef SDL_VIDEO_DRIVER_COCOA
|
||||
case SDL_SYSWM_COCOA:
|
||||
wi.type = WindowInfo::Type::MacOS;
|
||||
wi.window_handle = swi.info.cocoa.window;
|
||||
break;
|
||||
#endif
|
||||
|
||||
default:
|
||||
Log_ErrorFmt("Unhandled WM subsystem {}", static_cast<int>(swi.subsystem));
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
return wi;
|
||||
}
|
||||
|
||||
void SDLNoGUIPlatform::SetPlatformWindowTitle(std::string title)
|
||||
{
|
||||
if (!m_window)
|
||||
return;
|
||||
|
||||
SDL_SetWindowTitle(m_window, title.c_str());
|
||||
}
|
||||
|
||||
std::optional<u32> SDLNoGUIPlatform::ConvertHostKeyboardStringToCode(const std::string_view& str)
|
||||
{
|
||||
std::optional<DWORD> converted(SDLKeyNames::GetKeyCodeForName(str));
|
||||
return converted.has_value() ? std::optional<u32>(static_cast<u32>(converted.value())) : std::nullopt;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<std::string> SDLNoGUIPlatform::ConvertHostKeyboardCodeToString(u32 code)
|
||||
{
|
||||
const char* converted = SDLKeyNames::GetKeyName(code);
|
||||
return converted ? std::optional<std::string>(converted) : std::nullopt;
|
||||
}
|
||||
|
||||
void SDLNoGUIPlatform::RunMessageLoop()
|
||||
{
|
||||
while (m_message_loop_running.load(std::memory_order_acquire))
|
||||
{
|
||||
SDL_Event ev;
|
||||
if (!SDL_WaitEvent(&ev))
|
||||
continue;
|
||||
|
||||
ProcessEvent(&ev);
|
||||
}
|
||||
}
|
||||
|
||||
void SDLNoGUIPlatform::ExecuteInMessageLoop(std::function<void()> func)
|
||||
{
|
||||
std::function<void()>* pfunc = new std::function<void()>(std::move(func));
|
||||
|
||||
SDL_Event ev;
|
||||
ev.user = {};
|
||||
ev.type = m_func_event_id;
|
||||
ev.user.data1 = pfunc;
|
||||
SDL_PushEvent(&ev);
|
||||
}
|
||||
|
||||
void SDLNoGUIPlatform::QuitMessageLoop()
|
||||
{
|
||||
m_message_loop_running.store(false, std::memory_order_release);
|
||||
|
||||
SDL_Event ev;
|
||||
ev.user = {};
|
||||
ev.type = m_wakeup_event_id;
|
||||
SDL_PushEvent(&ev);
|
||||
}
|
||||
|
||||
void SDLNoGUIPlatform::SetFullscreen(bool enabled)
|
||||
{
|
||||
if (!m_window || m_fullscreen.load(std::memory_order_acquire) == enabled)
|
||||
return;
|
||||
|
||||
if (SDL_SetWindowFullscreen(m_window, enabled ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0) != 0)
|
||||
{
|
||||
Log_ErrorFmt("SDL_SetWindowFullscreen() failed: {}", SDL_GetError());
|
||||
return;
|
||||
}
|
||||
|
||||
m_fullscreen.store(enabled, std::memory_order_release);
|
||||
}
|
||||
|
||||
bool SDLNoGUIPlatform::RequestRenderWindowSize(s32 new_window_width, s32 new_window_height)
|
||||
{
|
||||
if (!m_window || m_fullscreen.load(std::memory_order_acquire))
|
||||
return false;
|
||||
|
||||
SDL_SetWindowSize(m_window, new_window_width, new_window_height);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SDLNoGUIPlatform::OpenURL(const std::string_view& url)
|
||||
{
|
||||
if (SDL_OpenURL(SmallString(url).c_str()) != 0)
|
||||
{
|
||||
Log_ErrorFmt("SDL_OpenURL() failed: {}", SDL_GetError());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SDLNoGUIPlatform::CopyTextToClipboard(const std::string_view& text)
|
||||
{
|
||||
if (SDL_SetClipboardText(SmallString(text).c_str()) != 0)
|
||||
{
|
||||
Log_ErrorFmt("SDL_SetClipboardText() failed: {}", SDL_GetError());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SDLNoGUIPlatform::ProcessEvent(const SDL_Event* ev)
|
||||
{
|
||||
switch (ev->type)
|
||||
{
|
||||
case SDL_WINDOWEVENT:
|
||||
{
|
||||
switch (ev->window.event)
|
||||
{
|
||||
case SDL_WINDOWEVENT_SIZE_CHANGED:
|
||||
{
|
||||
int window_width = ev->window.data1, window_height = ev->window.data2;
|
||||
SDL_GetWindowSizeInPixels(m_window, &window_width, &window_height);
|
||||
NoGUIHost::ProcessPlatformWindowResize(window_width, window_height, m_window_scale);
|
||||
}
|
||||
break;
|
||||
|
||||
case SDL_WINDOWEVENT_DISPLAY_CHANGED:
|
||||
{
|
||||
const int new_display = ev->window.data1;
|
||||
float ddpi, hdpi, vdpi;
|
||||
if (SDL_GetDisplayDPI(new_display, &ddpi, &hdpi, &vdpi) == 0)
|
||||
{
|
||||
if (const float new_scale = std::max(ddpi / DEFAULT_WINDOW_DPI, 0.5f); new_scale != m_window_scale)
|
||||
{
|
||||
m_window_scale = new_scale;
|
||||
|
||||
int window_width = 1, window_height = 1;
|
||||
SDL_GetWindowSizeInPixels(m_window, &window_width, &window_height);
|
||||
NoGUIHost::ProcessPlatformWindowResize(window_width, window_height, m_window_scale);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case SDL_WINDOWEVENT_CLOSE:
|
||||
{
|
||||
Host::RunOnCPUThread([]() { Host::RequestExit(false); });
|
||||
}
|
||||
break;
|
||||
|
||||
case SDL_WINDOWEVENT_FOCUS_GAINED:
|
||||
{
|
||||
NoGUIHost::PlatformWindowFocusGained();
|
||||
}
|
||||
break;
|
||||
|
||||
case SDL_WINDOWEVENT_FOCUS_LOST:
|
||||
{
|
||||
NoGUIHost::PlatformWindowFocusLost();
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case SDL_KEYDOWN:
|
||||
case SDL_KEYUP:
|
||||
{
|
||||
const bool pressed = (ev->type == SDL_KEYDOWN);
|
||||
NoGUIHost::ProcessPlatformKeyEvent(static_cast<s32>(ev->key.keysym.sym), pressed);
|
||||
}
|
||||
break;
|
||||
|
||||
case SDL_TEXTINPUT:
|
||||
{
|
||||
if (ImGuiManager::WantsTextInput())
|
||||
NoGUIHost::ProcessPlatformTextEvent(ev->text.text);
|
||||
}
|
||||
break;
|
||||
|
||||
case SDL_MOUSEMOTION:
|
||||
{
|
||||
const float x = static_cast<float>(ev->motion.x);
|
||||
const float y = static_cast<float>(ev->motion.y);
|
||||
NoGUIHost::ProcessPlatformMouseMoveEvent(x, y);
|
||||
}
|
||||
break;
|
||||
|
||||
case SDL_MOUSEBUTTONDOWN:
|
||||
case SDL_MOUSEBUTTONUP:
|
||||
{
|
||||
const bool pressed = (ev->type == SDL_MOUSEBUTTONDOWN);
|
||||
if (ev->button.button > 0)
|
||||
NoGUIHost::ProcessPlatformMouseButtonEvent(ev->button.button - 1, pressed);
|
||||
}
|
||||
break;
|
||||
|
||||
case SDL_MOUSEWHEEL:
|
||||
{
|
||||
NoGUIHost::ProcessPlatformMouseWheelEvent(ev->wheel.preciseX, ev->wheel.preciseY);
|
||||
}
|
||||
break;
|
||||
|
||||
case SDL_QUIT:
|
||||
{
|
||||
Host::RunOnCPUThread([]() { Host::RequestExit(false); });
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
{
|
||||
if (ev->type == m_func_event_id)
|
||||
{
|
||||
std::function<void()>* pfunc = reinterpret_cast<std::function<void()>*>(ev->user.data1);
|
||||
if (pfunc)
|
||||
{
|
||||
(*pfunc)();
|
||||
delete pfunc;
|
||||
}
|
||||
}
|
||||
else if (ev->type == m_wakeup_event_id)
|
||||
{
|
||||
}
|
||||
else if (SDLInputSource::IsHandledInputEvent(ev) && InputManager::GetInputSourceInterface(InputSourceType::SDL))
|
||||
{
|
||||
Host::RunOnCPUThread([event_copy = *ev]() {
|
||||
SDLInputSource* is =
|
||||
static_cast<SDLInputSource*>(InputManager::GetInputSourceInterface(InputSourceType::SDL));
|
||||
if (is) [[likely]]
|
||||
is->ProcessSDLEvent(&event_copy);
|
||||
});
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<NoGUIPlatform> NoGUIPlatform::CreateSDLPlatform()
|
||||
{
|
||||
std::unique_ptr<SDLNoGUIPlatform> ret(new SDLNoGUIPlatform());
|
||||
if (!ret->Initialize())
|
||||
return {};
|
||||
|
||||
return ret;
|
||||
}
|
57
src/duckstation-nogui/sdl_nogui_platform.h
Normal file
57
src/duckstation-nogui/sdl_nogui_platform.h
Normal file
|
@ -0,0 +1,57 @@
|
|||
// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com>
|
||||
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
|
||||
#include "common/windows_headers.h"
|
||||
|
||||
#include "nogui_platform.h"
|
||||
|
||||
#include <SDL.h>
|
||||
|
||||
class SDLNoGUIPlatform : public NoGUIPlatform
|
||||
{
|
||||
public:
|
||||
SDLNoGUIPlatform();
|
||||
~SDLNoGUIPlatform();
|
||||
|
||||
bool Initialize();
|
||||
|
||||
void ReportError(const std::string_view& title, const std::string_view& message) override;
|
||||
bool ConfirmMessage(const std::string_view& title, const std::string_view& message) override;
|
||||
|
||||
void SetDefaultConfig(SettingsInterface& si) override;
|
||||
|
||||
bool CreatePlatformWindow(std::string title) override;
|
||||
bool HasPlatformWindow() const override;
|
||||
void DestroyPlatformWindow() override;
|
||||
std::optional<WindowInfo> GetPlatformWindowInfo() override;
|
||||
void SetPlatformWindowTitle(std::string title) override;
|
||||
|
||||
std::optional<u32> ConvertHostKeyboardStringToCode(const std::string_view& str) override;
|
||||
std::optional<std::string> ConvertHostKeyboardCodeToString(u32 code) override;
|
||||
|
||||
void RunMessageLoop() override;
|
||||
void ExecuteInMessageLoop(std::function<void()> func) override;
|
||||
void QuitMessageLoop() override;
|
||||
|
||||
void SetFullscreen(bool enabled) override;
|
||||
|
||||
bool RequestRenderWindowSize(s32 new_window_width, s32 new_window_height) override;
|
||||
|
||||
bool OpenURL(const std::string_view& url) override;
|
||||
bool CopyTextToClipboard(const std::string_view& text) override;
|
||||
|
||||
private:
|
||||
void ProcessEvent(const SDL_Event* ev);
|
||||
|
||||
SDL_Window* m_window = nullptr;
|
||||
float m_window_scale = 1.0f;
|
||||
u32 m_func_event_id = 0;
|
||||
u32 m_wakeup_event_id = 0;
|
||||
|
||||
std::atomic_bool m_message_loop_running{false};
|
||||
std::atomic_bool m_fullscreen{false};
|
||||
};
|
|
@ -1,6 +1,11 @@
|
|||
// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com>
|
||||
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/types.h"
|
||||
#include "common/windows_headers.h"
|
||||
|
||||
#include <array>
|
||||
#include <cstring>
|
||||
#include <map>
|
||||
|
|
|
@ -151,6 +151,8 @@ static void SDLLogCallback(void* userdata, int category, SDL_LogPriority priorit
|
|||
Log::Write("SDL", "SDL", priority_map[priority], message);
|
||||
}
|
||||
|
||||
bool SDLInputSource::ALLOW_EVENT_POLLING = true;
|
||||
|
||||
SDLInputSource::SDLInputSource() = default;
|
||||
|
||||
SDLInputSource::~SDLInputSource()
|
||||
|
@ -322,6 +324,9 @@ void SDLInputSource::ShutdownSubsystem()
|
|||
|
||||
void SDLInputSource::PollEvents()
|
||||
{
|
||||
if (!ALLOW_EVENT_POLLING)
|
||||
return;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
SDL_Event ev;
|
||||
|
@ -548,6 +553,28 @@ TinyString SDLInputSource::ConvertKeyToIcon(InputBindingKey key)
|
|||
return ret;
|
||||
}
|
||||
|
||||
bool SDLInputSource::IsHandledInputEvent(const SDL_Event* ev)
|
||||
{
|
||||
switch (ev->type)
|
||||
{
|
||||
case SDL_CONTROLLERDEVICEADDED:
|
||||
case SDL_CONTROLLERDEVICEREMOVED:
|
||||
case SDL_JOYDEVICEADDED:
|
||||
case SDL_JOYDEVICEREMOVED:
|
||||
case SDL_CONTROLLERAXISMOTION:
|
||||
case SDL_CONTROLLERBUTTONDOWN:
|
||||
case SDL_CONTROLLERBUTTONUP:
|
||||
case SDL_JOYAXISMOTION:
|
||||
case SDL_JOYBUTTONDOWN:
|
||||
case SDL_JOYBUTTONUP:
|
||||
case SDL_JOYHATMOTION:
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool SDLInputSource::ProcessSDLEvent(const SDL_Event* event)
|
||||
{
|
||||
switch (event->type)
|
||||
|
@ -859,7 +886,7 @@ bool SDLInputSource::HandleJoystickButtonEvent(const SDL_JoyButtonEvent* ev)
|
|||
if (it == m_controllers.end())
|
||||
return false;
|
||||
if (ev->button < it->joy_button_used_in_gc.size() && it->joy_button_used_in_gc[ev->button])
|
||||
return false; // Will get handled by GC event
|
||||
return false; // Will get handled by GC event
|
||||
const u32 button =
|
||||
ev->button + static_cast<u32>(std::size(s_sdl_button_names)); // Ensure we don't conflict with GC buttons
|
||||
const InputBindingKey key(MakeGenericControllerButtonKey(InputSourceType::SDL, it->player_id, button));
|
||||
|
|
|
@ -46,6 +46,10 @@ public:
|
|||
static u32 GetRGBForPlayerId(SettingsInterface& si, u32 player_id);
|
||||
static u32 ParseRGBForPlayerId(const std::string_view& str, u32 player_id);
|
||||
|
||||
static bool IsHandledInputEvent(const SDL_Event* ev);
|
||||
|
||||
static bool ALLOW_EVENT_POLLING;
|
||||
|
||||
private:
|
||||
struct ControllerData
|
||||
{
|
||||
|
|
Loading…
Reference in a new issue