SDL3 update (#787)

* Update to SDL3

* Add XTest development dependency for SDL3

* Preserve native entry points with SDL3

* Document SDL3 XTest dependency
This commit is contained in:
nmzik
2026-09-23 15:22:15 +02:00
committed by GitHub
parent b5ba482fc2
commit f68c77d785
22 changed files with 469 additions and 554 deletions
+70 -67
View File
@@ -1,6 +1,7 @@
#include "graphics/presentation/systemOverlay.h"
#include "SDL.h"
#include <SDL3/SDL.h>
#include "common/assert.h"
#include "common/stringUtils.h"
#include "graphics/host_gpu/graphicContext.h"
@@ -176,6 +177,7 @@ bool g_input_multiline = false;
bool g_input_lifecycle_active = false;
bool g_controller_captured = false;
OverlaySession g_session;
SDL_Window* g_input_window = nullptr;
void ClearInputEvents() {
std::scoped_lock lock(g_input_mutex);
@@ -212,7 +214,7 @@ void RetryVisibilityWakeup() {
}
SDL_Event event {};
event.type = type;
if (SDL_PushEvent(&event) > 0) {
if (SDL_PushEvent(&event)) {
g_missing_visibility_wakeups--;
}
}
@@ -252,7 +254,7 @@ void RefreshVisibility() {
std::scoped_lock input_lock(g_input_mutex);
g_visibility_updates.push_back({snapshot.session, visible, capture_controller,
capture_keyboard, text_input, multiline});
if (g_missing_visibility_wakeups != 0 || SDL_PushEvent(&event) <= 0) {
if (g_missing_visibility_wakeups != 0 || !SDL_PushEvent(&event)) {
g_missing_visibility_wakeups++;
}
}
@@ -268,16 +270,16 @@ void OnDialogVisibilityChanged(bool, uint64_t) {
uint32_t ExternalKeyStatus(SDL_Keymod modifiers, bool character_valid) {
uint32_t status = 0x00000001 | (character_valid ? 0x00000002 : 0);
if ((modifiers & KMOD_LCTRL) != 0) status |= 0x00000100;
if ((modifiers & KMOD_LSHIFT) != 0) status |= 0x00000200;
if ((modifiers & KMOD_LALT) != 0) status |= 0x00000400;
if ((modifiers & KMOD_LGUI) != 0) status |= 0x00000800;
if ((modifiers & KMOD_RCTRL) != 0) status |= 0x00001000;
if ((modifiers & KMOD_RSHIFT) != 0) status |= 0x00002000;
if ((modifiers & KMOD_RALT) != 0) status |= 0x00004000;
if ((modifiers & KMOD_RGUI) != 0) status |= 0x00008000;
if ((modifiers & KMOD_NUM) != 0) status |= 0x00010000;
if ((modifiers & KMOD_CAPS) != 0) status |= 0x00020000;
if ((modifiers & SDL_KMOD_LCTRL) != 0) status |= 0x00000100;
if ((modifiers & SDL_KMOD_LSHIFT) != 0) status |= 0x00000200;
if ((modifiers & SDL_KMOD_LALT) != 0) status |= 0x00000400;
if ((modifiers & SDL_KMOD_LGUI) != 0) status |= 0x00000800;
if ((modifiers & SDL_KMOD_RCTRL) != 0) status |= 0x00001000;
if ((modifiers & SDL_KMOD_RSHIFT) != 0) status |= 0x00002000;
if ((modifiers & SDL_KMOD_RALT) != 0) status |= 0x00004000;
if ((modifiers & SDL_KMOD_RGUI) != 0) status |= 0x00008000;
if ((modifiers & SDL_KMOD_NUM) != 0) status |= 0x00010000;
if ((modifiers & SDL_KMOD_CAPS) != 0) status |= 0x00020000;
return status;
}
@@ -392,14 +394,14 @@ float AlignmentPivot(Ime::Alignment alignment) {
ImGuiKey ControllerButtonToKey(int button) {
switch (button) {
case SDL_CONTROLLER_BUTTON_A: return ImGuiKey_GamepadFaceDown;
case SDL_CONTROLLER_BUTTON_B: return ImGuiKey_GamepadFaceRight;
case SDL_CONTROLLER_BUTTON_X: return ImGuiKey_GamepadFaceLeft;
case SDL_CONTROLLER_BUTTON_Y: return ImGuiKey_GamepadFaceUp;
case SDL_CONTROLLER_BUTTON_DPAD_LEFT: return ImGuiKey_GamepadDpadLeft;
case SDL_CONTROLLER_BUTTON_DPAD_RIGHT: return ImGuiKey_GamepadDpadRight;
case SDL_CONTROLLER_BUTTON_DPAD_UP: return ImGuiKey_GamepadDpadUp;
case SDL_CONTROLLER_BUTTON_DPAD_DOWN: return ImGuiKey_GamepadDpadDown;
case SDL_GAMEPAD_BUTTON_SOUTH: return ImGuiKey_GamepadFaceDown;
case SDL_GAMEPAD_BUTTON_EAST: return ImGuiKey_GamepadFaceRight;
case SDL_GAMEPAD_BUTTON_WEST: return ImGuiKey_GamepadFaceLeft;
case SDL_GAMEPAD_BUTTON_NORTH: return ImGuiKey_GamepadFaceUp;
case SDL_GAMEPAD_BUTTON_DPAD_LEFT: return ImGuiKey_GamepadDpadLeft;
case SDL_GAMEPAD_BUTTON_DPAD_RIGHT: return ImGuiKey_GamepadDpadRight;
case SDL_GAMEPAD_BUTTON_DPAD_UP: return ImGuiKey_GamepadDpadUp;
case SDL_GAMEPAD_BUTTON_DPAD_DOWN: return ImGuiKey_GamepadDpadDown;
default: return ImGuiKey_None;
}
}
@@ -415,9 +417,11 @@ void CheckVulkanResult(VkResult result) {
} // namespace
void InitializeSystemOverlayInput() {
void InitializeSystemOverlayInput(SDL_Window* window) {
EXIT_IF(window == nullptr);
const Uint32 type = SDL_RegisterEvents(1);
EXIT_IF(type == static_cast<Uint32>(-1));
g_input_window = window;
{
std::scoped_lock lock(g_visibility_mutex);
g_input_lifecycle_active = true;
@@ -453,9 +457,10 @@ void ShutdownSystemOverlayInput() {
g_input_controller = false;
g_input_keyboard = false;
g_input_multiline = false;
if (SDL_IsTextInputActive() == SDL_TRUE) {
SDL_StopTextInput();
if (g_input_window != nullptr && SDL_TextInputActive(g_input_window)) {
SDL_StopTextInput(g_input_window);
}
g_input_window = nullptr;
}
SystemOverlayVisualState GetSystemOverlayVisualState() noexcept {
@@ -487,13 +492,13 @@ bool ProcessSystemOverlayInput(const SDL_Event& event) {
g_input_keyboard = update.capture_keyboard;
g_input_multiline = update.multiline;
if (update.text_input) {
SDL_StartTextInput();
} else if (SDL_IsTextInputActive() == SDL_TRUE) {
SDL_StopTextInput();
SDL_StartTextInput(g_input_window);
} else if (SDL_TextInputActive(g_input_window)) {
SDL_StopTextInput(g_input_window);
}
return true;
}
if (event.type == SDL_CONTROLLERDEVICEREMOVED) {
if (event.type == SDL_EVENT_GAMEPAD_REMOVED) {
if (g_input_active && g_input_controller) {
QueueInput({InputKind::ResetController, g_input_session, 0, 0.0f, 0.0f});
}
@@ -505,26 +510,26 @@ bool ProcessSystemOverlayInput(const SDL_Event& event) {
const auto session = g_input_session;
const uint64_t generation = session.generation;
const bool controller_event = event.type == SDL_CONTROLLERBUTTONDOWN ||
event.type == SDL_CONTROLLERBUTTONUP ||
event.type == SDL_CONTROLLERAXISMOTION;
const bool controller_event = event.type == SDL_EVENT_GAMEPAD_BUTTON_DOWN ||
event.type == SDL_EVENT_GAMEPAD_BUTTON_UP ||
event.type == SDL_EVENT_GAMEPAD_AXIS_MOTION;
if (controller_event && !g_input_controller) {
return false;
}
const bool keyboard_event = event.type == SDL_TEXTINPUT || event.type == SDL_TEXTEDITING ||
event.type == SDL_KEYDOWN || event.type == SDL_KEYUP;
const bool keyboard_event = event.type == SDL_EVENT_TEXT_INPUT || event.type == SDL_EVENT_TEXT_EDITING ||
event.type == SDL_EVENT_KEY_DOWN || event.type == SDL_EVENT_KEY_UP;
if (keyboard_event && !g_input_keyboard) {
return false;
}
if (keyboard_event && session.kind == OverlayKind::Error) {
if (event.type == SDL_KEYDOWN && event.key.repeat == 0 &&
(event.key.keysym.sym == SDLK_RETURN || event.key.keysym.sym == SDLK_KP_ENTER)) {
if (event.type == SDL_EVENT_KEY_DOWN && !event.key.repeat &&
(event.key.key == SDLK_RETURN || event.key.key == SDLK_KP_ENTER)) {
ErrorDialog::HostAccept(generation);
}
return true;
}
switch (event.type) {
case SDL_TEXTINPUT: {
case SDL_EVENT_TEXT_INPUT: {
const auto text = Utf8ToUtf16(event.text.text);
if (!text.empty()) {
auto input = MakeExternalInput(Ime::ExternalAction::Text, g_last_external_keycode,
@@ -535,26 +540,24 @@ bool ProcessSystemOverlayInput(const SDL_Event& event) {
}
return true;
}
case SDL_TEXTEDITING: return true;
case SDL_KEYDOWN: {
g_last_external_keycode = static_cast<uint16_t>(event.key.keysym.scancode);
g_last_external_status =
ExternalKeyStatus(static_cast<SDL_Keymod>(event.key.keysym.mod), false);
case SDL_EVENT_TEXT_EDITING: return true;
case SDL_EVENT_KEY_DOWN: {
g_last_external_keycode = static_cast<uint16_t>(event.key.scancode);
g_last_external_status = ExternalKeyStatus(event.key.mod, false);
auto action = Ime::ExternalAction::Text;
bool queue = true;
if (event.key.keysym.sym == SDLK_BACKSPACE) {
if (event.key.key == SDLK_BACKSPACE) {
action = Ime::ExternalAction::Backspace;
} else if (event.key.keysym.sym == SDLK_LEFT) {
} else if (event.key.key == SDLK_LEFT) {
action = Ime::ExternalAction::MoveLeft;
} else if (event.key.keysym.sym == SDLK_RIGHT) {
} else if (event.key.key == SDLK_RIGHT) {
action = Ime::ExternalAction::MoveRight;
} else if (event.key.keysym.sym == SDLK_ESCAPE) {
} else if (event.key.key == SDLK_ESCAPE) {
action = Ime::ExternalAction::Cancel;
} else if (event.key.keysym.sym == SDLK_RETURN ||
event.key.keysym.sym == SDLK_KP_ENTER) {
} else if (event.key.key == SDLK_RETURN || event.key.key == SDLK_KP_ENTER) {
action =
g_input_multiline ? Ime::ExternalAction::Newline : Ime::ExternalAction::Accept;
} else if (event.key.keysym.sym == SDLK_TAB) {
} else if (event.key.key == SDLK_TAB) {
action = Ime::ExternalAction::None;
} else {
queue = false;
@@ -566,28 +569,28 @@ bool ProcessSystemOverlayInput(const SDL_Event& event) {
}
return true;
}
case SDL_KEYUP: return true;
case SDL_CONTROLLERBUTTONDOWN:
case SDL_CONTROLLERBUTTONUP:
QueueInput({InputKind::Button, session, event.cbutton.button,
event.type == SDL_CONTROLLERBUTTONDOWN ? 1.0f : 0.0f, 0.0f});
case SDL_EVENT_KEY_UP: return true;
case SDL_EVENT_GAMEPAD_BUTTON_DOWN:
case SDL_EVENT_GAMEPAD_BUTTON_UP:
QueueInput({InputKind::Button, session, event.gbutton.button,
event.type == SDL_EVENT_GAMEPAD_BUTTON_DOWN ? 1.0f : 0.0f, 0.0f});
return true;
case SDL_CONTROLLERAXISMOTION:
QueueInput({InputKind::Axis, session, event.caxis.axis,
static_cast<float>(event.caxis.value), 0.0f});
case SDL_EVENT_GAMEPAD_AXIS_MOTION:
QueueInput({InputKind::Axis, session, event.gaxis.axis,
static_cast<float>(event.gaxis.value), 0.0f});
return true;
case SDL_MOUSEMOTION:
case SDL_EVENT_MOUSE_MOTION:
QueueInput({InputKind::MousePosition, session, 0, static_cast<float>(event.motion.x),
static_cast<float>(event.motion.y)});
return true;
case SDL_MOUSEBUTTONDOWN:
case SDL_MOUSEBUTTONUP:
case SDL_EVENT_MOUSE_BUTTON_DOWN:
case SDL_EVENT_MOUSE_BUTTON_UP:
QueueInput({InputKind::MousePosition, session, 0, static_cast<float>(event.button.x),
static_cast<float>(event.button.y)});
QueueInput({InputKind::MouseButton, session, event.button.button,
event.type == SDL_MOUSEBUTTONDOWN ? 1.0f : 0.0f, 0.0f});
event.type == SDL_EVENT_MOUSE_BUTTON_DOWN ? 1.0f : 0.0f, 0.0f});
return true;
case SDL_MOUSEWHEEL:
case SDL_EVENT_MOUSE_WHEEL:
QueueInput({InputKind::MouseWheel, session, 0, static_cast<float>(event.wheel.x),
static_cast<float>(event.wheel.y)});
return true;
@@ -681,9 +684,9 @@ struct SystemOverlay::Impl {
case InputKind::Button: {
const bool down = event.x != 0.0f;
if (session.kind == OverlayKind::Ime && down) {
if (event.id == SDL_CONTROLLER_BUTTON_B) {
if (event.id == SDL_GAMEPAD_BUTTON_EAST) {
Ime::HostCancel(session.generation);
} else if (event.id == SDL_CONTROLLER_BUTTON_Y) {
} else if (event.id == SDL_GAMEPAD_BUTTON_NORTH) {
Ime::HostBackspace(session.generation);
}
}
@@ -695,19 +698,19 @@ struct SystemOverlay::Impl {
}
case InputKind::Axis: {
const float value = event.x / (event.x < 0.0f ? 32768.0f : 32767.0f);
if (event.id == SDL_CONTROLLER_AXIS_LEFTX) {
if (event.id == SDL_GAMEPAD_AXIS_LEFTX) {
io.AddKeyAnalogEvent(ImGuiKey_GamepadLStickLeft, value < -0.25f,
std::max(-value, 0.0f));
io.AddKeyAnalogEvent(ImGuiKey_GamepadLStickRight, value > 0.25f,
std::max(value, 0.0f));
} else if (event.id == SDL_CONTROLLER_AXIS_LEFTY) {
} else if (event.id == SDL_GAMEPAD_AXIS_LEFTY) {
io.AddKeyAnalogEvent(ImGuiKey_GamepadLStickUp, value < -0.25f,
std::max(-value, 0.0f));
io.AddKeyAnalogEvent(ImGuiKey_GamepadLStickDown, value > 0.25f,
std::max(value, 0.0f));
} else if (event.id == SDL_CONTROLLER_AXIS_RIGHTX) {
} else if (event.id == SDL_GAMEPAD_AXIS_RIGHTX) {
right_stick.x = std::abs(value) > 0.2f ? value : 0.0f;
} else if (event.id == SDL_CONTROLLER_AXIS_RIGHTY) {
} else if (event.id == SDL_GAMEPAD_AXIS_RIGHTY) {
right_stick.y = std::abs(value) > 0.2f ? value : 0.0f;
}
break;
+2 -1
View File
@@ -7,6 +7,7 @@
#include <memory>
union SDL_Event;
struct SDL_Window;
namespace Libs::Graphics {
@@ -17,7 +18,7 @@ struct SystemOverlayVisualState {
uint64_t revision;
};
void InitializeSystemOverlayInput();
void InitializeSystemOverlayInput(SDL_Window* window);
void ShutdownSystemOverlayInput();
bool ProcessSystemOverlayInput(const SDL_Event& event);
SystemOverlayVisualState GetSystemOverlayVisualState() noexcept;
+69 -62
View File
@@ -1,12 +1,7 @@
#include "graphics/presentation/window/hostInput.h"
#include "SDL_error.h"
#include "SDL_events.h"
#include "SDL_keyboard.h"
#include "SDL_keycode.h"
#include "SDL_mouse.h"
#include "SDL_timer.h"
#include "SDL_video.h"
#include <SDL3/SDL.h>
#include "common/assert.h"
#include "common/emulatorConfig.h"
#include "common/logging/log.h"
@@ -80,6 +75,7 @@ struct MouseJoystickState {
};
MouseJoystickState g_mouse;
SDL_Window* g_mouse_window = nullptr;
std::size_t ControlFromName(std::string_view name) {
const auto info = std::find_if(CONTROL_INFO.begin(), CONTROL_INFO.end(),
@@ -213,14 +209,14 @@ uint32_t DefaultKeyboardButton(int key_code) {
case SDLK_LEFT: return Controller::PAD_BUTTON_LEFT;
case SDLK_DOWN: return Controller::PAD_BUTTON_DOWN;
case SDLK_RIGHT: return Controller::PAD_BUTTON_RIGHT;
case SDLK_j: return Controller::PAD_BUTTON_CROSS;
case SDLK_i: return Controller::PAD_BUTTON_TRIANGLE;
case SDLK_k: return Controller::PAD_BUTTON_SQUARE;
case SDLK_l: return Controller::PAD_BUTTON_CIRCLE;
case SDLK_q: return Controller::PAD_BUTTON_L1;
case SDLK_e: return Controller::PAD_BUTTON_R1;
case SDLK_z: return Controller::PAD_BUTTON_L2;
case SDLK_c: return Controller::PAD_BUTTON_R2;
case SDLK_J: return Controller::PAD_BUTTON_CROSS;
case SDLK_I: return Controller::PAD_BUTTON_TRIANGLE;
case SDLK_K: return Controller::PAD_BUTTON_SQUARE;
case SDLK_L: return Controller::PAD_BUTTON_CIRCLE;
case SDLK_Q: return Controller::PAD_BUTTON_L1;
case SDLK_E: return Controller::PAD_BUTTON_R1;
case SDLK_Z: return Controller::PAD_BUTTON_L2;
case SDLK_C: return Controller::PAD_BUTTON_R2;
case SDLK_LSHIFT: return Controller::PAD_BUTTON_L3;
case SDLK_LCTRL: return Controller::PAD_BUTTON_R3;
case SDLK_RETURN:
@@ -284,35 +280,35 @@ void DefaultKeyboardInput(int key_code, bool down) {
switch (NormalizeKey(static_cast<SDL_Keycode>(key_code))) {
case SDLK_BACKSPACE: SetTouchPad(0.25f, down); return;
case SDLK_TAB: SetTouchPad(0.75f, down); return;
case SDLK_a:
case SDLK_A:
left.left = down;
SetStickAxis(Controller::Axis::LeftX, left.left, left.right);
return;
case SDLK_d:
case SDLK_D:
left.right = down;
SetStickAxis(Controller::Axis::LeftX, left.left, left.right);
return;
case SDLK_w:
case SDLK_W:
left.up = down;
SetStickAxis(Controller::Axis::LeftY, left.up, left.down);
return;
case SDLK_s:
case SDLK_S:
left.down = down;
SetStickAxis(Controller::Axis::LeftY, left.up, left.down);
return;
case SDLK_f:
case SDLK_F:
right.left = down;
SetStickAxis(Controller::Axis::RightX, right.left, right.right);
return;
case SDLK_h:
case SDLK_H:
right.right = down;
SetStickAxis(Controller::Axis::RightX, right.left, right.right);
return;
case SDLK_t:
case SDLK_T:
right.up = down;
SetStickAxis(Controller::Axis::RightY, right.up, right.down);
return;
case SDLK_g:
case SDLK_G:
right.down = down;
SetStickAxis(Controller::Axis::RightY, right.up, right.down);
return;
@@ -320,11 +316,11 @@ void DefaultKeyboardInput(int key_code, bool down) {
}
}
void MouseToJoystick(int delta_x, int delta_y) {
void MouseToJoystick(float delta_x, float delta_y) {
const double distance = std::hypot(delta_x, delta_y);
const double scale =
std::clamp(distance * GetInputMap().MouseSensitivity() + 16.0, 64.0, 128.0) / distance;
const auto map_axis = [scale](int delta) {
const auto map_axis = [scale](float delta) {
return std::clamp(128 + static_cast<int>(std::lround(delta * scale)), 0, 255);
};
Controller::SetRightStick(Controller::HOST_INPUT_CONTROLLER_ID, map_axis(delta_x),
@@ -340,16 +336,45 @@ void CenterMouseStick() {
}
bool SetRelativeMouseMode(bool enabled) {
if (SDL_SetRelativeMouseMode(enabled ? SDL_TRUE : SDL_FALSE) == 0) {
if (SDL_SetWindowRelativeMouseMode(g_mouse_window, enabled)) {
return true;
}
LOGF("Mouse-to-joystick relative mode failed: %s\n", SDL_GetError());
return false;
}
int PollMouse(uint64_t now_ms) {
if (now_ms < g_mouse.next_poll) {
return static_cast<int>(g_mouse.next_poll - now_ms);
}
g_mouse.next_poll = now_ms + MOUSE_POLL_INTERVAL_MS;
float delta_x = 0;
float delta_y = 0;
SDL_GetRelativeMouseState(&delta_x, &delta_y);
if (delta_x == 0 && delta_y == 0) {
CenterMouseStick();
} else {
MouseToJoystick(delta_x, delta_y);
g_mouse.output = true;
}
return MOUSE_POLL_INTERVAL_MS;
}
} // namespace
void HostInputInit() {
void HostInputInit(SDL_Window* window) {
GetInputMap();
g_mouse_window = window;
}
void HostInputShutdown() {
if (g_mouse.enabled) {
SetRelativeMouseMode(false);
CenterMouseStick();
g_mouse = {};
}
g_mouse_window = nullptr;
}
void HostInputKey(int key_code, bool down) {
@@ -380,51 +405,33 @@ void HostInputToggleMouseToJoystick() {
if (!SetRelativeMouseMode(true)) {
return;
}
int ignored_x = 0;
int ignored_y = 0;
SDL_GetRelativeMouseState(&ignored_x, &ignored_y);
g_mouse.enabled = true;
g_mouse.next_poll = SDL_GetTicks64() + MOUSE_POLL_INTERVAL_MS;
g_mouse.enabled = true;
LOGF("Mouse to right stick: enabled (F7 to release)\n");
}
int PollMouse(uint64_t now_ms) {
if (now_ms < g_mouse.next_poll) {
return static_cast<int>(g_mouse.next_poll - now_ms);
}
g_mouse.next_poll = now_ms + MOUSE_POLL_INTERVAL_MS;
int delta_x = 0;
int delta_y = 0;
SDL_GetRelativeMouseState(&delta_x, &delta_y);
if (delta_x == 0 && delta_y == 0) {
CenterMouseStick();
return MOUSE_POLL_INTERVAL_MS;
}
MouseToJoystick(delta_x, delta_y);
g_mouse.output = true;
return MOUSE_POLL_INTERVAL_MS;
}
bool HostInputWaitEvent(SDL_Event* event) {
if (!g_mouse.enabled || SDL_GetKeyboardFocus() == nullptr) {
bool has_event;
if (!g_mouse.enabled || SDL_GetKeyboardFocus() != g_mouse_window) {
g_mouse.next_poll = 0;
CenterMouseStick();
if (SDL_WaitEvent(event) == 0) {
has_event = SDL_WaitEvent(event);
if (!has_event) {
EXIT("%s\n", SDL_GetError());
}
return true;
} else {
if (g_mouse.next_poll == 0) {
SDL_GetRelativeMouseState(nullptr, nullptr);
g_mouse.next_poll = SDL_GetTicks() + MOUSE_POLL_INTERVAL_MS;
}
has_event = SDL_WaitEventTimeout(event, PollMouse(SDL_GetTicks()));
}
const int timeout_ms = PollMouse(SDL_GetTicks64());
SDL_ClearError();
if (SDL_WaitEventTimeout(event, timeout_ms) != 0) {
return true;
if (has_event && event->type == SDL_EVENT_WINDOW_FOCUS_LOST &&
event->window.windowID == SDL_GetWindowID(g_mouse_window)) {
g_mouse.next_poll = 0;
CenterMouseStick();
}
if (SDL_GetError()[0] != '\0') {
EXIT("%s\n", SDL_GetError());
}
return false;
return has_event;
}
} // namespace Libs::Graphics
+3 -1
View File
@@ -3,11 +3,13 @@
#include <cstdint>
struct SDL_Window;
union SDL_Event;
namespace Libs::Graphics {
void HostInputInit();
void HostInputInit(SDL_Window* window);
void HostInputShutdown();
void HostInputKey(int key_code, bool down);
void HostInputMouseButton(uint8_t mouse_button, bool down);
void HostInputToggleMouseToJoystick();
@@ -532,7 +532,9 @@ void Swapchain::Recreate(bool surface_lost) {
#if defined(__APPLE__)
// Surface recreation goes through SDL_Vulkan_CreateSurface, which touches the
// window's view/layer and must run on the main thread on macOS.
m_window.RunOnMainThread([this] { m_window.RecreateSurface(); });
EXIT_IF(!SDL_RunOnMainThread(
[](void* window) { static_cast<WindowContext*>(window)->RecreateSurface(); },
&m_window, true));
#else
m_window.RecreateSurface();
#endif
@@ -1,20 +1,6 @@
#include "SDL.h"
#include "SDL_error.h"
#include "SDL_events.h"
#include "SDL_gamecontroller.h"
#include "SDL_hints.h"
#include "SDL_joystick.h"
#include "SDL_keyboard.h"
#include "SDL_keycode.h"
#include "SDL_mouse.h"
#include "SDL_pixels.h"
#include "SDL_rwops.h"
#include "SDL_stdinc.h"
#include "SDL_surface.h"
#include "SDL_thread.h"
#include "SDL_touch.h"
#include "SDL_video.h"
#include "SDL_vulkan.h"
#include <SDL3/SDL.h>
#include <SDL3/SDL_vulkan.h>
#include "common/assert.h"
#include "common/common.h"
#include "common/emulatorConfig.h"
@@ -700,25 +686,13 @@ static vk::Device VulkanCreateDevice(GraphicContext& graphics,
return device;
}
static void VulkanGetExtensions(SDL_Window* window, VulkanExtensions& r) {
EXIT_IF(window == nullptr);
static void VulkanGetExtensions(VulkanExtensions& r) {
uint32_t required_extensions_count = 0;
auto sdl_result = SDL_Vulkan_GetInstanceExtensions(window, &required_extensions_count, nullptr);
EXIT_NOT_IMPLEMENTED(sdl_result == SDL_FALSE);
const char* const* extensions = SDL_Vulkan_GetInstanceExtensions(&required_extensions_count);
EXIT_NOT_IMPLEMENTED(extensions == nullptr);
EXIT_NOT_IMPLEMENTED(required_extensions_count == 0);
r.required_extensions =
std::vector<const char*>(required_extensions_count); // @suppress("Ambiguous problem")
sdl_result = SDL_Vulkan_GetInstanceExtensions(window, &required_extensions_count,
r.required_extensions.data());
EXIT_NOT_IMPLEMENTED(sdl_result == SDL_FALSE);
EXIT_NOT_IMPLEMENTED(required_extensions_count == 0);
EXIT_NOT_IMPLEMENTED(required_extensions_count != r.required_extensions.size());
r.required_extensions.assign(extensions, extensions + required_extensions_count);
r.available_extensions =
EnumerateVulkan<vk::ExtensionProperties>( // @suppress("Ambiguous problem")
@@ -899,7 +873,7 @@ void WindowContext::CreateVulkan() {
VULKAN_HPP_DEFAULT_DISPATCHER.init(get_instance_proc_addr);
VulkanExtensions r;
VulkanGetExtensions(window, r);
VulkanGetExtensions(r);
VulkanCheckInstanceVersion();
vk::ApplicationInfo app_info {};
@@ -986,8 +960,8 @@ void WindowContext::CreateVulkan() {
}
vk::SurfaceKHR::CType native_surface = VK_NULL_HANDLE;
if (SDL_Vulkan_CreateSurface(window, static_cast<vk::Instance::CType>(graphic_ctx.instance),
&native_surface) == SDL_FALSE) {
if (!SDL_Vulkan_CreateSurface(window, static_cast<vk::Instance::CType>(graphic_ctx.instance),
nullptr, &native_surface)) {
EXIT("Could not create a Vulkan surface");
}
surface = native_surface;
@@ -1100,8 +1074,8 @@ void WindowContext::RecreateSurface() {
surface = nullptr;
}
vk::SurfaceKHR::CType native_surface = VK_NULL_HANDLE;
if (SDL_Vulkan_CreateSurface(window, static_cast<vk::Instance::CType>(graphic_ctx.instance),
&native_surface) == SDL_FALSE) {
if (!SDL_Vulkan_CreateSurface(window, static_cast<vk::Instance::CType>(graphic_ctx.instance),
nullptr, &native_surface)) {
EXIT("Could not recreate the Vulkan surface: %s\n", SDL_GetError());
}
surface = native_surface;
@@ -1137,7 +1111,7 @@ WindowContext::~WindowContext() {
SDL_DestroyWindow(window);
window = nullptr;
}
SDL_QuitSubSystem(SDL_INIT_VIDEO | SDL_INIT_GAMECONTROLLER);
SDL_QuitSubSystem(SDL_INIT_VIDEO | SDL_INIT_GAMEPAD);
}
} // namespace Libs::Graphics
+159 -232
View File
@@ -1,23 +1,7 @@
#include "graphics/presentation/window.h"
#include "SDL.h"
#include "SDL_error.h"
#include "SDL_events.h"
#include "SDL_filesystem.h"
#include "SDL_gamecontroller.h"
#include "SDL_hints.h"
#include "SDL_joystick.h"
#include "SDL_keyboard.h"
#include "SDL_keycode.h"
#include "SDL_mouse.h"
#include "SDL_pixels.h"
#include "SDL_rwops.h"
#include "SDL_stdinc.h"
#include "SDL_surface.h"
#include "SDL_thread.h"
#include "SDL_touch.h"
#include "SDL_video.h"
#include "SDL_vulkan.h"
#include <SDL3/SDL.h>
#include "common/assert.h"
#include "common/common.h"
#include "common/emulatorConfig.h"
@@ -72,39 +56,39 @@ struct EventKeyboard {
static uint32_t ControllerButtonToPadButton(int button) {
switch (button) {
case SDL_CONTROLLER_BUTTON_A: return Controller::PAD_BUTTON_CROSS;
case SDL_CONTROLLER_BUTTON_B: return Controller::PAD_BUTTON_CIRCLE;
case SDL_CONTROLLER_BUTTON_X: return Controller::PAD_BUTTON_SQUARE;
case SDL_CONTROLLER_BUTTON_Y: return Controller::PAD_BUTTON_TRIANGLE;
case SDL_CONTROLLER_BUTTON_START: return Controller::PAD_BUTTON_OPTIONS;
case SDL_CONTROLLER_BUTTON_LEFTSTICK: return Controller::PAD_BUTTON_L3;
case SDL_CONTROLLER_BUTTON_RIGHTSTICK: return Controller::PAD_BUTTON_R3;
case SDL_CONTROLLER_BUTTON_LEFTSHOULDER: return Controller::PAD_BUTTON_L1;
case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER: return Controller::PAD_BUTTON_R1;
case SDL_CONTROLLER_BUTTON_DPAD_UP: return Controller::PAD_BUTTON_UP;
case SDL_CONTROLLER_BUTTON_DPAD_DOWN: return Controller::PAD_BUTTON_DOWN;
case SDL_CONTROLLER_BUTTON_DPAD_LEFT: return Controller::PAD_BUTTON_LEFT;
case SDL_CONTROLLER_BUTTON_DPAD_RIGHT: return Controller::PAD_BUTTON_RIGHT;
case SDL_CONTROLLER_BUTTON_TOUCHPAD: return Controller::PAD_BUTTON_TOUCH_PAD;
case SDL_GAMEPAD_BUTTON_SOUTH: return Controller::PAD_BUTTON_CROSS;
case SDL_GAMEPAD_BUTTON_EAST: return Controller::PAD_BUTTON_CIRCLE;
case SDL_GAMEPAD_BUTTON_WEST: return Controller::PAD_BUTTON_SQUARE;
case SDL_GAMEPAD_BUTTON_NORTH: return Controller::PAD_BUTTON_TRIANGLE;
case SDL_GAMEPAD_BUTTON_START: return Controller::PAD_BUTTON_OPTIONS;
case SDL_GAMEPAD_BUTTON_LEFT_STICK: return Controller::PAD_BUTTON_L3;
case SDL_GAMEPAD_BUTTON_RIGHT_STICK: return Controller::PAD_BUTTON_R3;
case SDL_GAMEPAD_BUTTON_LEFT_SHOULDER: return Controller::PAD_BUTTON_L1;
case SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER: return Controller::PAD_BUTTON_R1;
case SDL_GAMEPAD_BUTTON_DPAD_UP: return Controller::PAD_BUTTON_UP;
case SDL_GAMEPAD_BUTTON_DPAD_DOWN: return Controller::PAD_BUTTON_DOWN;
case SDL_GAMEPAD_BUTTON_DPAD_LEFT: return Controller::PAD_BUTTON_LEFT;
case SDL_GAMEPAD_BUTTON_DPAD_RIGHT: return Controller::PAD_BUTTON_RIGHT;
case SDL_GAMEPAD_BUTTON_TOUCHPAD: return Controller::PAD_BUTTON_TOUCH_PAD;
default: return 0;
}
}
static Controller::Axis ControllerAxisFromSdl(int axis_id) {
switch (axis_id) {
case SDL_CONTROLLER_AXIS_LEFTX: return Controller::Axis::LeftX;
case SDL_CONTROLLER_AXIS_LEFTY: return Controller::Axis::LeftY;
case SDL_CONTROLLER_AXIS_RIGHTX: return Controller::Axis::RightX;
case SDL_CONTROLLER_AXIS_RIGHTY: return Controller::Axis::RightY;
case SDL_CONTROLLER_AXIS_TRIGGERLEFT: return Controller::Axis::TriggerLeft;
case SDL_CONTROLLER_AXIS_TRIGGERRIGHT: return Controller::Axis::TriggerRight;
case SDL_GAMEPAD_AXIS_LEFTX: return Controller::Axis::LeftX;
case SDL_GAMEPAD_AXIS_LEFTY: return Controller::Axis::LeftY;
case SDL_GAMEPAD_AXIS_RIGHTX: return Controller::Axis::RightX;
case SDL_GAMEPAD_AXIS_RIGHTY: return Controller::Axis::RightY;
case SDL_GAMEPAD_AXIS_LEFT_TRIGGER: return Controller::Axis::TriggerLeft;
case SDL_GAMEPAD_AXIS_RIGHT_TRIGGER: return Controller::Axis::TriggerRight;
default: return Controller::Axis::AxisMax;
}
}
static bool ControllerAxisIsTrigger(int axis_id) {
return axis_id == SDL_CONTROLLER_AXIS_TRIGGERLEFT ||
axis_id == SDL_CONTROLLER_AXIS_TRIGGERRIGHT;
return axis_id == SDL_GAMEPAD_AXIS_LEFT_TRIGGER ||
axis_id == SDL_GAMEPAD_AXIS_RIGHT_TRIGGER;
}
static int ControllerAxisValueFromSdl(int axis_id, int axis_value) {
@@ -165,22 +149,6 @@ struct EventController {
double timestamp_seconds;
};
enum class DisplayOrientation {
Unknown, /* The display orientation can't be determined */
Landscape, /* The display is in landscape mode, with the right side up, relative to portrait
mode */
LandscapeFlipped, /* The display is in landscape mode, with the left side up, relative to
portrait mode */
Portrait, /* The display is in portrait mode */
PortraitFlipped, /* The display is in portrait mode, upside down */
DisplayEventOrientation = 0xF0
};
struct EventDisplay {
DisplayOrientation orientation;
};
namespace {
std::unique_ptr<WindowContext> g_window;
@@ -213,11 +181,8 @@ static void ToggleDesktopFullscreen() {
}
const auto flags = static_cast<uint32_t>(SDL_GetWindowFlags(g_window->window));
const bool fullscreen =
(flags & static_cast<uint32_t>(SDL_WINDOW_FULLSCREEN_DESKTOP)) != 0u;
const auto mode =
fullscreen ? 0u : static_cast<uint32_t>(SDL_WINDOW_FULLSCREEN_DESKTOP);
if (SDL_SetWindowFullscreen(g_window->window, mode) != 0) {
const bool fullscreen = (flags & SDL_WINDOW_FULLSCREEN) != 0u;
if (!SDL_SetWindowFullscreen(g_window->window, !fullscreen)) {
LOGF("Toggle fullscreen failed: %s\n", SDL_GetError());
}
}
@@ -247,7 +212,7 @@ static void GameEventKeyboard(const EventKeyboard& key) {
break;
case SDLK_RETURN:
case SDLK_KP_ENTER:
if (!key.repeat && (key.mod & KMOD_ALT) != 0) {
if (!key.repeat && (key.mod & SDL_KMOD_ALT) != 0) {
ToggleDesktopFullscreen();
fullscreen_key = key.key_code;
}
@@ -340,15 +305,15 @@ static void GameEventController([[maybe_unused]] const EventController& f) {
#endif
if (f.added) {
auto* pad = SDL_GameControllerOpen(f.id);
auto* pad = SDL_OpenGamepad(f.id);
EXIT_NOT_IMPLEMENTED(pad == nullptr);
int id = SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(pad));
int id = SDL_GetJoystickID(SDL_GetGamepadJoystick(pad));
Controller::Connect(id);
}
if (f.removed) {
Controller::Disconnect(f.id);
SDL_GameControllerClose(SDL_GameControllerFromInstanceID(f.id));
SDL_CloseGamepad(SDL_GetGamepadFromID(f.id));
}
if (f.down || f.up) {
@@ -400,81 +365,73 @@ void WindowContext::Resize(uint32_t new_width, uint32_t new_height) {
void WindowContext::ProcessWindowEvent(const SDL_WindowEvent& event) {
const auto& window_event = event;
switch (window_event.event) {
case SDL_WINDOWEVENT_SHOWN:
switch (window_event.type) {
case SDL_EVENT_WINDOW_SHOWN:
LOGF("Window %" PRIu32 " shown\n", window_event.windowID);
break;
case SDL_WINDOWEVENT_HIDDEN:
case SDL_EVENT_WINDOW_HIDDEN:
LOGF("Window %" PRIu32 " hidden\n", window_event.windowID);
break;
case SDL_WINDOWEVENT_EXPOSED:
case SDL_EVENT_WINDOW_EXPOSED:
LOGF("Window %" PRIu32 " exposed\n", window_event.windowID);
break;
case SDL_WINDOWEVENT_MOVED:
case SDL_EVENT_WINDOW_MOVED:
LOGF("Window %" PRIu32 " moved to %" PRId32 ",%" PRId32 "\n", window_event.windowID,
window_event.data1, window_event.data2);
break;
case SDL_WINDOWEVENT_RESIZED:
case SDL_EVENT_WINDOW_RESIZED:
LOGF("Window %" PRIu32 " resized to %" PRId32 "x%" PRId32 "\n", window_event.windowID,
window_event.data1, window_event.data2);
LOGF("m: %d\n", static_cast<int>(SDL_ThreadID()));
Resize(window_event.data1, window_event.data2);
break;
case SDL_WINDOWEVENT_SIZE_CHANGED:
case SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED:
LOGF("Window %" PRIu32 " size changed to %" PRId32 "x%" PRId32 "\n",
window_event.windowID, window_event.data1, window_event.data2);
LOGF("m: %d\n", static_cast<int>(SDL_ThreadID()));
Resize(window_event.data1, window_event.data2);
break;
case SDL_WINDOWEVENT_MINIMIZED:
case SDL_EVENT_WINDOW_MINIMIZED:
LOGF("Window %" PRIu32 " minimized\n", window_event.windowID);
break;
case SDL_WINDOWEVENT_MAXIMIZED:
case SDL_EVENT_WINDOW_MAXIMIZED:
LOGF("Window %" PRIu32 " maximized\n", window_event.windowID);
break;
case SDL_WINDOWEVENT_RESTORED:
case SDL_EVENT_WINDOW_RESTORED:
LOGF("Window %" PRIu32 " restored\n", window_event.windowID);
break;
case SDL_WINDOWEVENT_ENTER:
case SDL_EVENT_WINDOW_MOUSE_ENTER:
LOGF("Mouse entered window %" PRIu32 "\n", window_event.windowID);
break;
case SDL_WINDOWEVENT_LEAVE:
case SDL_EVENT_WINDOW_MOUSE_LEAVE:
LOGF("Mouse left window %" PRIu32 "\n", window_event.windowID);
break;
case SDL_WINDOWEVENT_FOCUS_GAINED:
case SDL_EVENT_WINDOW_FOCUS_GAINED:
LOGF("Window %" PRIu32 " gained keyboard focus\n", window_event.windowID);
break;
case SDL_WINDOWEVENT_FOCUS_LOST:
case SDL_EVENT_WINDOW_FOCUS_LOST:
LOGF("Window %" PRIu32 " lost keyboard focus\n", window_event.windowID);
break;
case SDL_WINDOWEVENT_CLOSE:
case SDL_EVENT_WINDOW_CLOSE_REQUESTED:
LOGF("Window %" PRIu32 " closed\n", window_event.windowID);
break;
default:
LOGF("Window %" PRIu32 " got unknown event %" PRIu8 "\n", window_event.windowID,
window_event.event);
LOGF("Window %" PRIu32 " got unknown event %" PRIu32 "\n", window_event.windowID,
static_cast<uint32_t>(window_event.type));
break;
}
}
void WindowContext::ProcessDisplayEvent(const SDL_DisplayEvent& display) {
bool sdl = false;
switch (display.event) {
case SDL_DISPLAYEVENT_ORIENTATION: sdl = true; [[fallthrough]];
case static_cast<Uint8>(DisplayOrientation::DisplayEventOrientation): {
LOGF("Display %" PRIu32 "[%s] changed orientation to %d - ", display.display,
sdl ? "SDL" : "Kyty", static_cast<int>(display.data1));
switch (display.type) {
case SDL_EVENT_DISPLAY_ORIENTATION: {
LOGF("Display %" PRIu32 " changed orientation to %d - ", display.displayID,
static_cast<int>(display.data1));
switch (display.data1) {
case SDL_ORIENTATION_UNKNOWN: LOGF("UNKNOWN\n"); break;
@@ -488,8 +445,8 @@ void WindowContext::ProcessDisplayEvent(const SDL_DisplayEvent& display) {
break;
}
default:
LOGF("Display %" PRIu32 " got unknown event 0x%" PRIx8 "\n", display.display,
display.event);
LOGF("Display %" PRIu32 " got unknown event 0x%" PRIx32 "\n", display.displayID,
static_cast<uint32_t>(display.type));
break;
}
}
@@ -497,10 +454,9 @@ void WindowContext::ProcessDisplayEvent(const SDL_DisplayEvent& display) {
void WindowContext::ProcessEvent(double time_s) {
auto& game = loop;
auto* event = &game.event;
EXIT_IF(SDL_GetEventState(SDL_DISPLAYEVENT) != SDL_ENABLE);
if ((event->type == SDL_KEYDOWN || event->type == SDL_KEYUP) &&
event->key.keysym.sym == SDLK_F7) {
if (event->type == SDL_KEYDOWN && event->key.repeat == 0) {
if ((event->type == SDL_EVENT_KEY_DOWN || event->type == SDL_EVENT_KEY_UP) &&
event->key.key == SDLK_F7) {
if (event->type == SDL_EVENT_KEY_DOWN && event->key.repeat == 0) {
HostInputToggleMouseToJoystick();
}
return;
@@ -508,34 +464,42 @@ void WindowContext::ProcessEvent(double time_s) {
if (ProcessSystemOverlayInput(*event)) {
return;
}
if (event->type >= SDL_EVENT_WINDOW_FIRST && event->type <= SDL_EVENT_WINDOW_LAST) {
ProcessWindowEvent(event->window);
return;
}
if (event->type >= SDL_EVENT_DISPLAY_FIRST && event->type <= SDL_EVENT_DISPLAY_LAST) {
ProcessDisplayEvent(event->display);
return;
}
switch (event->type) {
case SDL_QUIT: GameEventQuit(game); break;
case SDL_EVENT_QUIT: GameEventQuit(game); break;
case SDL_APP_TERMINATING: GameEventTerminate(game); break;
case SDL_EVENT_TERMINATING: GameEventTerminate(game); break;
case SDL_APP_LOWMEMORY: GameEventLowMemory(); break;
case SDL_EVENT_LOW_MEMORY: GameEventLowMemory(); break;
case SDL_APP_WILLENTERBACKGROUND: GameEventWillEnterBackground(game); break;
case SDL_EVENT_WILL_ENTER_BACKGROUND: GameEventWillEnterBackground(game); break;
case SDL_APP_DIDENTERBACKGROUND: GameEventDidEnterBackground(); break;
case SDL_EVENT_DID_ENTER_BACKGROUND: GameEventDidEnterBackground(); break;
case SDL_APP_WILLENTERFOREGROUND: GameEventWillEnterForeground(); break;
case SDL_EVENT_WILL_ENTER_FOREGROUND: GameEventWillEnterForeground(); break;
case SDL_APP_DIDENTERFOREGROUND: GameEventDidEnterForeground(game); break;
case SDL_EVENT_DID_ENTER_FOREGROUND: GameEventDidEnterForeground(game); break;
case SDL_KEYDOWN:
case SDL_KEYUP: {
case SDL_EVENT_KEY_DOWN:
case SDL_EVENT_KEY_UP: {
EventKeyboard key {};
key.down = (event->type == SDL_KEYDOWN);
key.up = (event->type == SDL_KEYUP);
key.pressed = (event->key.state == SDL_PRESSED);
key.released = (event->key.state == SDL_RELEASED);
key.down = (event->type == SDL_EVENT_KEY_DOWN);
key.up = (event->type == SDL_EVENT_KEY_UP);
key.pressed = event->key.down;
key.released = !event->key.down;
key.repeat = (event->key.repeat != 0u);
key.scan_code = event->key.keysym.scancode;
key.key_code = event->key.keysym.sym;
key.mod = event->key.keysym.mod;
key.scan_code = event->key.scancode;
key.key_code = event->key.key;
key.mod = event->key.mod;
key.timestamp_seconds = time_s;
GameEventKeyboard(key);
@@ -543,24 +507,20 @@ void WindowContext::ProcessEvent(double time_s) {
break;
}
case SDL_WINDOWEVENT: ProcessWindowEvent(event->window); break;
case SDL_DISPLAYEVENT: ProcessDisplayEvent(event->display); break;
case SDL_MOUSEBUTTONDOWN:
case SDL_MOUSEBUTTONUP: {
case SDL_EVENT_MOUSE_BUTTON_DOWN:
case SDL_EVENT_MOUSE_BUTTON_UP: {
EventMouse mb {};
mb.down = (event->button.type == SDL_MOUSEBUTTONDOWN);
mb.up = (event->button.type == SDL_MOUSEBUTTONUP);
mb.down = (event->button.type == SDL_EVENT_MOUSE_BUTTON_DOWN);
mb.up = (event->button.type == SDL_EVENT_MOUSE_BUTTON_UP);
mb.left = (event->button.button == SDL_BUTTON_LEFT);
mb.middle = (event->button.button == SDL_BUTTON_MIDDLE);
mb.right = (event->button.button == SDL_BUTTON_RIGHT);
mb.x1 = (event->button.button == SDL_BUTTON_X1);
mb.x2 = (event->button.button == SDL_BUTTON_X2);
mb.touch = (event->button.which == SDL_TOUCH_MOUSEID);
mb.pressed = (event->button.state == SDL_PRESSED);
mb.released = (event->button.state == SDL_RELEASED);
mb.pressed = event->button.down;
mb.released = !event->button.down;
mb.num_of_clicks = event->button.clicks;
mb.wheel = false;
mb.x = event->button.x;
@@ -575,7 +535,7 @@ void WindowContext::ProcessEvent(double time_s) {
break;
}
case SDL_MOUSEWHEEL: {
case SDL_EVENT_MOUSE_WHEEL: {
EventMouse mb {};
mb.down = false;
@@ -602,7 +562,7 @@ void WindowContext::ProcessEvent(double time_s) {
break;
}
case SDL_MOUSEMOTION: {
case SDL_EVENT_MOUSE_MOTION: {
EventMouse mb {};
mb.down = false;
@@ -629,16 +589,16 @@ void WindowContext::ProcessEvent(double time_s) {
break;
}
case SDL_FINGERMOTION:
case SDL_FINGERDOWN:
case SDL_FINGERUP: {
case SDL_EVENT_FINGER_MOTION:
case SDL_EVENT_FINGER_DOWN:
case SDL_EVENT_FINGER_UP: {
EventFinger f {};
f.down = (event->tfinger.type == SDL_FINGERDOWN);
f.up = (event->tfinger.type == SDL_FINGERUP);
f.motion = (event->tfinger.type == SDL_FINGERMOTION);
f.finger_id = static_cast<int>(event->tfinger.fingerId);
f.touch_id = static_cast<int>(event->tfinger.touchId);
f.down = (event->tfinger.type == SDL_EVENT_FINGER_DOWN);
f.up = (event->tfinger.type == SDL_EVENT_FINGER_UP);
f.motion = (event->tfinger.type == SDL_EVENT_FINGER_MOTION);
f.finger_id = static_cast<int>(event->tfinger.fingerID);
f.touch_id = static_cast<int>(event->tfinger.touchID);
f.x = event->tfinger.x;
f.y = event->tfinger.y;
f.dx = event->tfinger.dx;
@@ -651,13 +611,13 @@ void WindowContext::ProcessEvent(double time_s) {
break;
}
case SDL_CONTROLLERAXISMOTION: {
case SDL_EVENT_GAMEPAD_AXIS_MOTION: {
EventController c {};
c.id = event->caxis.which;
c.button = SDL_CONTROLLER_BUTTON_INVALID;
c.axis_id = event->caxis.axis;
c.axis_value = event->caxis.value;
c.id = event->gaxis.which;
c.button = SDL_GAMEPAD_BUTTON_INVALID;
c.axis_id = event->gaxis.axis;
c.axis_value = event->gaxis.value;
c.down = false;
c.up = false;
c.added = false;
@@ -673,22 +633,22 @@ void WindowContext::ProcessEvent(double time_s) {
break;
}
case SDL_CONTROLLERBUTTONDOWN:
case SDL_CONTROLLERBUTTONUP: {
case SDL_EVENT_GAMEPAD_BUTTON_DOWN:
case SDL_EVENT_GAMEPAD_BUTTON_UP: {
EventController c {};
c.id = event->cbutton.which;
c.button = event->cbutton.button;
c.axis_id = SDL_CONTROLLER_AXIS_INVALID;
c.id = event->gbutton.which;
c.button = event->gbutton.button;
c.axis_id = SDL_GAMEPAD_AXIS_INVALID;
c.axis_value = 0;
c.down = (event->cbutton.type == SDL_CONTROLLERBUTTONDOWN);
c.up = (event->cbutton.type == SDL_CONTROLLERBUTTONUP);
c.down = event->gbutton.down;
c.up = !event->gbutton.down;
c.added = false;
c.removed = false;
c.remapped = false;
c.axis = false;
c.pressed = (event->cbutton.state == SDL_PRESSED);
c.released = (event->cbutton.state == SDL_RELEASED);
c.pressed = event->gbutton.down;
c.released = !event->gbutton.down;
c.timestamp_seconds = time_s;
GameEventController(c);
@@ -696,44 +656,44 @@ void WindowContext::ProcessEvent(double time_s) {
break;
}
case SDL_CONTROLLERTOUCHPADDOWN:
case SDL_CONTROLLERTOUCHPADMOTION:
case SDL_CONTROLLERTOUCHPADUP:
if (event->ctouchpad.touchpad == 0) {
Controller::SetTouchPad(event->ctouchpad.which, event->ctouchpad.finger,
event->ctouchpad.type != SDL_CONTROLLERTOUCHPADUP,
event->ctouchpad.x, event->ctouchpad.y);
case SDL_EVENT_GAMEPAD_TOUCHPAD_DOWN:
case SDL_EVENT_GAMEPAD_TOUCHPAD_MOTION:
case SDL_EVENT_GAMEPAD_TOUCHPAD_UP:
if (event->gtouchpad.touchpad == 0) {
Controller::SetTouchPad(event->gtouchpad.which, event->gtouchpad.finger,
event->gtouchpad.type != SDL_EVENT_GAMEPAD_TOUCHPAD_UP,
event->gtouchpad.x, event->gtouchpad.y);
}
break;
case SDL_CONTROLLERSENSORUPDATE: {
const auto& sensor = event->csensor;
case SDL_EVENT_GAMEPAD_SENSOR_UPDATE: {
const auto& sensor = event->gsensor;
if (sensor.sensor == SDL_SENSOR_ACCEL || sensor.sensor == SDL_SENSOR_GYRO) {
Controller::SetSensor(sensor.which,
sensor.sensor == SDL_SENSOR_ACCEL ? Controller::Sensor::Accel
: Controller::Sensor::Gyro,
sensor.data,
sensor.timestamp_us != 0
? sensor.timestamp_us
: static_cast<uint64_t>(sensor.timestamp) * 1000);
sensor.sensor_timestamp != 0
? sensor.sensor_timestamp / 1000
: sensor.timestamp / 1000);
}
break;
}
case SDL_CONTROLLERDEVICEADDED:
case SDL_CONTROLLERDEVICEREMOVED:
case SDL_CONTROLLERDEVICEREMAPPED: {
case SDL_EVENT_GAMEPAD_ADDED:
case SDL_EVENT_GAMEPAD_REMOVED:
case SDL_EVENT_GAMEPAD_REMAPPED: {
EventController c {};
c.id = event->cdevice.which;
c.button = SDL_CONTROLLER_BUTTON_INVALID;
c.axis_id = SDL_CONTROLLER_AXIS_INVALID;
c.id = event->gdevice.which;
c.button = SDL_GAMEPAD_BUTTON_INVALID;
c.axis_id = SDL_GAMEPAD_AXIS_INVALID;
c.axis_value = 0;
c.down = false;
c.up = false;
c.added = (event->cdevice.type == SDL_CONTROLLERDEVICEADDED);
c.removed = (event->cdevice.type == SDL_CONTROLLERDEVICEREMOVED);
c.remapped = (event->cdevice.type == SDL_CONTROLLERDEVICEREMAPPED);
c.added = (event->gdevice.type == SDL_EVENT_GAMEPAD_ADDED);
c.removed = (event->gdevice.type == SDL_EVENT_GAMEPAD_REMOVED);
c.remapped = (event->gdevice.type == SDL_EVENT_GAMEPAD_REMAPPED);
c.axis = false;
c.pressed = false;
c.released = false;
@@ -746,47 +706,6 @@ void WindowContext::ProcessEvent(double time_s) {
}
}
void WindowContext::RunOnMainThread(std::function<void()> task) {
if (Common::Thread::IsMainThread()) {
task();
return;
}
uint64_t ticket = 0;
{
Common::LockGuard lock(main_task_mutex);
main_tasks.push_back(std::move(task));
ticket = ++main_tasks_queued;
}
// Wake the main loop in case it is blocked in SDL_WaitEvent.
SDL_Event event {};
event.type = SDL_USEREVENT;
SDL_PushEvent(&event);
Common::LockGuard lock(main_task_mutex);
while (main_tasks_run < ticket) {
main_task_done.Wait(&main_task_mutex);
}
}
void WindowContext::DrainMainThreadTasks() {
std::vector<std::function<void()>> tasks;
{
Common::LockGuard lock(main_task_mutex);
tasks.swap(main_tasks);
}
if (tasks.empty()) {
return;
}
for (auto& task: tasks) {
task();
}
Common::LockGuard lock(main_task_mutex);
main_tasks_run += tasks.size();
main_task_done.SignalAll();
}
void WindowContext::Run() {
Common::Timer timer;
timer.Start();
@@ -796,7 +715,6 @@ void WindowContext::Run() {
loop.paused.store(false, std::memory_order_release);
while (!loop.need_exit) {
DrainMainThreadTasks();
if (loop.paused.load(std::memory_order_acquire)) {
if (!timer.IsPaused()) {
timer.Pause();
@@ -820,17 +738,15 @@ static void WindowCreate(WindowContext& context) {
int width = static_cast<int>(context.graphic_ctx.screen_width);
int height = static_cast<int>(context.graphic_ctx.screen_height);
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS4_RUMBLE, "1");
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS5_RUMBLE, "1");
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
SDL_SetHint(SDL_HINT_WINDOWS_DPI_SCALING, "0");
#if KYTY_PLATFORM == KYTY_PLATFORM_LINUX
// RenderDoc can hide Wayland Vulkan surface support; prefer X11 for captures.
if (Config::RenderDocEnabled()) {
SDL_SetHint(SDL_HINT_VIDEO_DRIVER, "x11,wayland");
}
#endif
if (SDL_InitSubSystem(SDL_INIT_VIDEO | SDL_INIT_GAMECONTROLLER) < 0) {
if (!SDL_InitSubSystem(SDL_INIT_VIDEO | SDL_INIT_GAMEPAD)) {
EXIT("%s\n", SDL_GetError());
}
HostInputInit();
InitializeSystemOverlayInput();
LOGF("WindowCreate(): width = %d, height = %d\n", width, height);
@@ -840,15 +756,14 @@ static void WindowCreate(WindowContext& context) {
// MoltenVK loader before calling SDL_CreateWindow. Keep an explicit user
// override, and fall back to SDL's normal loader search when no bundle is present.
if (std::getenv("SDL_VULKAN_LIBRARY") == nullptr) {
if (char* base_path = SDL_GetBasePath(); base_path != nullptr) {
if (const char* base_path = SDL_GetBasePath(); base_path != nullptr) {
const std::string base_path_str = base_path;
SDL_free(base_path);
std::string moltenvk_path = base_path_str + "libMoltenVK.dylib";
if (!Common::File::IsFileExisting(moltenvk_path)) {
moltenvk_path = base_path_str + "../Frameworks/libMoltenVK.dylib";
}
if (Common::File::IsFileExisting(moltenvk_path) &&
SDL_setenv("SDL_VULKAN_LIBRARY", moltenvk_path.c_str(), 0) == 0) {
SDL_setenv_unsafe("SDL_VULKAN_LIBRARY", moltenvk_path.c_str(), 0) == 0) {
LOGF("Vulkan loader: %s\n", moltenvk_path.c_str());
}
}
@@ -861,21 +776,23 @@ static void WindowCreate(WindowContext& context) {
window_flags |= static_cast<uint32_t>(SDL_WINDOW_BORDERLESS);
}
#endif
context.window = SDL_CreateWindow(KYTY_SDL_WINDOW_CAPTION, SDL_WINDOWPOS_CENTERED,
SDL_WINDOWPOS_CENTERED, width, height, window_flags);
context.window = SDL_CreateWindow(KYTY_SDL_WINDOW_CAPTION, width, height, window_flags);
if (context.window == nullptr) {
EXIT("%s\n", SDL_GetError());
}
SDL_SetWindowResizable(context.window, SDL_FALSE);
SDL_SetWindowPosition(context.window, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED);
SDL_SetWindowResizable(context.window, false);
HostInputInit(context.window);
InitializeSystemOverlayInput(context.window);
context.UpdateIcon();
}
uint32_t WindowContext::InitialWindowFlags(bool fullscreen) noexcept {
auto flags = static_cast<uint32_t>(SDL_WINDOW_VULKAN);
if (fullscreen) {
flags |= static_cast<uint32_t>(SDL_WINDOW_FULLSCREEN_DESKTOP);
flags |= static_cast<uint32_t>(SDL_WINDOW_FULLSCREEN);
}
return flags;
}
@@ -906,6 +823,7 @@ void WindowRun() {
void WindowShutdown() {
if (g_window != nullptr) {
HostInputShutdown();
Controller::EmergencyShutdown();
g_window.reset();
}
@@ -941,7 +859,7 @@ struct WindowIcon {
void* pixels = nullptr;
~WindowIcon() {
SDL_FreeSurface(surface);
SDL_DestroySurface(surface);
stbi_image_free(pixels);
}
};
@@ -965,8 +883,8 @@ static void WindowLoadPngIcon(const std::filesystem::path& path, WindowIcon* ico
EXIT_IF(icon->pixels == nullptr);
icon->surface = SDL_CreateRGBSurfaceWithFormatFrom(icon->pixels, width, height, 32, width * 4,
SDL_PIXELFORMAT_RGBA32);
icon->surface = SDL_CreateSurfaceFrom(width, height, SDL_PIXELFORMAT_RGBA32, icon->pixels,
width * 4);
EXIT_NOT_IMPLEMENTED(icon->surface == nullptr);
}
@@ -1028,7 +946,16 @@ void WindowContext::UpdateTitle() {
(has_title_id ? ", " : ""), (has_app_ver ? app_ver : ""), (has_app_ver ? " " : ""),
device_name, processor_name, frame_num, current_fps);
RunOnMainThread([this, text = std::move(text)] { SDL_SetWindowTitle(window, text.c_str()); });
struct TitleUpdate {
SDL_Window* window;
std::string* text;
} update {window, &text};
EXIT_IF(!SDL_RunOnMainThread(
[](void* data) {
auto& title = *static_cast<TitleUpdate*>(data);
SDL_SetWindowTitle(title.window, title.text->c_str());
},
&update, true));
}
} // namespace Libs::Graphics
@@ -1,15 +1,14 @@
#ifndef EMULATOR_SRC_GRAPHICS_PRESENTATION_WINDOW_WINDOWINTERNAL_H_
#define EMULATOR_SRC_GRAPHICS_PRESENTATION_WINDOW_WINDOWINTERNAL_H_
#include "SDL_events.h"
#include "SDL_video.h"
#include <SDL3/SDL.h>
#include "common/threads.h"
#include "graphics/host_gpu/graphicContext.h"
#include "graphics/host_gpu/vulkanCommon.h"
#include <atomic>
#include <cstdint>
#include <functional>
#include <memory>
#include <vector>
@@ -48,9 +47,6 @@ struct WindowContext {
void ProcessDisplayEvent(const SDL_DisplayEvent& event);
void ProcessEvent(double time_seconds);
void Run();
// SDL window operations must complete on the main thread.
void RunOnMainThread(std::function<void()> task);
void DrainMainThreadTasks();
GraphicContext graphic_ctx;
SDL_Window* window = nullptr;
@@ -61,12 +57,6 @@ struct WindowContext {
WindowLoopState loop;
Common::Mutex mutex;
Common::Mutex main_task_mutex;
Common::CondVar main_task_done;
std::vector<std::function<void()>> main_tasks; // guarded by main_task_mutex
uint64_t main_tasks_queued = 0; // guarded by main_task_mutex
uint64_t main_tasks_run = 0; // guarded by main_task_mutex
};
} // namespace Libs::Graphics